WordPress.org

Making WordPress.org

Changeset 5501


Ignore:
Timestamp:
05/19/2017 06:53:29 PM (12 months ago)
Author:
iandunn
Message:

Events: Support IPv6 address geolocation.

See #2823
Props coreymckrill

Location:
sites/trunk/api.wordpress.org/public_html/events/1.0
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/api.wordpress.org/public_html/events/1.0/index.php

    r5499 r5501  
    385385 *       See `rebuild_location_from_geonames()`.
    386386 *
    387  * @todo - Add support for IPv6 addresses. Otherwise, this will quickly lose effectiveness. As of March 2017, IPv6
    388  *         adoption is at 16% globally and rising relatively fast. Some countries are as high as 30%.
    389  *         See https://www.google.com/intl/en/ipv6/statistics.html#tab=ipv6-adoption for current stats.
    390  *
    391  * @todo - Core sends anonymized IPs like `2a03:2880:2110:df07::`, so make sure those work when implementing IPv6
    392  *
    393387 * @param string $dotted_ip
    394388 *
     
    398392    global $wpdb;
    399393
    400     $long_ip = ip2long( $dotted_ip );
    401     if ( $long_ip === false )
     394    $long_ip = false;
     395
     396    if ( filter_var( $dotted_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
     397        $long_ip = ip2long( $dotted_ip );
     398        $from    = 'ip2location';
     399        $where   = 'ip_to >= %d';
     400    } else if ( filter_var( $dotted_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
     401        $long_ip = _ip2long_v6( $dotted_ip );
     402        $from    = 'ipv62location';
     403        $where   = "ip_to >= CAST( %s AS DECIMAL( 39, 0 ) )";
     404    }
     405
     406    if ( false === $long_ip || ! isset( $from, $where ) ) {
    402407        return;
     408    }
    403409
    404410    $row = $wpdb->get_row( $wpdb->prepare( "
    405411        SELECT ip_city, ip_latitude, ip_longitude, country_short
    406         FROM ip2location
    407         WHERE ip_to >= %d
     412        FROM $from
     413        WHERE $where
    408414        ORDER BY ip_to ASC
    409415        LIMIT 1",
     
    417423
    418424    return $row;
     425}
     426
     427/**
     428 * Convert an IPv6 address to an IP number than can be queried in the ip2location database.
     429 *
     430 * PHP doesn't handle integers large enough to accommodate IPv6 numbers (128 bit), so the number needs
     431 * to be cast as a string.
     432 *
     433 * @link https://en.wikipedia.org/wiki/IPv6
     434 * @link http://php.net/manual/en/language.types.integer.php
     435 *
     436 * The code in this function is based on an answer here: http://lite.ip2location.com/faqs
     437 *
     438 * Uses `inet_pton()` which correctly parses truncated IPv6 addresses such as `2a03:2880:2110:df07::`.
     439 * That is important, because Core will send anonymized addresses instead of complete ones.
     440 *
     441 * @access private
     442 *
     443 * @param string $address The IPv6 address to convert.
     444 *
     445 * @return string|bool `false` if invalid address. Otherwise an IP number cast as a string.
     446 */
     447function _ip2long_v6( $address ) {
     448    $int = inet_pton( $address );
     449
     450    if ( false === $int ) {
     451        return false;
     452    }
     453
     454    $bits     = 15;
     455    $ipv6long = 0;
     456
     457    while ( $bits >= 0 ) {
     458        $bin = sprintf( "%08b", ( ord( $int[ $bits ] ) ) );
     459
     460        if ( $ipv6long ) {
     461            $ipv6long = $bin . $ipv6long;
     462        } else {
     463            $ipv6long = $bin;
     464        }
     465
     466        $bits--;
     467    }
     468
     469    $ipv6long = gmp_strval( gmp_init( $ipv6long, 2 ), 10 );
     470
     471    return $ipv6long;
    419472}
    420473
  • sites/trunk/api.wordpress.org/public_html/events/1.0/tests/test-index.php

    r5497 r5501  
    719719
    720720        /*
    721          * Only the IP is given
     721         * Only the IPv4 address is given
     722         *
     723         * See https://awebanalysis.com/en/ipv4-directory/
    722724         */
    723725        'ip-africa' => array(
     
    786788            ),
    787789        ),
     790
     791        /*
     792         * Only an IPv6 address is given
     793         *
     794         * See https://www.google.com/intl/en/ipv6/statistics.html#tab=per-country-ipv6-adoption&tab=per-country-ipv6-adoption
     795         * See https://awebanalysis.com/en/ipv6-directory/
     796         * See https://www.google.com/search?q=australia+site%3Ahttps%3A%2F%2Fawebanalysis.com%2Fen%2Fipv6-directory%2F
     797         */
     798        'ipv6-africa' => array(
     799            'input'    => array( 'ip' => '2c0f:f8f0:ffff:ffff:ffff:ffff:ffff:ffff' ),
     800            'expected' => array(
     801                'description' => 'harare',
     802                'latitude'    => '-17.829',
     803                'longitude'   => '31.054',
     804                'country'     => 'ZW',
     805                'internal'    => true,
     806            ),
     807        ),
     808
     809        'ipv6-asia-anonymized' => array(
     810            'input'    => array( 'ip' => '2405:200:1000::' ),
     811            'expected' => array(
     812                'description' => 'mumbai',
     813                'latitude'    => '19.014',
     814                'longitude'   => '72.848',
     815                'country'     => 'IN',
     816                'internal'    => true,
     817            ),
     818        ),
     819
     820        'ipv6-europe-anonymized' => array(
     821            'input'    => array( 'ip' => '2a02:578:1000::' ),
     822            'expected' => array(
     823                'description' => 'sint-niklaas',
     824                'latitude'    => '51.165',
     825                'longitude'   => '4.144',
     826                'country'     => 'BE',
     827                'internal'    => true,
     828            ),
     829        ),
     830
     831        'ipv6-north-america-anonymized' => array(
     832            'input'    => array( 'ip' => '2605:a600::' ),
     833            'expected' => array(
     834                'description' => 'mountain view',
     835                'latitude'    => '37.386',
     836                'longitude'   => '-122.084',
     837                'country'     => 'US',
     838                'internal'    => true,
     839            ),
     840        ),
     841
     842        'ipv6-oceania-collapsed-prefix' => array(
     843            'input'    => array( 'ip' => '::ffff:0190:c500' ),
     844            'expected' => array(
     845                'description' => 'quedjinup',
     846                'latitude'    => '-33.634',
     847                'longitude'   => '115.084',
     848                'country'     => 'AU',
     849                'internal'    => true,
     850            ),
     851        ),
     852
     853        'ipv6-south-america' => array(
     854            'input'    => array( 'ip' => '2001:1388:6643:2736:10f1:897c:428c:1b3b' ),
     855            'expected' => array(
     856                'description' => 'ayacucho',
     857                'latitude'    => '-13.158',
     858                'longitude'   => '-74.224',
     859                'country'     => 'PE',
     860                'internal'    => true,
     861            ),
     862        ),
    788863    );
    789864
    790      return $cases;
     865    return $cases;
    791866}
    792867
Note: See TracChangeset for help on using the changeset viewer.