Pregunta

Después de buscar a fondo, noté una ligera falta de funciones en PHP para manejar IPv6.Para mi satisfacción personal, creé algunas funciones para ayudar en la transición.

El IPv6ToLong() La función es una solución temporal a la que aparece aquí: Cómo almacenar una dirección compatible con IPv6 en una base de datos relacional.Dividirá la IP en dos números enteros y los devolverá en una matriz.

/**
 * Convert an IPv4 address to IPv6
 *
 * @param string IP Address in dot notation (192.168.1.100)
 * @return string IPv6 formatted address or false if invalid input
 */
function IPv4To6($Ip) {
    static $Mask = '::ffff:'; // This tells IPv6 it has an IPv4 address
    $IPv6 = (strpos($Ip, '::') === 0);
    $IPv4 = (strpos($Ip, '.') > 0);

    if (!$IPv4 && !$IPv6) return false;
    if ($IPv6 && $IPv4) $Ip = substr($Ip, strrpos($Ip, ':')+1); // Strip IPv4 Compatibility notation
    elseif (!$IPv4) return $Ip; // Seems to be IPv6 already?
    $Ip = array_pad(explode('.', $Ip), 4, 0);
    if (count($Ip) > 4) return false;
    for ($i = 0; $i < 4; $i++) if ($Ip[$i] > 255) return false;

    $Part7 = base_convert(($Ip[0] * 256) + $Ip[1], 10, 16);
    $Part8 = base_convert(($Ip[2] * 256) + $Ip[3], 10, 16);
    return $Mask.$Part7.':'.$Part8;
}

/**
 * Replace '::' with appropriate number of ':0'
 */
function ExpandIPv6Notation($Ip) {
    if (strpos($Ip, '::') !== false)
        $Ip = str_replace('::', str_repeat(':0', 8 - substr_count($Ip, ':')).':', $Ip);
    if (strpos($Ip, ':') === 0) $Ip = '0'.$Ip;
    return $Ip;
}

/**
 * Convert IPv6 address to an integer
 *
 * Optionally split in to two parts.
 *
 * @see https://stackoverflow.com/questions/420680/
 */
function IPv6ToLong($Ip, $DatabaseParts= 2) {
    $Ip = ExpandIPv6Notation($Ip);
    $Parts = explode(':', $Ip);
    $Ip = array('', '');
    for ($i = 0; $i < 4; $i++) $Ip[0] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
    for ($i = 4; $i < 8; $i++) $Ip[1] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);

    if ($DatabaseParts == 2)
            return array(base_convert($Ip[0], 2, 10), base_convert($Ip[1], 2, 10));
    else    return base_convert($Ip[0], 2, 10) + base_convert($Ip[1], 2, 10);
}

Para estas funciones normalmente las implemento llamando primero a esta función:

/**
 * Attempt to find the client's IP Address
 *
 * @param bool Should the IP be converted using ip2long?
 * @return string|long The IP Address
 */
function GetRealRemoteIp($ForDatabase= false, $DatabaseParts= 2) {
    $Ip = '0.0.0.0';
    if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != '')
        $Ip = $_SERVER['HTTP_CLIENT_IP'];
    elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != '')
        $Ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] != '')
        $Ip = $_SERVER['REMOTE_ADDR'];
    if (($CommaPos = strpos($Ip, ',')) > 0)
        $Ip = substr($Ip, 0, ($CommaPos - 1));

    $Ip = IPv4To6($Ip);
    return ($ForDatabase ? IPv6ToLong($Ip, $DatabaseParts) : $Ip);
}

Alguien por favor dígame si aquí estoy reinventando la rueda o he hecho algo mal.

Esta implementación convierte IPv4 a IPv6.Cualquier dirección IPv6 que no toque.

¿Fue útil?

Solución

¿Qué tal inet_ntop() ? Luego, en lugar de cortar cosas en números enteros, solo usa un varbinary(16) para almacenarlo.

Otros consejos

PHP.net's La extensión del filtro contiene algunas constantes para hacer coincidir las direcciones IPv4 e IPv6, que pueden ser útiles para verificar la dirección. Sin embargo, no he visto ninguna utilidad de conversión.

También podría almacenar la dirección en un binario (16) en mysql, por lo que debería tener una opción para generarla en binario desde IPv6ToLong ().

Esto es realmente algo que debe agregarse de forma nativa en PHP, especialmente cuando muchos servidores web habilitados para IPv6 informan :: FFFF: 1.2.3.4 como la IP del cliente y es incompatible con ip2long, y romperá muchas cosas.

Aquí hay una función alternativa usando filter_var (PHP > = 5.2)

function IPv4To6($ip) {
 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === true) {
  if (strpos($ip, '.') > 0) {
   $ip = substr($ip, strrpos($ip, ':')+1);
  } else { //native ipv6
   return $ip;
  }
 }
 $is_v4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
 if (!$is_v4) { return false; }
 $iparr = array_pad(explode('.', $ip), 4, 0);
    $Part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
    $Part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
    return '::ffff:'.$Part7.':'.$Part8;
}

Volviendo atrás, escribí dos funciones, dtr_pton y dtr_ntop que funcionan tanto con IPv4 como con IPv6.Los convertirá entre imprimibles y binarios.

La primera función, dtr_pton comprobará si el argumento proporcionado es IPv4 válido o IPv6 válido.Dependiendo del resultado, se podría generar una excepción o se podría devolver la representación binaria de la IP.Al usar esta función, puede realizar operaciones AND u OR contra el resultado (para subredes/lo que sea).Le sugeriría que los almacene en su base de datos como un VARBINARY(39) o VARCHAR(39).

/**
* dtr_pton
*
* Converts a printable IP into an unpacked binary string
*
* @author Mike Mackintosh - mike@bakeryphp.com
* @param string $ip
* @return string $bin
*/
function dtr_pton( $ip ){

    if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
        return current( unpack( "A4", inet_pton( $ip ) ) );
    }
    elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
        return current( unpack( "A16", inet_pton( $ip ) ) );
    }

    throw new \Exception("Please supply a valid IPv4 or IPv6 address");

    return false;
}

La segunda función, dtr_ntop convertirá la representación binaria de la IP nuevamente en una dirección IP imprimible.

/**
* dtr_ntop
*
* Converts an unpacked binary string into a printable IP
*
* @author Mike Mackintosh - mike@bakeryphp.com
* @param string $str
* @return string $ip
*/
function dtr_ntop( $str ){
    if( strlen( $str ) == 16 OR strlen( $str ) == 4 ){
        return inet_ntop( pack( "A".strlen( $str ) , $str ) );
    }

    throw new \Exception( "Please provide a 4 or 16 byte string" );

    return false;
}

Además, aquí hay una forma rápida de expandir las direcciones IPv6. encontrado en StackOverflow

function expand($ip){
    $hex = unpack("H*hex", inet_pton($ip));         
    $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1);

    return $ip;
}

Además, puede encontrar una buena lectura sobre el tema en mi blog en Alto en PHP:5 consejos para trabajar con IPv6 en PHP.Este artículo utiliza algunos de los métodos descritos anteriormente en una clase orientada a objetos que se puede encontrar en GitHub:mikemackintosh\dTR-IP

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top