【问题标题】:Powershell; Why does this code always return 'True' even if no IPv6? Is it wrong?电源外壳;为什么即使没有 IPv6,此代码也总是返回“True”?这是错的吗?
【发布时间】:2018-03-10 11:44:29
【问题描述】:

不是 powershell 专家,但在 Windows 10 的一个小项目中检查 IPv6 连接性。这(下面的代码)是我正在使用的,如果可能的话,我想继续使用它,因为它很快并且不涉及某个网站返回公共视图的 IPv6 地址。使用的远程 IPv6 地址 (2001:4860:4860::8888) 是 Google 公共 IPv6 DNS。我从一些代码中得到了这个想法,其中 Google Ipv6 DNS 服务器用于检查 Chrome 中的全局 Ipv6 连接性 > https://codereview.chromium.org/141023002

$Saddrf   = [System.Net.Sockets.AddressFamily]::InterNetworkV6;
$Stypeg   = [System.Net.Sockets.SocketType]::Dgram ;
$Ptypeg   = [System.Net.Sockets.ProtocolType]::UDP ;
$Sockg    = New-Object System.Net.Sockets.Socket($Saddrf, $Stypeg, $Ptypeg);
$Sockg.Connect('2001:4860:4860::8888', 53); $cnx = $Sockg.Connected;
write $cnx

它的问题是,即使在接口适配器上禁用了 IPv6 并禁用了 Teredo,当它不应该连接时,它仍然返回“True”(它已连接)。当接口适配器上没有启用 IPv6 或 IPv6 在防火墙或通过其他方式被阻止时,我希望它返回“False”。我做错了什么?

【问题讨论】:

  • 粗略浏览一下 chromium 代码 sn-p 表明它实际上向 Google 公共 DNS 解析器发送了一个 DNS 数据包,只有在收到响应时才返回 true。上面的代码只创建了一个 udp6 套接字并停在那里。
  • 一开始我也是这么想的,但是从我得到这个想法的地方阅读链接说“(请注意,由于检查,实际上没有发送任何数据包;UDP不需要发送任何要连接的数据包,并且 HostResolverImpl 不会通过套接字显式发送任何数据。)"
  • 来自docsIf you call Connect on a User Datagram Protocol (UDP) socket, the Connected property always returns true; however, this action does not change the inherent connectionless nature of UDP.
  • 啊哈!因此,如果它的 UDP 实际连接与否,它总是会返回 true。好吧,我想这可以回答这个问题。

标签: .net powershell udp


【解决方案1】:

UDP 协议是无连接的。因此,“连接”状态对它没有实际意义,这与 TCP 不同(我怀疑您收到的只是硬编码的存根值)。然后,您可以尝试从一开始就切换到 TCP 版本的 DNS 请求(尽管 RFC 建议仅当 UDP 响应包含“截断”标志时才使用 TCP DNS)。

【讨论】:

【解决方案2】:

再看看chromium code,我们可以看到它在做什么。

TL;DR,它检查机器是否确实分配了有效的非本地、非隧道、ipv6 地址。

关键函数(或方法,它是 C++)是 bool IsGloballyReachable()

它从一些带有函数头的样板开始,一个跟踪函数和一个 UDP 套接字指针的创建

// Attempts to connect a UDP socket to |dest|:53.
bool IsGloballyReachable(const IPAddress& dest,
             const NetLogWithSource& net_log) {
  // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed.
  tracked_objects::ScopedTracker tracking_profile_1(
  FROM_HERE_WITH_EXPLICIT_FUNCTION("455942 IsGloballyReachable")
  );

  std::unique_ptr<DatagramClientSocket> socket(
    ClientSocketFactory::GetDefaultFactory()->CreateDatagramClientSocket(
      DatagramSocket::DEFAULT_BIND, 
      RandIntCallback(), 
      net_log.net_log(),
      net_log.source()
    )
  );

然后是第一个测试:我们可以连接到给定的地址吗?

  int rv = socket->Connect(IPEndPoint(dest, 53));
  if (rv != OK)
      return false;

如果失败,这就是我们返回的内容。

这是 powershell 代码停止的地方

但我们还没有完成......

下一个测试:能不能得到socket的本地地址?

  IPEndPoint endpoint;
  rv = socket->GetLocalAddress(&endpoint);
  if (rv != OK)
      return false;

如果可行,我们开始检查实际地址,

  DCHECK_EQ(ADDRESS_FAMILY_IPV6, endpoint.GetFamily());
  const IPAddress& address = endpoint.address();

我们首先消除link_local地址:

  bool is_link_local =
  (address.bytes()[0] == 0xFE) && ((address.bytes()[1] & 0xC0) == 0x80);
  if (is_link_local)
      return false;

我们消除带有 teredo 前缀的地址

  const uint8_t kTeredoPrefix[] = {0x20, 0x01, 0, 0};
  if (IPAddressStartsWith(address, kTeredoPrefix))
      return false;

假定其他任何东西都是全局地址空间中的有效本地 ipv6 地址:

  return true;
}

这是否真的在测试全球连通性?不。因为您实际上并没有发送任何数据包,所以不会查看是否有任何防火墙可能阻止您的流量。但它会验证至少一个网络接口具有有效的非本地 ipv6 地址。

要复制此测试,您需要检查您通过连接的网络接口实际上是 ipv6 地址。

【讨论】:

  • 啊,我现在明白了。我误以为它实际上是在检查 Ipv6 全局连接,而实际上它实际上是在检查全局 Ipv6 地址是否可用。
  • 很好的总结。我应该将其添加为 tl;dr
猜你喜欢
  • 1970-01-01
  • 2014-08-06
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 2022-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多