TCP Hole Punching (bypassed listening socket)

Viewed 2600

Its been a few days since I'm playing around Hole Punching in order to have some kind of reliable behaviour, but I'm now at a dead end.

UDP Hole punching works great: simply first send a packet to the remote, and get the remote to send a packet the otherway as it will land through the source NAT. Its rather reliable from what I tried.

But now comes TCP... I don't get it.

Right now, I can establish a connection through NATs but only with connecting sockets:

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.

But now, the two sockets that sended the SYN packets for connection are connected.

You would think that I would have done it, got a connection through 2 NATs, hooray.

But the problem is that this is not a normal behaviour, and given to this paper: http://www.brynosaurus.com/pub/net/p2pnat/, I should be able to have a listening socket in parallel to the connecting socket.

So I did bind a listening socket, which would accept inbound connections.

But inbound connections are always caught by the connecting socket and not the by the listening one...

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)

Now with this script, just agree on a port with a friend or whatever, and execute it on one end's, the Connecting.connect(...) should run for a bit (waiting for timeout, because SYN packet crashed into the distant NAT, but fortunately opened a hole in his own NAT), meanwhile execute the script on the other end, now the Connecting.connect(...) will return because it will have connected.

The weirdest part being: The Listening socket was never triggerred.

Why ? How to get the listening socket to catch inbound connections over the connecting socket ?

Note: Closing the connecting socket does send something on the network which immediately close the hole, at least it does on my network.

2nd-Note: I'm on windows.

Edit: The main problem is that in any circumstance, this script outputs CONNECTED [...] rather than CLIENT [...], which given some lecture should not happen.

2 Answers
Related