【问题标题】:Inet6Address valid for invalid IPv6 AddressInet6Address 对无效的 IPv6 地址有效
【发布时间】:2023-04-08 03:27:01
【问题描述】:

我正在使用java.net.Inet6Address 来验证输入字符串是否为有效的IPv6 地址

这是我的代码 sn-p:

public static boolean isValidIPv6ddress(String address) {
    if (address.isEmpty()) {
        return false;
    }
    try {
        Object res = InetAddress.getByName(address);
        return res instanceof Inet6Address;
    } catch (final UnknownHostException ex) {
        return false;
    }
}

不幸的是,即使对于以下无效的输入,上述方法也会返回true

System.out.println(isValidIPv6ddress("2A00:17C8:50C:0000:0000:0000:0000:00001"));
System.out.println(isValidIPv6ddress("2A00:17C8:50C:0000:0000:00000000000000:0000:00001"));
System.out.println(isValidIPv6ddress("2A00:17C8:50C:00001235:0000:00000000000000:0000:00001"));

API 是否忽略前导零?或者 API 中是否存在 错误

【问题讨论】:

  • 根据哪些具体规则您认为这些无效?
  • @SotiriosDelimanolis,根据 RFC 4291。请参阅下面的答案。

标签: java ipv6


【解决方案1】:

根据有关 IPv6 地址的有效文本表示的最新 RFC,您遇到了一个错误,或者对 IPv6 地址文本表示的错误解释。 IPv6 地址架构的最新 RFC 是 RFC 4291, IP Version 6 Addressing Architecture。该 RFC 有 Section 2.2. Text Representation of Addresses 表示(注意限制是四个十六进制数字):

2.2。地址的文本表示

将 IPv6 地址表示为三种常规形式 文本字符串:

  1. 首选形式是 x:x:x:x:x:x:x:x,其中 'x' 是 一到 地址的八个 16 位片段中的四个十六进制数字。 例子:

    ABCD:EF01:2345:6789:ABCD:EF01:2345:6789

    2001:DB8:0:0:8:800:200C:417A

    请注意,不必将前导零写入 单个字段,但每个字段中必须至少有一个数字 字段(2.中描述的情况除外)。

  2. 由于某些分配某些样式的 IPv6 的方法 地址,通常地址包含长字符串 零位。为了使写入地址包含零 更容易一点,可以使用特殊的语法来压缩零。 “::”的使用表示一组或多组 16 位零。 “::”在一个地址中只能出现一次。 "::" 也可以是 用于压缩地址中的前导零或尾随零。

    例如以下地址

    2001:DB8:0:0:8:800:200C:417A 单播地址 FF01:0:0:0:0:0:0:101 多播地址 0:0:0:0:0:0:0:1 环回地址 0:0:0:0:0:0:0:0 未指定地址

    可以表示为

    2001:DB8::8:800:200C:417A 单播地址 FF01::101 多播地址 ::1 环回地址 :: 未指定的地址

  3. 另一种形式,有时在交易时更方便 IPv4 和 IPv6 节点的混合环境是 x:x:x:x:x:x:d.d.d.d,其中“x”是 地址的六个高位 16 位片段,而 'd' 是 四个低位 8 位片段的十进制值 地址(标准 IPv4 表示)。例子:

    0:0:0:0:0:0:13.1.68.3 0:0:0:0:0:FFFF:129.144.52.38

    或压缩形式:

    ::13.1.68.3 ::FFFF:129.144.52.38

由于 RFC 4291 要求每个 16 位字段不超过四个十六进制字符,因此认为任何在 16 位字段中具有四个以上十六进制字符的 IPv6 地址文本表示是有效的是不正确的。


RFC 4291 已由 RFC 5952, A Recommendation for IPv6 Address Text Representation 更新,这进一步限制了 16 位字段中 4.1. Handling Leading Zeros 部分中的正确表示,以不使用前导零:

