Change IPv4 to IPv6 string

后端 未结 3 1310
被撕碎了的回忆
被撕碎了的回忆 2021-01-12 17:33

Sander Steffann mentioned in a previous question of mine:

Addresses like 0000:0000:0000:0000:0000:0000:192.168.0.1 are written as 0000:0000:0000:00

相关标签:
3条回答
  • 2021-01-12 17:47

    Your best bet is to not do this manually, but instead call inet_pton to get a binary representation, and then convert that to the format you wish to have.

    $foo = inet_pton("::1");
    for ($i = 0 ; $i < 8 ; $i++)
        $arr[$i] = sprintf("%02x%02x", ord($foo[$i * 2]), ord($foo[$i * 2 + 1]));
    $addr = implode(":", $arr);
    
    0 讨论(0)
  • 2021-01-12 17:49

    First of all: why would you care how the address is written? inet_pton() will parse all variations for you and give you a consistent result, which you can then transform into binary, hex, or whatever you want.

    All the code for converting things like ::192.168.0.1 to 0000:0000:0000:0000:0000:0000:c0a8:0001 was actually in my post. That's exactly what my example function does.

    If you feed 0000:0000:0000:0000:0000:0000:192.168.0.1 to inet_pton() and then to inet_ntop() you'll get the canonical IPv6 notation, which is ::192.168.0.1 in this case. If that string begins with :: and the rest contains no : and three dots then you can be pretty sure it's an IPv4 address ;-)

    To combine the answer to your previous question with this question:

    function expand_ip_address($addr_str) {
      /* First convert to binary, which also does syntax checking */
      $addr_bin = @inet_pton($addr_str);
      if ($addr_bin === FALSE) {
        return FALSE;
      }
    
      $addr_hex = bin2hex($addr_bin);
    
      /* See if this is an IPv4-Compatible IPv6 address (deprecated) or an
         IPv4-Mapped IPv6 Address (used when IPv4 connections are mapped to
         an IPv6 sockets and convert it to a normal IPv4 address */
      if (strlen($addr_bin) == 16
      &&  substr($addr_hex, 0, 20) == str_repeat('0', 20)) {
        /* First 80 bits are zero: now see if bits 81-96 are either all 0 or all 1 */
        if (substr($addr_hex, 20, 4) == '0000')
        ||  substr($addr_hex, 20, 4) == 'ffff')) {
          /* Remove leading bits so only the IPv4 bits remain */
          $addr_bin = substr($addr_hex, 12);
        }
      }
    
      /* Then differentiate between IPv4 and IPv6 */
      if (strlen($addr_bin) == 4) {
        /* IPv4: print each byte as 3 digits and add dots between them */
        $ipv4_bytes = str_split($addr_bin);
        $ipv4_ints = array_map('ord', $ipv4_bytes);
        return vsprintf('%03d.%03d.%03d.%03d', $ipv4_ints);
      } else {
        /* IPv6: print as hex and add colons between each group of 4 hex digits */
        return implode(':', str_split($addr_hex, 4));
      }
    }
    
    0 讨论(0)
  • 2021-01-12 18:01

    I can't believe I wrote this all out in one go and it worked the first time.

    $strIP = '0000:0000:0000:0000:0000:0000:192.168.0.1';
    $arrIP = explode(':', $strIP);
    
    if( preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $arrIP[count($arrIP)-1]) ) {
      $ip4parts = explode('.', $arrIP[count($arrIP)-1]);
      $ip6trans = sprintf("%02x%02x:%02x%02x", $ip4parts[0], $ip4parts[1], $ip4parts[2], $ip4parts[3]);
      $arrIP[count($arrIP)-1] = $ip6trans;
    
      $strIP = implode(':', $arrIP);
    }
    echo $strIP; //output: 0000:0000:0000:0000:0000:0000:c0a8:0001
    

    Basically:

    1. Explode the string on :
    2. Check if the last quad is formatted like an IP4 address
    3. Explode the last quad on .
    4. Re-print the IP4 octets into two hex quads
    5. Replace the IP4 quad with the new ones
    6. Implode the array on :.
    0 讨论(0)
提交回复
热议问题