9
votes

I've tried to write a basic TCP hole puncher for a firewall in Python 3 using the principles outlined in this article. I'm having trouble getting anything to connect, though. Here is the code:

#!/usr/bin/python3

import sys
import socket
import _thread as thread

def client():
    c = socket.socket()

    c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

    c.bind((socket.gethostbyname(socket.gethostname()), int(sys.argv[3])))
    while(c.connect_ex((sys.argv[1], int(sys.argv[2])))):
        pass
    print("connected!")
    thread.interrupt_main()

def server():
    c = socket.socket()

    c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

    c.bind((socket.gethostbyname(socket.gethostname()), int(sys.argv[3])))
    c.listen(5)
    c.accept()
    print("connected!")
    thread.interrupt_main()

def main():
    thread.start_new_thread(client, ())
    thread.start_new_thread(server, ())

    while True:
        pass

if __name__ == '__main__':
    main()

I decided to try the puncher on my local machine, so that I could capture all the traffic sent by both instances. I first set up a loopback firewall:

iptables -A INPUT -i lo -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i lo -j DROP

Then I launched two copies of the script:

left$ ./tcphole.py localhost 20012 20011

and

right$ ./tcphole.py localhost 20011 20012

I can see according to Wireshark that the SYN packets are being transmitted both ways:

Wireshark capture

But nothing ever prints "connected!" What am I doing wrong?

1
Wait, wouldn't your firewall rules (specifically, rule #2) drop incoming SYN packets on lo? There are no "ESTABLISHED" or "RELATED" connections in your example, right?NPE
Yes, that's exactly the behavior I am trying to work around. (The firewall is the adversary in this case.) SYN packets identified as RELATED should make it back through though.George Hilliard
And thanks for one of the most complete and clearly articulated questions I've seen for the while. (+1)NPE
Haha, thanks! I always try to thoroughly hash things out before asking so I don't look like an idiot. Sometimes I end up looking like an idiot anyway, but you can only do so much.George Hilliard
I could be missing something obvious, but it's not entirely clear to me why you are expecting those SYN packets to be identified as RELATED.NPE

1 Answers

6
votes

The answer turned out to be quite simple: packets aren't considered RELATED if they aren't coming to the same IP address!

Changing the bind lines to

c.bind('', int(sys.argv[3])))

(the '' binds to the loopback address) fixes the problem entirely.