turnproxy
                                
                                
                                
                                    turnproxy copied to clipboard
                            
                            
                            
                        Added async support, IPv6, and other features
Recently, I've been working on a library for P2P networking and I decided to use your code within my software. You probably saved me hours or weeks trying to understand TURN. It's possibly the worst protocol I've ever encountered and I consider it the definition of over-engineering. But eventually I did seem to get the hang of it (I think so anyway.)
Some changes I made from your original code:
- I wanted to focus more on using TURN for NAT traversal in P2P networking than for proxying. So I chose to use UDP over TCP.
 - I've added support for IPv6.
 - The original code used blocking sockets. I wanted to use async for everything so I changed all the networking I/O to use async functions.
 - I added in a list of public TURN servers. My own is tried first in my software but uses others in random order as fallbacks if that server is down.
 - My UDP implementation uses sequence numbers to provide some kind of delivery mechanism. But currently ordering hasn't been provided. Maybe I'll add that at a later date.
 - Added support for refresh messages so permissions and allocations don't expire.
 
Usage looks like this:
start a python REPR with await support python3 -m asyncio
from p2pd import *
# Network interface details.
i = await Interface().start()
r = await i.route().bind()
# Address of a TURN server.
dest = await Address(
    "p2pd.net",
    3478,
    r
).res()
# Sync message callback -- do something here if you like.
# Can be async too.
msg_cb = lambda msg, client_tup, pipe: print(msg)
# Implement the TURN protocol for UDP send / recv.
client = TURNClient(
    turn_addr=dest,
    turn_user=None,
    turn_pw=None,
    turn_realm=b"p2pd.net",
    route=r,
    msg_cb=msg_cb
)
# Wait for authentication and relay address allocation.
await async_wrap_errors(
    client.start()
)
client.subscribe(SUB_ALL)
# Send a message to ourselves.
await client.send(b"hello, world!", await client.relay_tup_future)
# You should see the message printed.
# Alternatively the pub-sub API is available since we called subscribe.
out = await client.recv()
print(out)
# Cleanup.
That code loads the default interface, resolves the address of my TURN server, starts the TURN client, waits for it to allocate a relay address, and uses it to send a message back to itself.
Note: that recent versions of Coturn seem to have disabled being able to message yourself so such a test is unlikely to work for all servers.
Thanks for your awesome work on TURN. All of my code is public so what's mine is yours. Cheers.