Domanda

Dopo aver cercato un po 'a fondo, ho notato una leggera mancanza di funzioni in PHP per la gestione di IPv6 . Per mia personale soddisfazione ho creato alcune funzioni per aiutare la transizione.

La funzione IPv6ToLong() è una soluzione temporanea a quella proposta qui: Come archiviare un indirizzo compatibile con IPv6 in un database relazionale . Suddividerà l'IP in due numeri interi e li restituirà in un array.

/**
 * 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);
}

Per queste funzioni in genere le implemento chiamando prima questa funzione:

/**
 * 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);
}

Qualcuno per favore dimmi se sto reinventando la ruota qui o ho fatto qualcosa di sbagliato.

Questa implementazione converte IPv4 in IPv6. Qualsiasi indirizzo IPv6 che non tocca.

È stato utile?

Soluzione

Che ne dici di inet_ntop() ? Quindi invece di tagliare le cose in numeri interi, basta usare un varbinary(16) per memorizzarlo.

Altri suggerimenti

La alcune costanti per la corrispondenza degli indirizzi IPv4 e IPv6, che potrebbero essere utili per controllare l'indirizzo. Non ho visto alcuna utilità di conversione però.

Potresti anche memorizzare l'indirizzo in un binario (16) in mysql, quindi dovresti avere un'opzione per emetterlo in binario da IPv6ToLong ().

Questo è davvero qualcosa che deve essere aggiunto in modo nativo in PHP, specialmente quando molti server Web abilitati per IPv6 riportano :: FFFF: 1.2.3.4 come IP client ed è incompatibile con ip2long e romperà molte cose.

Ecco una funzione alternativa che utilizza 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;
}

Tornando indietro, ho scritto due funzioni, dtr_pton e dtr_ntop che funzionano sia con IPv4 che con IPv6. Li convertirà avanti e indietro tra stampabili e binari.

La prima funzione, VARBINARY(39) verificherà se l'argomento fornito è IPv4 valido o IPv6 valido. A seconda del risultato, potrebbe essere generata un'eccezione oppure potrebbe essere restituita la rappresentazione binaria dell'IP. Utilizzando questa funzione, è quindi possibile eseguire AND'ing o OR'ing rispetto al risultato (per subnet / whathaveyou). Suggerirei di memorizzarli nel database come VARCHAR(39) o <=>.

/**
* 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 seconda funzione, <=> convertirà la rappresentazione binaria dell'IP in un indirizzo IP stampabile.

/**
* 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;
}

Inoltre, ecco un modo rapido per espandere gli indirizzi IPv6 disponibili su 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;
}

Inoltre, una buona lettura sull'argomento potrebbe essere trovata sul mio blog all'indirizzo HighOnPHP: 5 suggerimenti per lavorare con IPv6 in PHP . Questo articolo utilizza alcuni dei metodi sopra descritti in una classe orientata agli oggetti che può essere trovata in GitHub: mikemackintosh \ DTR-IP

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top