【问题标题】:TCP Hole Punching (bypassed listening socket)TCP Hole Punching(绕过监听套接字)
【发布时间】:2017-10-16 04:44:51
【问题描述】:

自从我为了获得某种可靠的行为而玩打孔以来已经有几天了,但我现在处于死胡同。

UDP 打孔效果很好只需先向远程发送一个数据包,然后让远程发送一个数据包,因为它将通过源 NAT 登陆。从我的尝试来看,它相当可靠

但现在是 TCP……我不明白。

现在,我可以通过 NAT 建立连接,但只能使用连接套接字

A.connect(B) -> Crash agains't B's NAT, but open a hole in A's NAT.
B.connect(A) -> Get in A's NAT hole, reach A's connecting socket.

但是现在,发送 SYN 数据包进行连接的两个套接字已经连接了。

你会认为我会这样做,通过 2 个 NAT 建立连接,万岁。

但问题是这不是一种正常的行为,给这篇论文:http://www.brynosaurus.com/pub/net/p2pnat/,我应该能够有一个与连接套接字并行的监听套接字。

所以我确实绑定了一个监听套接字,它可以接受入站连接。

但是入站连接总是被连接的套接字捕获,而不是被监听的……

例如:

#!/usr/bin/env python3
from socket import *
from threading import Thread
Socket = socket

# The used endpoints:
LOCAL = '0.0.0.0', 7000
REMOTE = 'remote', 7000

# Create the listening socket, bind it and make it listen:
Listening = Socket(AF_INET, SOCK_STREAM)
Listening.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
Listening.bind(LOCAL)
Listening.listen(5)

# Just start in another thread some kind of debug:
# Print the addr of any connecting client:
def handle():
    while not Listening._closed:
        client, addr = Listening.accept()
        print('ACCEPTED', addr)
Thread(target=handle).start()

# Now creating the connecting socket:
Connecting = Socket(AF_INET, SOCK_STREAM)
Connecting.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
Connecting.bind(LOCAL)

# Now we can attempt a connection:
try:
    Connecting.connect(REMOTE)
    print('CONNECTED', Connecting.getpeername())
except Exception as e:
    print('TRIED', type(e), e)

现在有了这个脚本,只需与朋友或其他什么的端口达成一致,并在一端执行它,Connecting.connect(...) 应该运行一段时间(等待超时,因为 SYN 数据包崩溃到远程 NAT,但是幸好自己的NAT开了个洞),同时在另一端执行脚本,现在Connecting.connect(...)会返回,因为它已经连接了。

最奇怪的部分是:Listening 套接字从未被触发

为什么?如何让监听套接字通过连接套接字捕获入站连接?

注意:关闭连接套接字确实会在网络上发送一些立即关闭漏洞的东西,至少在我的网络上是这样。

第二个注意:我在 windows 上。

编辑:主要问题是在任何情况下,这个脚本输出CONNECTED [...]而不是CLIENT [...],这不应该发生一些讲座。

【问题讨论】:

  • 您是否尝试过在侦听端捕获数据包?您在那里看到来自远程端的 SYN 请求吗?
  • 是的,正如我所说,两边(远程/本地)的两个套接字都建立了连接,但不是在Listening 套接字上...

标签: python sockets tcp udp hole-punching


【解决方案1】:

所以,经过更多的测试和阅读,这就是我得出的结论:

事实上,可以绑定一个监听套接字和一个在同一个地址(ip、端口)上进行出站连接的套接字。

但是套接字的行为在很大程度上取决于系统/TCP 堆栈的实现,正如http://www.brynosaurus.com/pub/net/p2pnat/§4.3中提到的那样:

