ustulation / p2p

NAT Traversal techniques for p2p communication
BSD 3-Clause "New" or "Revised" License
133 stars 28 forks source link

TCP STUN requests fails with only one STUN server #46

Closed povilasb closed 6 years ago

povilasb commented 6 years ago

cargo run --example tcp_rendezvous_connect -- --disable-igd --traversal-server=86.100.203.141:3000 --relay=86.100.203.141:4000 "I'm 4000" this command runs TCP example which initially does some STUN requests to determine it's public address. Unfortunately it fails with such logs:

TRACE:p2p::rendezvous_addr: got a new server to try: 86.100.203.141:3000
TRACE:p2p::rendezvous_addr: in PublicAddrsFromStun::poll() loop
TRACE:p2p::rendezvous_addr: query returned error: error connecting to echo server: Cannot assign requested address (os error 99)
TRACE:p2p::tcp::stream: got rendezvous address: None

I did some research and it seems like you cannot bind and connect multiple times with the same source/destination address pair: https://idea.popcount.org/2014-04-03-bind-before-connect/

I've also got small python script to reproduce this:

import socket

def reusable_socket(addr):
    sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    sock.bind(addr)
    return sock

def main():
    sock = reusable_socket(('0.0.0.0', 0))
    our_addr = sock.getsockname()
    print('our bind addr:', our_addr)

    sock2 = reusable_socket(our_addr)
    sock2.connect(('86.100.203.141', 3000))
    sock2.send(b'ECHOADDR')
    resp = sock2.recv(4096)
    print('Resp:', resp)

    # this works V
    # sock3 = reusable_socket(('0.0.0.0', 0))
    # this will fail V
    sock3 = reusable_socket(our_addr)
    sock3.connect(('86.100.203.141', 3000))
    sock3.send(b'ECHOADDR')
    resp = sock3.recv(4096)
    print('Resp:', resp)

main()

it fails with such output:

our bind addr: ('0.0.0.0', 45121)
Resp: b'\x13\x00\x00\x00\x00\x00\x00\x0078.60.234.207:45121'
Traceback (most recent call last):
  File "main.py", line 33, in <module>
    main()
  File "main.py", line 27, in main
    sock3.connect(('86.100.203.141', 3000))
OSError: [Errno 99] Cannot assign requested address

So the first connect and request succeeds, but the next one fails and the error message basically says that another socket already has a connection with the same source IP:port to the same destination IP:port .

povilasb commented 6 years ago

As noted by Andrew the problem is that p2p shouldn't even do multiple STUN queries to one server at the same time. And this is actually a bug introduced by https://github.com/ustulation/p2p/commit/044351480723fba4b88805a54f75c22e5239fab7 I'm on it and will fix it soon.

Also, worth noting that if our STUN server won't close the connection after it sends response, p2p won't be able to get the response. The issue lies here: https://github.com/ustulation/p2p/blob/master/src/mc.rs#L224 , because tokio::io::read_to_end() will read until TCP connection is alive.

povilasb commented 6 years ago

Fixed by https://github.com/ustulation/p2p/pull/48