PHP Преобразование IPv6 в двоичное (/ память) представление

Я уже "сделал" это для 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]);

Мне нужно сделать то же самое и для IPv6. Прочитав спецификацию IPv6 (признаюсь, что я не читал ее все), я заметил несколько странностей ("исключений"), таких как набор 0, который можно сжать до двойного двоеточия: " :0000:0000"=>"::" (если я правильно понял). Я также видел, как вы можете иметь строку в стиле IPv4 внутри строки IPv6: 0:0:0:0:0:0:127.0.0.1

Начнем с того, что я понятия не имею, с чего начать.


Благодаря Альваро теперь у меня есть реализация inet_pton на чистом PHP:

/**
 * @copyright   2004-2007 Aidan Lister <[email protected]>, Arpad Ray <[email protected]>
 * @link        http://php.net/inet_pton
 * @author      Arpad Ray <[email protected]>
 */
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;
}

Проблема в том, что я не совсем понимаю, что он делает. Проблема в том, что я не могу просто использовать такую ​​функцию, поскольку (во-первых) я знаю, что она упаковывает IP-адрес в другой формат, чем я делаю (или хочу).


person Christian    schedule 09.08.2010    source источник
comment
Я никогда не работал с IPv6 (я даже не знал, что упакованное представление не уникально), но, вероятно, было бы полезно, если бы вы привели пример несовпадающих выходных данных и/или объяснили, какую часть функции вы не выполняете. Т понять.   -  person Álvaro González    schedule 09.08.2010
comment
У меня есть собственный упаковочный механизм. Обычный алгоритм упаковки сводится к сложению байтов, например, RGB 0,255,255 => 0+255*256+255*256*256 = 16 776 960 = 0xFFFF00 (hex) = 00FFFF (html RGB). Мой собственный - это просто прямое двоичное представление этой строки, которое не заботится о порядке следования байтов (что делает его кросс-платформенным и т.д.).   -  person Christian    schedule 09.08.2010
comment
Проблема в том, что я не понимаю эту функцию и/или что она делает, поэтому я не могу полагаться на ее вывод (я не просто копирую и вставляю код;)).   -  person Christian    schedule 09.08.2010
comment
Я попытался объяснить функцию полностью, но, если серьезно, вам нужны некоторые основы PHP для выполнения такого рода задач. Наслаждаться!   -  person Álvaro González    schedule 09.08.2010
comment
Альваро, я хорошо знаю, что делают эти функции, это второй вопрос, почему, которого я не понимаю. В общем, эта функция не делает то, что мне нужно, и я не могу понять, как она работает в IPv6 (в чем я счел ее полезной). :(   -  person Christian    schedule 09.08.2010


Ответы (2)


См. ip2long() и inet_pton().

Редактировать: в PHP_Compat пакет.

Обновлять

Я прокомментировал функцию для вас. Обратите внимание, что я не имею ни малейшего представления о формате адресов IPv6, поэтому я могу примерно сказать вам, что он делает, но не почему.

<?php

/**
 * @copyright   2004-2007 Aidan Lister <[email protected]>, Arpad Ray <[email protected]>
 * @link        http://php.net/inet_pton
 * @author      Arpad Ray <[email protected]>
 */
function php_compat_inet_pton($address) {
    // Convert to IPv4 (numeric representation)
    $r = ip2long($address);

    // ip2long() will return FALSE if it's an invalid IPv4 address (or -1 if PHP earlier than 5.0.0)
    if ($r !== false && $r != -1)
        // if it didn't, then it *is* a valid IPv4 address
        // We pack the number as unsigned long (always 32 bit, big endian byte order) and we're done
        return pack('N', $r);

    // Count the number of delimiters (:)
    $delim_count = substr_count($address, ':');

    // If none or more than 7, the address is not valid
    if ($delim_count < 1 || $delim_count > 7) return false;

    // Create an array with the delimited substrings
    $r = explode(':', $address);

    // Count the number of items
    $rcount = count($r);

    // If we have empty items, fetch the position of the first one
    if (($doub = array_search('', $r, 1)) !== false) {

        // We fill a $length variable with this rule:
        // - If it's the first or last item ---> 2
        // - Otherwhise                     ---> 1
        $length = (!$doub || $doub == $rcount - 1 ? 2 : 1);

        // Remove a portion of the array and replace it with something else
        array_splice($r,

            // We skip items before the empty one
            $doub,

            // We remove one or two items
            $length,

            // We replace each removed value with zeros
            array_fill(0, 8 + $length - $rcount, 0)

        );
    }

    // We convert each item from hexadecimal to decimal
    $r = array_map('hexdec', $r);

    // We add 'n*' at the beginning of the array (just a trick to use pack on all the items)
    array_unshift($r, 'n*');

    // We pack all the items as unsigned shorts (always 16 bit, big endian byte order)
    $r = call_user_func_array('pack', $r);

    // Return the resulting string
    return $r;
}
person Álvaro González    schedule 09.08.2010
comment
Согласно документам, ip2long работает только для адреса ipv4. inet_pton() работает для IPv6, только если PHP создан для поддержки IPv6. Из-за характера моего приложения я не могу предположить, что PHP поддерживает IPv6. - person Christian; 09.08.2010
comment
Отредактировал мой пост с дополнительной информацией. - person Christian; 09.08.2010

Я буду использовать следующий URL для написания нужной мне функции:

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

Я отредактирую код функции.

Изменить Вот оно: надеюсь, люди сочтут это полезным.

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));
    }
}
person Christian    schedule 09.08.2010
comment
Для примеров адресов ваша функция возвращает те же значения, что и php_compat_inet_pton(), за исключением 220F::127.0.0.1, где она возвращает 144-битную двоичную строку. - person Álvaro González; 10.08.2010