TCP打孔(旁路监听插座)

时间:2017-05-16 22:34:50

标签: python sockets tcp udp hole-punching

为了获得某种可靠的行为,我在围绕打孔的时候已经过了几天,但我现在已经走到了尽头。

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/,我应该能够与连接套接字并行使用侦听套接字。

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

但是入站连接总是被连接套接字捕获而不是通过监听接入...

e.g:

#!/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套接字从未被触发

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

注意:关闭连接插座确实会在网络上发送一些立即关闭该孔的内容,至少在我的网络上是这样。

第二 - 注意:我在窗户上。

编辑:主要问题是,在任何情况下,此脚本都会输出CONNECTED [...]而不是CLIENT [...],因为某些讲座不应该发生。

2 个答案:

答案 0 :(得分:1)

为什么不会触发侦听套接字

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

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

建立TCP连接时,您可以从此元组创建绑定到本地主机上的连接套接字。

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

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

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

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

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

答案 1 :(得分:1)

所以,经过更多的测试和阅读,这就是我的意思:

实际上,可以将侦听套接字和套接字绑定在同一地址(ip,port)上进行出站连接。

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

  

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

     
      
  • 的TCP实施会注意到,传入SYN的会话端点与出站会话 A 尝试启动的会话端点匹配。 的TCP堆栈因此将此新会话与 A 上的本地应用程序用于连接()到 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()调用其侦听套接字。 的TCP然后使用上述SYN-ACK响应 B ,并且TCP连接设置照常进行客户端/服务器式连接。
      由于 A 的先前出站连接()尝试 B 使用了另一个套接字正在使用的源端点和目标端点的组合,即刚刚返回到另一个套接字的端点应用程序通过accept(), A 的异步connect()尝试必须在某些时候失败,通常是“正在使用的地址”错误。然而,应用程序具有与 B 进行通信所需的工作对等流套接字,因此它忽略了此故障。

  •   
     

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

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

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