Making WordPress.org


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

Events: Support IPv6 address geolocation.

See #2823
Props coreymckrill

File:
1 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
Note: See TracChangeset for help on using the changeset viewer.