4.1。处理 16 位字段中的前导零

必须抑制前导零。例如,2001:0db8::0001 是 不可接受,必须表示为 2001:db8::1。单个 16 位 0000 字段必须表示为 0。

RFC 5952 还需要压缩格式,其中多个连续的 16 位字段必须压缩为 ::

4.2。 "::" 用法

4.2.1。尽可能缩短

必须最大限度地使用符号“::”。 例如,2001:db8:0:0:0:0:2:1 必须缩短为 2001:db8::2:1。 同样,2001:db8::0:1 也是不可接受的,因为符号“::” 可以用来生成更短的表示 2001:db8::1。

4.2.2。处理一个 16 位 0 字段

符号“::”不得仅用于缩短一个 16 位 0 场地。例如,表示 2001:db8:0:1:1:1:1:1 是 正确,但 2001:db8::1:1:1:1:1 不正确。

4.2.3。 “::”的放置选择

当“::”的位置有其他选择时, 最长的连续 16 位 0 字段必须缩短(即, 具有三个连续零字段的序列在 2001 年被缩短: 0:0:1:0:0:0:1)。当连续 16 位 0 字段的长度 相等(即 2001:db8:0:0:1:0:0:1),第一个零序列 位必须缩短。例如 2001:db8::1:0:0:1 是正确的 表示。

基本上,RFC 5952 还要求您接受任何有效的 RFC 4291 格式,但您应该只输出任何 RFC 5952 格式的 IPv6 文本表示:

  1. IPv6 文本表示建议

    关于 IPv6 的规范文本表示格式的建议 地址在本节中介绍。在这个推荐 文档是完全符合 [RFC4291] 的文档,由 多种操作系统,人性化。建议 在本节中,系统在生成 地址表示为文本,但所有实现必须 接受并能够处理任何合法的 [RFC4291] 格式。这是 建议人们在拼写时也遵循这些建议 地址。

【讨论】:

  • 嗯,这是一个详细的答案。干得好。
  • 感谢@ron-maupin 的详细解释。
【解决方案2】:

我检查了类 java.net.Inet6Address 的文档,它说只考虑 rfc2373。这意味着 API 已过时,必须升级以考虑 IETF 对 IPv6 文本表示的最新建议。

【讨论】:

    【解决方案3】:

    查看源代码,它看起来像是将字符串分解为字符,然后对每个字符进行位操作以将位添加到最终结果中,然后左移 4 位并继续直到遇到 : 或结果超过 0xFFFF。 (http://www.docjar.com/html/api/sun/net/util/IPAddressUtil.java.html 第 170 到 180 行)。由于字符全为零,显然它永远不会超过 0xFFFF,因此它永远不会达到该限制。

    我还阅读了 RFC2373 并说前导零是可选的,但它没有明确限制允许的数量。我会说这是一个错误,但(恕我直言)仅适用于迂腐的人。

    【讨论】:

    • 如果前导零是合法的,为什么它是一个错误?
    • RFC 2373 已被 RFC 3513 淘汰,后者已被 RFC 4291 淘汰,后者由 RFC 5259, A Recommendation for IPv6 Address Text Representation 更新。 4.1. Handling Leading Zeros in a 16-Bit Field 部分说,“必须抑制前导零。例如,2001:0db8::0001 是不可接受的,必须表示为 2001:db8::1。必须表示单个 16 位 0000 字段为 0."
    • 显然前导零不再被允许,所以它确实是一个错误。
    • 我检查了 java.net.Inet6Address 类的文档,它说只考虑了 rfc2373 link。这意味着 API 已过时,必须升级以考虑 IETF 对 IPv6 文本表示的最新建议。
    猜你喜欢
    • 2017-03-07
    • 2012-08-28
    • 2014-12-19
    • 1970-01-01
    • 2014-08-03
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 2021-12-13
    相关资源
    最近更新 更多