客户端应用程序在 TCP 打孔期间观察到其套接字发生的情况取决于时间和所涉及的 TCP 实现。假设 AB 的公共端点的第一个出站 SYN 数据包被 NAT B 丢弃,但是 B > 在 A 的 TCP 重新传输其 SYN 之前,> 到 A 的公共端点的第一个后续 SYN 数据包到达 A。根据所涉及的操作系统,可能会发生以下两种情况之一:

  • A 的 TCP 实现注意到传入 SYN 的会话端点与 A 试图启动的出站会话的端点相匹配。因此,A 的 TCP 堆栈将这个新会话与 A 上的本地应用程序用来 connect() 到 B 的套接字相关联公共端点。应用程序的异步 connect() 调用成功,应用程序的侦听套接字没有任何反应。
    由于收到的 SYN 数据包不包含 A 之前的出站 SYN 的 ACK,因此 A 的 TCP 回复 B 的公共端点对于 SYN-ACK 数据包,SYN 部分只是 A 的原始出站 SYN 的重放,使用相同的序列号。一旦 B 的 TCP 收到 A 的 SYN-ACK,它会用自己的 ACK 对 A 的 SYN 和 TCP 会话进行响应两端进入连接状态。

  • 或者,A 的 TCP 实现可能会注意到 A 在该端口上有一个活动的侦听套接字,等待传入的连接尝试。由于 B 的 SYN 看起来像一个传入的连接尝试,A 的 TCP 创建一个新的流套接字来关联新的 TCP 会话,并将这个新的套接字传递给应用程序通过应用程序在其侦听套接字上的下一个 accept() 调用。然后,A 的 TCP 使用上述 SYN-ACK 响应 B,然后 TCP 连接设置将照常进行客户端/服务器式连接。
    由于 A 之前对 B 的出站 connect() 尝试使用了源端点和目标端点的组合,该端点现在正被另一个套接字使用,即刚刚返回到通过 accept() 的应用程序,A 的异步 connect() 尝试必须在某些时候失败,通常会出现“地址正在使用”错误。尽管如此,应用程序仍具有与 B 通信所需的有效对等流套接字,因此它忽略了此故障。

上面的第一个行为在基于 BSD 的操作系统中似乎很常见,而第二个行为在 Linux 和 Windows 下似乎更常见。

所以我实际上是第一种情况。在我的 Windows 10 上。

这意味着为了使TCP打孔的方法可靠,我需要在连接套接字的同时绑定一个侦听套接字,但是我稍后需要检测哪个触发(侦听或连接)并通过它顺着应用程序的流程。

【讨论】:

  • 唯一困扰我的是我只看到了这两种行为中的一种,但我想我会保留这一段并采取相应的行动。
  • 你有代码示例吗?你是怎么解决这个问题的?
  • @Amar 您可以从我在原始问题中编写的代码中获得灵感。我的问题是关于我观察到的行为,根据brynosaurus.com/pub/net/p2pnat,这似乎是正常的。因此,根据您的平台,可能会发生不同的行为。然后由您的应用程序来检测触发了哪些行为。我没有更多的代码要分享。
【解决方案2】:

为什么不触发监听套接字

我认为答案就在这里。 TCP 连接由四个元素的元组定义:

  • 本地地址
  • 本地端口
  • 远程地址
  • 远程端口

当你建立 TCP 连接时,你会创建从这个元组到本地主机上连接套接字的绑定。

当通过 NAT 发送 SYN 时,它会创建绑定: - 本地地址/端口 -> 公共地址/端口

当远程端将其 SYN 发送到公共地址/端口时,此地址将转换为本地地址/端口并传递到本地计算机。在这台机器上,此连接与初始连接无法区分,并且成功建立(使用 SYN/ACK)。

这意味着本地端没有收到 INITIAL SYN。

如何让监听套接字通过连接套接字捕获入站连接?

源 NAT 是不可能的。要接受 NAT 后面的新连接,您需要将一些公共 IP/端口映射到您的私有 IP/端口的目标 NAT

【讨论】:

  • 再次感谢您的回答,在这个问题上不孤单真的很愉快 :) 现在,如果我猜对了,您说我不能接受连接,因为我的 NAT 没有任何重定向?因为这是我试图通过打孔来解决的问题。现在,基于此,我可以使用REMOTE = 'localhost', 7000 运行作为示例给出的脚本,它应该按照预期的方式工作吗?没有更多的 NAT,数据包在本地发送。好吧,它实际上是在做同样的事情:连接套接字捕获连接,而监听套接字则没有。
  • (顺便说一句,我先绑定哪个套接字并不重要,连接的套接字总是优先于侦听/接受的,叹息)
  • (也许我没有完全理解你)
  • 您不能接受客户端,因为 NAT 只映射一个连接 有两种情况:1)您发送与该连接匹配的数据包,并且该数据包在连接内被接受 2)您发送的数据包与该连接不匹配或其状态(序列号空间等),您将获得 RST。
  • 另外,我认为如果您可以从外部打开新会话,那么这将是 NAT 中的一个大安全漏洞。
猜你喜欢
  • 2016-04-06
  • 1970-01-01
  • 2016-02-09
  • 2011-02-15
  • 2011-03-31
  • 2016-11-13
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多