Bug 857668: Teach test TURN server to open additional ports, on which it will respond with 300. r=mjf,ahal

Differential Revision: https://phabricator.services.mozilla.com/D115276
This commit is contained in:
Byron Campen [:bwc] 2021-05-27 21:08:37 +00:00
Родитель fd55c6b4ca
Коммит d1fc4209b2
1 изменённых файлов: 116 добавлений и 7 удалений

Просмотреть файл

@ -61,6 +61,12 @@ SOFTWARE = 0x8022
ALTERNATE_SERVER = 0x8023
FINGERPRINT = 0x8028
STUN_PORT = 3478
STUNS_PORT = 5349
TURN_REDIRECT_PORT = 3479
TURNS_REDIRECT_PORT = 5350
def unpack_uint(bytes_buf):
result = 0
@ -368,6 +374,11 @@ class StunMessage(object):
digest = self.calculate_message_digest(username, realm, password)
self.find(MESSAGE_INTEGRITY).data = digest
def add_alternate_server(self, host, port):
address = ipaddr.IPAddress(host)
version = STUN_IPV6 if address.version == 6 else STUN_IPV4
self.add_address(int(address), version, port, ALTERNATE_SERVER)
class Allocation(protocol.DatagramProtocol):
"""
@ -682,6 +693,40 @@ class StunHandler(object):
return self.make_success_response(request)
class StunRedirectHandler(StunHandler):
"""
Frames and handles STUN messages by redirecting to the "real" server port.
Performs the redirect with auth, so does a 401 to unauthed requests.
Can be used to test port-based redirect handling.
"""
def __init__(self, transport_handler):
super(StunRedirectHandler, self).__init__(transport_handler)
def handle_stun(self, stun_message, address):
self.client_address = address
if stun_message.msg_class == REQUEST:
challenge_response = self.check_long_term_auth(stun_message)
if challenge_response.msg_class == SUCCESS_RESPONSE:
return self.make_redirect_response(stun_message).build()
return challenge_response.build()
def make_redirect_response(self, request):
response = self.make_error_response(request, 300, "Try alternate")
port = STUN_PORT
if self.transport_handler.transport.getHost().port == TURNS_REDIRECT_PORT:
port = STUNS_PORT
response.add_alternate_server(
self.transport_handler.transport.getHost().host, port
)
response.add_message_integrity(turn_user, turn_realm, turn_pass)
return response
class UdpStunHandler(protocol.DatagramProtocol):
"""
Represents a UDP listen port for TURN.
@ -695,6 +740,19 @@ class UdpStunHandler(protocol.DatagramProtocol):
self.transport.write(bytes(data), (address.host, address.port))
class UdpStunRedirectHandler(protocol.DatagramProtocol):
"""
Represents a UDP listen port for TURN that will redirect.
"""
def datagramReceived(self, data, address):
stun_handler = StunRedirectHandler(self)
stun_handler.data_received(data, to_ipaddress("UDP", address[0], address[1]))
def write(self, data, address):
self.transport.write(bytes(data), (address.host, address.port))
class TcpStunHandlerFactory(protocol.Factory):
"""
Represents a TCP listen port for TURN.
@ -736,6 +794,31 @@ class TcpStunHandler(protocol.Protocol):
self.transport.write(bytes(data))
class TcpStunRedirectHandlerFactory(protocol.Factory):
"""
Represents a TCP listen port for TURN that will redirect.
"""
def buildProtocol(self, addr):
return TcpStunRedirectHandler(addr)
class TcpStunRedirectHandler(protocol.DatagramProtocol):
def __init__(self, addr):
self.address = addr
self.stun_handler = None
def dataReceived(self, data):
# This needs to persist, since it handles framing. Framing matters here
# because we do a round of auth before redirecting.
if not self.stun_handler:
self.stun_handler = StunRedirectHandler(self)
self.stun_handler.data_received(data, self.address)
def write(self, data, address):
self.transport.write(bytes(data))
def get_default_route(family):
dummy_socket = socket.socket(family, socket.SOCK_DGRAM)
if family is socket.AF_INET:
@ -820,12 +903,26 @@ if __name__ == "__main__":
interface_6 = "::1"
hostname = "localhost"
reactor.listenUDP(3478, UdpStunHandler(), interface=interface_4)
reactor.listenTCP(3478, TcpStunHandlerFactory(), interface=interface_4)
reactor.listenUDP(STUN_PORT, UdpStunHandler(), interface=interface_4)
reactor.listenTCP(STUN_PORT, TcpStunHandlerFactory(), interface=interface_4)
reactor.listenUDP(
TURN_REDIRECT_PORT, UdpStunRedirectHandler(), interface=interface_4
)
reactor.listenTCP(
TURN_REDIRECT_PORT, TcpStunRedirectHandlerFactory(), interface=interface_4
)
try:
reactor.listenUDP(3478, UdpStunHandler(), interface=interface_6)
reactor.listenTCP(3478, TcpStunHandlerFactory(), interface=interface_6)
reactor.listenUDP(STUN_PORT, UdpStunHandler(), interface=interface_6)
reactor.listenTCP(STUN_PORT, TcpStunHandlerFactory(), interface=interface_6)
reactor.listenUDP(
TURN_REDIRECT_PORT, UdpStunRedirectHandler(), interface=interface_6
)
reactor.listenTCP(
TURN_REDIRECT_PORT, TcpStunRedirectHandlerFactory(), interface=interface_6
)
except:
pass
@ -838,16 +935,26 @@ if __name__ == "__main__":
KEY_FILE, CERT_FILE, SSL.TLSv1_2_METHOD
)
reactor.listenSSL(
5349, TcpStunHandlerFactory(), tls_context_factory, interface=interface_4
STUNS_PORT,
TcpStunHandlerFactory(),
tls_context_factory,
interface=interface_4,
)
try:
reactor.listenSSL(
5349,
STUNS_PORT,
TcpStunHandlerFactory(),
tls_context_factory,
interface=interface_6,
)
reactor.listenSSL(
TURNS_REDIRECT_PORT,
TcpStunRedirectHandlerFactory(),
tls_context_factory,
interface=interface_6,
)
except:
pass
@ -872,7 +979,7 @@ if __name__ == "__main__":
template = Template(
'[\
{"urls":["stun:$hostname", "stun:$hostname?transport=tcp"]}, \
{"username":"$user","credential":"$pwd","urls": \
{"username":"$user","credential":"$pwd","turn_redirect_port":"$TURN_REDIRECT_PORT","turns_redirect_port":"$TURNS_REDIRECT_PORT","urls": \
["turn:$hostname", "turn:$hostname?transport=tcp" $turns_url] \
$cert_prop}]' # Hack to make it easier to override cert checks
)
@ -884,6 +991,8 @@ $cert_prop}]' # Hack to make it easier to override cert checks
hostname=hostname,
turns_url=turns_url,
cert_prop=cert_prop,
TURN_REDIRECT_PORT=TURN_REDIRECT_PORT,
TURNS_REDIRECT_PORT=TURNS_REDIRECT_PORT,
)
)