turnproxy icon indicating copy to clipboard operation
turnproxy copied to clipboard

Added async support, IPv6, and other features

Open robertsdotpm opened this issue 2 years ago • 0 comments

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.

robertsdotpm avatar Jan 15 '23 04:01 robertsdotpm