【问题标题】:IPv6 address validation and canonicalization [closed]IPv6地址验证和规范化[关闭]
【发布时间】:2010-11-26 05:18:46
【问题描述】:

您为此使用了哪些库?它们之间的兼容性如何?还是您编写了自己的解析例程?

我对 Java、C++、Python 和 JavaScript 的相互兼容实现特别感兴趣,它们支持:

  • 零压缩(“::”)
  • IPv4 映射地址(“::ffff:123.45.67.89”)
  • 规范化(包括缩写形式,以供人类阅读)
  • CIDR 样式的网络掩码(如末尾的“/64”)

【问题讨论】:

标签: java javascript c++ python ipv6


【解决方案1】:

在 POSIX 系统上,您可以结合使用 inet_ptoninet_ntop 来进行规范化。您仍然需要进行自己的 CIDR 解析。幸运的是,我认为 IPv6 唯一有效的 CIDR 语法是 /number_of_bits 表示法,所以这相当容易。

您将遇到的另一个问题是缺乏对接口规范的支持。对于链接本地地址,您会在末尾看到 %eth0 之类的内容,以指定它们也是本地链接。 getaddrinfo 会解析,但 inet_pton 不会。

您可以采用的一种策略是使用getaddrinfo 进行解析并使用inet_ntop 进行规范化。

getaddrinfo 适用于 Windows。 inet_ptoninet_ntop 不是。幸运的是,编写代码来生成规范形式的 IPv6 地址并不难。它将需要两次通过,因为 0 压缩的规则是首先出现的最大的 0 字符串。 IPv4 形式(即::127.0.0.1)也仅用于::IPv4::ffff:IPv4

我没有要测试的 Windows 机器,但从文档看来,Windows 上的 Python 在其套接字模块中支持 inet_ptoninet_ntop

编写自己的例程来生成规范形式可能不是一个坏主意,因为即使您的规范形式与其他人的不同,只要它有效,其他人就可以解析它。但在任何情况下,我都不会编写您自己的例程来解析 IPv6 地址。

我的上述建议适用于 Python、C 和 C++。我对如何用 Java 或 Javascript 解决这个问题知之甚少。

编辑:我一直在研究 getaddrinfo 及其对应的 getnameinfo。这些几乎在所有方面都比inet_ptoninet_ntop 好。它们是线程安全的,您可以向它们传递选项(getaddrinfo 的情况下为AI_NUMERICHOSTgetnameinfo 的情况下为NI_NUMERCHOST)以防止它们执行任何类型的 DNS 查询。他们的界面有点复杂,在某些方面让我想起了丑陋的 Windows 界面,但很容易弄清楚要传递哪些选项来获得你想要的东西。我衷心推荐他们两个。

【讨论】:

  • 我想评论一下,在我自己玩过 getaddrinfo 之后,OSX 10.4 和 glibc-2.10.1 版本都有有趣的错误。 OSX 版本有更严重的错误。
  • Windows 确实支持 Vista 及更高版本的 inet_pton 和 inet_ntop(请参阅 msdn.microsoft.com/en-us/library/cc805843%28VS.85%29.aspx)。但是,Python 2.6 和 3.0 没有(文档指出可用性 Unix)。
  • 注意 Windows 中的速度。由于某种原因,Windows inet_pton 和 inet_ntop 非常慢。或者他们是我在 Vista 和 Server 2008 上的经验。
  • @Omnifarious,您写道,“但我在任何情况下都不会编写您自己的例程来解析 IPv6 地址。”我使用 RegEx 相当轻松地做到了这一点,它接受所有合法形式的 IPv6 寻址(压缩的、未压缩的、混合压缩的、混合未压缩的,以及每个版本的大写或小写版本,带或不带 /<mask length>)。反向例程将返回 IPv6 地址的 RFC 5259“官方”规范表示,包括 /96 地址的混合。
【解决方案2】:

在 Java 中,您可以使用

InetAddress.getByName(IP)

然后检查由此引发的异常以验证 IPv6 地址

如果您觉得可以,您也可以使用 Sun Propreitary API。这不会执行 DNS 查找。 (他们可能会在不通知的情况下更改/删除它,因为它是他们的专有 API。这是在使用它编译代码时会出现的警告)

boolean sun.net.util.IPAddressUtil.isIPv6LiteralAddress(String IP)

【讨论】:

  • 感谢您的想法。不幸的是,该方法在抛出异常之前会进行 DNS 查找。
  • Okie.. 我查看了 InetAddress 的源代码。它使用 sun.net.util.IPAddressUtil 类。它是一个 sun 专有的 API。但如果这对你来说没问题,你可以使用它。使用静态方法 isIPv6LiteralAddress(String IP)。它返回真或假
  • IPAddressUtil 效果很好。我想我也可以通过 textToNumericFormatV*(String) 和一些自定义格式返回文本来应对规范化。 +1,我希望我能接受多个答案。
  • 哇。很高兴我能帮助你! :) 一点问题都没有!
  • 你可以使用 Guava 的 InetAddresses 来避免查找
