Search code examples
phpipv6ipv4

PHP Convert IPv6 to binary(/memory) representation


I already "did" this for IPv4;

$ip = '127.0.0.1'; // example
$ip = explode('.',$ip);
if( count($ip) != 4 ) $ip = array(0,0,0,0); // wrong ip format, default to 0.0.0.0
return chr($ip[0]) . chr($ip[1]) . chr($ip[2]) . chr($ip[3]);

I need to do the above for IPv6 as well. Reading through the IPv6 spec, (I admit I didn't read all of it), I saw several oddities ("exceptions") such as a set of 0 could be compressed to a double colon: ":0000:0000"=>"::" (if my understanding was correct). I also saw how you can have an IPv4-style string inside an IPv6 string: 0:0:0:0:0:0:127.0.0.1

Let's start by saying I've no freakin idea where to start.


Thanks to Alvaro, now I've got a pure-PHP implementation of inet_pton:

/**
 * @copyright   2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
 * @link        http://php.net/inet_pton
 * @author      Arpad Ray <arpad@php.net>
 */
function php_compat_inet_pton($address) {
    $r = ip2long($address);
    if ($r !== false && $r != -1) return pack('N', $r);
    $delim_count = substr_count($address, ':');
    if ($delim_count < 1 || $delim_count > 7) return false;
    $r = explode(':', $address);
    $rcount = count($r);
    if (($doub = array_search('', $r, 1)) !== false) {
        $length = (!$doub || $doub == $rcount - 1 ? 2 : 1);
        array_splice($r, $doub, $length, array_fill(0, 8 + $length - $rcount, 0));
    }
    $r = array_map('hexdec', $r);
    array_unshift($r, 'n*');
    $r = call_user_func_array('pack', $r);
    return $r;
}

Problem is, I can't quite understand what it's doing. The issue is, I can't just use such a function since (for one thing) I know it's packing the IP in a differnt format than I'm doing (or want to).


Solution

  • I will use the following URL to write the function I need:

    http://www.zytrax.com/tech/protocols/ipv6.html

    I will edit back with the function code.

    Edit Here it goes: hope people find this useful.

    class Connect {
        /**
         * Returns the IP in it's fullest format.
         * @example
         *          ::1              => 0000:0000:0000:0000:0000:0000:0000:0001
         *          220F::127.0.0.1  => 220F:0000:0000:0000:0000:0000:7F00:0001
         *          2F:A1::1         => 002F:00A1:0000:0000:0000:0000:0000:0001
         * @param string $ip Original/compressed/packed IPv6.
         * @return string Full IP.
         */
        protected static function fixIpv6($ip){
            // fix double colon
            if(strpos($ip,'::')!==false)$ip=str_replace('::',str_repeat(':',9-substr_count($ip,':')),$ip);
            // fix each slot
            $ip=explode(':',$ip);
            foreach($ip as $k=>$v){
                // fix empty/compressed slots
                $ip[$k]=$v=str_pad($v,4,'0',STR_PAD_LEFT);
                // fix ipv4-style slot
                if(strpos($v,'.')!==false){
                    // initially empty buffer
                    $ip[$k]='';
                    // replace each number(byte) with a two-digit hex representation
                    foreach(explode('.',$v) as $v2){
                        $v=dechex(min((int)$v2,255));
                        if(strlen($v)==1)$v='0'.$v;
                        $ip[$k].=$v;
                    }
                    // add colon in between two pairs(bytes) (FFFFFFFF=>FFFF:FFFF)
                    $ip[$k]=implode(':',str_split($ip[$k],4));
                }
            }
            return strtoupper(implode(':',$ip));
        }
        /**
         * Compresses an IP to it's binary representation.
         * @param string $ip A well-formatted full IPv4 or IPv6 address.
         * @return string Binary representation of address.
         */
        public static function compressIp($ip){
            if(strpos($ip,':')!==false){ // ipv6
            $ip=str_split(str_replace(':','',self::fixIpv6($ip)),2);
            foreach($ip as $k=>$v)$ip[$k]=chr(hexdec($v));
            return implode('',$ip);
            }elseif(strpos($ip,'.')!==false){ // ipv4
                $ip=explode('.',$ip);
                if(count($ip)!=4)$ip=array(0,0,0,0);
                return chr($ip[0]).chr($ip[1]).chr($ip[2]).chr($ip[3]);
            }else throw new Exception('Unrecognized IP format: '.MB_SECURITY::snohtml($ip));
        }
    }