Question

Après une recherche approfondie, j'ai constaté un léger manque de fonctions dans PHP pour la gestion de IPv6 . . Pour ma propre satisfaction, j'ai créé quelques fonctions pour faciliter la transition.

La fonction IPv6ToLong() est une solution temporaire à celle évoquée ici: Comment stocker une adresse compatible IPv6 dans une base de données relationnelle . Il divisera l’IP en deux entiers et les retournera dans un tableau.

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

Pour ces fonctions, je les implémente généralement en appelant d'abord cette fonction:

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

S'il vous plaît, dites-moi si je réinvente la roue ici ou si j'ai fait quelque chose de mal.

Cette implémentation convertit IPv4 en IPv6. Toute adresse IPv6 qu’elle ne touche pas.

Était-ce utile?

La solution

Qu'en est-il de inet_ntop() ? Ensuite, au lieu de couper les éléments en entiers, vous utilisez simplement un varbinary(16) pour le stocker.

Autres conseils

L'extension de filtre de PHP.net contient certaines constantes permettant de faire correspondre les adresses IPv4 et IPv6, ce qui peut s'avérer utile pour vérifier l'adresse. Je n'ai toutefois pas vu d'utilitaire de conversion.

Vous pouvez également stocker l'adresse dans un fichier binaire (16) dans mysql. Vous devriez donc avoir la possibilité de la générer en binaire à partir de IPv6ToLong ().

C’est vraiment quelque chose qui doit être ajouté de manière native en PHP, en particulier lorsque de nombreux serveurs Web activés pour IPv6 signalent: FFFF: 1.2.3.4 l’adresse IP du client, incompatible avec ip2long et qui va casser beaucoup de choses.

Voici une fonction alternative utilisant 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;
}

Pour revenir en arrière, j’ai écrit deux fonctions, dtr_pton et dtr_ntop qui fonctionnent à la fois avec IPv4 et IPv6. Il les convertira entre imprimable et binaire.

La première fonction, VARBINARY(39) vérifiera si l'argument fourni est un IPv4 valide ou un IPv6 valide. En fonction du résultat, une exception peut être levée ou la représentation binaire de l'adresse IP peut être renvoyée. En utilisant cette fonction, vous pouvez alors exécuter AND ou OR contre le résultat (pour le sous-réseau / whathaveyou). Je vous suggère de les stocker dans votre base de données sous la forme VARCHAR(39) ou <=>.

/**
* 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 deuxième fonction, <=> convertira la représentation binaire de l’IP en une adresse IP imprimable.

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

En outre, voici un moyen rapide de développer les adresses IPv6 trouvées sur 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;
}

En outre, vous trouverez une bonne lecture sur le sujet sur mon blog à l'adresse HighOnPHP: 5 conseils pour utiliser IPv6 en PHP . Cet article utilise certaines des méthodes décrites ci-dessus dans une classe orientée objet, disponible à l'adresse GitHub: mikemackintosh \ dTR-IP

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top