【解决方案3】:

在 Java 中,Guava 库在 com.google.common.net.InetAddresses 类中具有用于验证 IPv6(和 IPv4)的实用函数。 http://goo.gl/RucRU

【讨论】:

    【解决方案4】:

    我为此写了javascript-ipv6。它目前为v6decode.com 供电。

    这是 API 的一个简短示例:

    var address = new v6.Address("::ffff:7b2d:4359/64");
    
    if (address.isValid()) {
       // Do something if the address is valid
    }
    
    console.log(address.correctForm());         // "::ffff:7b2d:4359"
    console.log(address.canonicalForm());       // "0000:0000:0000:0000:0000:ffff:7b2d:4359"
    console.log(address.v4Form());              // "::ffff:123.45.67.89"
    console.log(address.subnetMask);            // "64"
    console.log(address.possibleAddresses(96)); // "4,294,967,296"
    

    【讨论】:

      【解决方案5】:

      当操作系统支持可能不可用时,我使用正则表达式 - RE 在大多数语言中都可用,包括 C/C++/Java/Python/Perl/bash/.... 以下 python 代码在启动时构建 RE,生成的 RE 源代码是一个 humdinger - 但一旦由 re 引擎编译,它的速度与本机代码一样快。

      PAT_IP4 = r'\.'.join([r'(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])']*4)
      RE_IP4 = re.compile(PAT_IP4+'$')
      RE_IP6 = re.compile(                 '(?:%(hex4)s:){6}%(ls32)s$'
                     '|::(?:%(hex4)s:){5}%(ls32)s$'
                    '|(?:%(hex4)s)?::(?:%(hex4)s:){4}%(ls32)s$'
      '|(?:(?:%(hex4)s:){0,1}%(hex4)s)?::(?:%(hex4)s:){3}%(ls32)s$'
      '|(?:(?:%(hex4)s:){0,2}%(hex4)s)?::(?:%(hex4)s:){2}%(ls32)s$'
      '|(?:(?:%(hex4)s:){0,3}%(hex4)s)?::%(hex4)s:%(ls32)s$'
      '|(?:(?:%(hex4)s:){0,4}%(hex4)s)?::%(ls32)s$'
      '|(?:(?:%(hex4)s:){0,5}%(hex4)s)?::%(hex4)s$'
      '|(?:(?:%(hex4)s:){0,6}%(hex4)s)?::$'
        % {
      'ls32': r'(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|%s)'%PAT_IP4,
      'hex4': r'[0-9a-f]{1,4}'
      }, re.IGNORECASE)
      

      【讨论】:

        【解决方案6】:

        据我所知,您应该在拥有它的系统上使用@987654321@,即 Linux 和 POSIX 位置。应该不需要编写自己的低级解析器。

        Windows also provides getaddrinfo(),在 XP 及更高版本上。

        【讨论】:

        • 我不认为 getaddrinfo 支持解析子网位切片表示法。它也不支持规范化。
        【解决方案7】:

        对于 Python,最好的解决方案可能是 IPy (http://pypi.python.org/pypi/IPy/0.51)

        【讨论】:

          【解决方案8】:

          getaddrinfo 已经返回最短的文本形式,所以如果你有更长的形式,你可以通过再次运行 getaddrinfo 来规范化。示例 Python,

          import sys, socket;
          result = socket.getaddrinfo('0:0::0:1', None);
          print "family:%i socktype:%i proto:%i canonname:%s sockaddr:%s"%result[0];
          

          输出以下内容,

          family:10 socktype:1 proto:6 canonname: sockaddr:('::1', 0, 0, 0)
          

          IPv6 CIDR 块似乎没有很好地记录或定义,因此我编写了自己的 inet6_network implementation 来处理它 (C99)。

          【讨论】:

            【解决方案9】:

            我只是通过临时方法来检查 IPV4 和 IPV6 两种寻址方案的有效性:这是我的代码......我的代码总是欢迎更正。如果我的解决方案有误,请原谅我。

            #include <cstdio>
            #include <iostream>
            #include <vector>
            #include <stack>
            #include <algorithm>
            #include <sstream>
            #include <utility>
            #include <cmath>
            #include <cstring>
            #include <map>
            #include <queue>
            #include <limits.h>
            using namespace std;
            bool check6(string s)
            {
                for(int i=0;i<s.length();i++)
                {
                   if((s[i] < '0' || s[i] > '9') && (s[i] < 'a' || s[i]> 'f') && 
                       (s[i] < 'A' || s[i] > 'F'))                       
                      return false;
                }
                return true;
            }
               bool check4(string s)
               {
                  for(int i=0; i< s.length(); i++)
                  {
                   if(!(s[i]>= '0' && s[i] <= '9'))
                      return false;
                  }
                 stringstream ss(s);
                  int e;
                  ss >> e;
                 if( e < 0 || e > 255 )
                  return false;
                 else
                  return true;
               }
            
            int main()
            {
              string s;
              cin>>s;
              vector<string> v;
              int i=0;
              int d=0;
              if((s.find(":")!=std::string::npos || 
                     s.find("::")!=std::string::npos)&&  
                 s.find(".")==std::string::npos)     
               {
                  int x=0;
                  while(i< s.length())
                  {
                      string s1 ="";
                      while(i< s.length() && s[i]!= ':')
                      {
                         s1+= s[i];
                         i++;
                      }
                     if(s1!="")
                     v.push_back(s1);
                     if((i+1)< s.length() && s[i]==':' && s[i+1]==':')
                        x++;
                     i++;
                     if(i< s.length())
                     d++;
                 }
                  if(x > 1 || d > 7 || v.size() > 8 || (x==1 && d >6))
                  {
                     cout<<"Not Valid"<<endl;
                     return 0;
                  }
                  else if(d > 2 && v.size() ==0)
                  {
            
                     cout<<"Not Valid"<<endl;
                     return 0;
                  }
                  else
                  {
                     for(int i=0;i< v.size();i++)
                     {
                         if((v[i]).length() > 4)
                         {
                                cout<<"Not Valid"<<endl;
                             return 0;
                          }
                         else
                         {
                            if(!check6(v[i]))
                            {
            
                                cout<<"Not Valid"<<endl;
                                return 0;
                            }
                        }
                     }
                     cout<<"Valid Ipv6"<<endl;
                      return 0;
                  }
               }
            else if(s.find(":")==std::string::npos && s.find(".")!=std::string::npos)
            {
                while(i< s.length())
                {
                    string s1="";
                    while( i< s.length() && s[i]!='.')
                    {
                        s1+=s[i];
                        i++;
                    }
                    i++;
                    if(i< s.length())
                    d++;
                    v.push_back(s1);
                }
                if(d > 4 || v.size()> 4)
                {
                    cout<<"Not Valid"<<endl;
                    return 0;
                }
                else
                {
                    for(int i=0;i<v.size();i++)
                    {
                        if((v[i]).length() > 3)
                        {
                            cout<<"Not Valid"<<endl;
                            return 0;
                        }
                        if(!check4(v[i]))
                        {
                            cout<<"Not Valid"<<endl;
                            return 0;
                        }
                    }   
                }
                cout<<"Valid Ipv4"<<endl;
                return 0;
            }
            
                    cout<<"Not Valid"<<endl;
            
            return 0;
            

            }

            【讨论】:

              【解决方案10】:

              太糟糕了 Python 3.1 丢失了 ipaddr 库。

              它仍可作为第三方库使用:py-ipaddr 可在 PyPI 上使用。

              压缩

              >>> ipaddr.IPv6Address('0:0::0:1').compressed
              '::1'
              

              IPv4 映射

              >>> ipaddr.IPv6Address('::ffff:123.45.67.89').ipv4_mapped
              IPv4Address('123.45.67.89')
              

              CIDR

              >>> ipaddr.IPv6Network('::ffff:123.45.67.89/128')
              IPv6Network('::ffff:7b2d:4359/128')
              

              【讨论】:

                【解决方案11】:

                对于 C#,我建议使用 IPNetwork Library https://github.com/lduchosal/ipnetwork。 从版本 2 开始,它也支持 IPv4 和 IPv6。

                IPv6

                  IPNetwork ipnetwork = IPNetwork.Parse("2001:0db8::/64");
                
                  Console.WriteLine("Network : {0}", ipnetwork.Network);
                  Console.WriteLine("Netmask : {0}", ipnetwork.Netmask);
                  Console.WriteLine("Broadcast : {0}", ipnetwork.Broadcast);
                  Console.WriteLine("FirstUsable : {0}", ipnetwork.FirstUsable);
                  Console.WriteLine("LastUsable : {0}", ipnetwork.LastUsable);
                  Console.WriteLine("Usable : {0}", ipnetwork.Usable);
                  Console.WriteLine("Cidr : {0}", ipnetwork.Cidr);
                

                输出

                Network : 2001:db8::
                Netmask : ffff:ffff:ffff:ffff::
                Broadcast : 
                FirstUsable : 2001:db8::
                LastUsable : 2001:db8::ffff:ffff:ffff:ffff
                Usable : 18446744073709551616
                Cidr : 64
                

                玩得开心!

                【讨论】:

                • 第一个和最后一个可用地址的输出不正确。 IPv6 与 IPv4 不同,它可以使用子网中的所有地址。标准 IPv6 子网是 /64(少数特殊情况使用其他掩码长度),可用地址从 &lt;subnet&gt;::&lt;subnet&gt;:ffff:ffff:ffff:ffff。子网(&lt;subnet&gt;::)没有预留,IPv6没有广播的概念,所以&lt;subnet&gt;:ffff:ffff:ffff:ffff不存在广播地址。
                • 感谢 Ron 指出这一点。
                猜你喜欢
                • 1970-01-01
                • 2010-10-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-02-15
                • 1970-01-01
                • 1970-01-01
                • 2023-04-01
                相关资源
                最近更新 更多