class StunUdpServer(StunUdpProtocol): def __init__(self, reactor, interface, port, software): StunUdpProtocol.__init__(self, reactor, interface, port, software) def respond(self, response, addr): response.add_attr(attributes.Software, self.software) self.credential_mechanism.update(response) response.add_attr(attributes.Fingerprint) self.transport.write(response, addr) logger.info("%s Sending response", self) logger.debug(response.format()) def _stun_binding_request(self, msg, (host, port)): if msg.msg_class == stun.CLASS_REQUEST: unknown_attributes = msg.unknown_comp_required_attrs() if unknown_attributes: response = Message.encode(stun.METHOD_BINDING, stun.CLASS_RESPONSE_ERROR, transaction_id=msg.transaction_id) response.add_attr(attributes.ErrorCode, *stun.ERR_UNKNOWN_ATTRIBUTE) response.add_attr(attributes.UnknownAttributes, unknown_attributes) else: response = Message.encode(stun.METHOD_BINDING, stun.CLASS_RESPONSE_SUCCESS, transaction_id=msg.transaction_id) family = Address.aftof(self.transport.addressFamily) response.add_attr(attributes.XorMappedAddress, family, port, host) response.add_attr(attributes.Software, self.software) response.add_attr(attributes.Fingerprint) self.respond(response, (host, port))
def allocate(cls, server, client_addr, port=0): relay = cls(server, client_addr) port = server.reactor.listenUDP(port, relay, server.interface) family = Address.aftof(relay.transport.socket.family) relay_ip, port = relay.transport.socket.getsockname() relay.relay_addr = (family, port, relay_ip) logger.info("%s Allocated", relay) return relay
def allocate(cls, server, client_addr, port=0): relay = cls(server, client_addr) port = server.reactor.listenUDP(port, relay, server.interface) family = Address.aftof(relay.transport.socket.family) relay_ip, port = relay.transport.socket.getsockname()[:2] relay.relay_addr = (family, port, relay_ip) logger.info("%s Allocated", relay) return relay
def _stun_binding_request(self, msg, addr): if msg.msg_class == stun.CLASS_REQUEST: unknown_attributes = msg.unknown_comp_required_attrs() if unknown_attributes: response = Message.encode(stun.METHOD_BINDING, stun.CLASS_RESPONSE_ERROR, transaction_id=msg.transaction_id) response.add_attr(attributes.ErrorCode, *stun.ERR_UNKNOWN_ATTRIBUTE) response.add_attr(attributes.UnknownAttributes, unknown_attributes) else: response = Message.encode(stun.METHOD_BINDING, stun.CLASS_RESPONSE_SUCCESS, transaction_id=msg.transaction_id) family = Address.aftof(self.transport.addressFamily) host, port = self.overrides.get('mapped_address', addr) response.add_attr(attributes.XorMappedAddress, family, port, host) response.add_attr(attributes.Software, self.software) self.transport.write(response, addr) logger.info("%s Sending response", self) logger.debug(response.format())
def datagramReceived(self, datagram, addr): """ :see: http://tools.ietf.org/html/rfc5766#section-10.3 """ logger.info("%s <- %s:%d", self, *addr) host, port = addr if host in self.permissions: channel = self._channels.get(addr) if channel: # TODO: send channel message to client raise NotImplementedError("Send channel message") else: msg = Message.encode(turn.METHOD_DATA, stun.CLASS_INDICATION) family = Address.aftof(self.transport.addressFamily) msg.add_attr(attributes.XorPeerAddress, family, port, host) msg.add_attr(attributes.Data, datagram) self.server.transport.write(msg, self.client_addr) else: logger.warning("No permissions for %s: Dropping datagram", host) logger.debug(datagram.encode('hex'))
def _stun_allocate_request(self, msg, addr): """ :see: http://tools.ietf.org/html/rfc5766#section-6.2 """ # Detect retransmission, resend success response relay_allocation = self._relays.get(addr) if relay_allocation and relay_allocation.transaction_id == msg.transaction_id: # TODO: handle allocate retransmission raise NotImplementedError("Allocation retransmission") # 1. require request to be authenticated message_integrity = msg.get_attr(stun.ATTR_MESSAGE_INTEGRITY) if not message_integrity: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *stun.ERR_UNAUTHORIZED) self.respond(response, addr) return # 2. Check if the 5-tuple is currently in use if relay_allocation: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *turn.ERR_ALLOCATION_MISMATCH) self.respond(response, addr) return # 3. Check REQUESTED-TRANSPORT attribute requested_transport = msg.get_attr(turn.ATTR_REQUESTED_TRANSPORT) if not requested_transport: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *stun.ERR_BAD_REQUEST) self.respond(response, addr) return elif requested_transport.protocol != turn.TRANSPORT_UDP: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *turn.ERR_UNSUPPORTED_TRANSPORT_PROTOCOL) self.respond(response, addr) return # 4. handle DONT-FRAGMENT attribute # 5. Check RESERVATION-TOKEN attribute reservation_token = msg.get_attr(turn.ATTR_RESERVATION_TOKEN) even_port = msg.get_attr(turn.ATTR_EVEN_PORT) if reservation_token: if even_port: pass # TODO: reject with 400 relay_addr = self.get_reserved_transport_address(reservation_token) # TODO: check that token is in range and has not expired # and that corresponding relayed address is still available # if token not valid, reject with 508 # 7. reject with 486 if username allocation quota reached # 8. reject with 300 if we want to redirect to another server RFC5389 # 6. Check EVEN-PORT else: relay, token = self._allocate_relay_addr(even_port, addr) relay.transaction_id = msg.transaction_id relay_addr = relay.relay_addr # Determine initial time-to-expiry time_to_expiry = self._time_to_expiry(msg.get_attr(turn.ATTR_LIFETIME)) response = msg.create_response(stun.CLASS_RESPONSE_SUCCESS) response.add_attr(XorRelayedAddress, *relay_addr) if token: response.add_attr(ReservationToken, token) response.add_attr(Lifetime, time_to_expiry) family = Address.aftof(self.transport.addressFamily) host, port = addr response.add_attr(XorMappedAddress, family, port, host) self.respond(response, addr)
def _stun_allocate_request(self, msg, addr): """ :see: http://tools.ietf.org/html/rfc5766#section-6.2 """ # Detect retransmission, resend success response relay_allocation = self._relays.get(addr) if relay_allocation and relay_allocation.transaction_id == msg.transaction_id: # TODO: handle allocate retransmission raise NotImplementedError("Allocation retransmission") # 1. require request to be authenticated message_integrity = msg.get_attr(stun.ATTR_MESSAGE_INTEGRITY) if not message_integrity: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *stun.ERR_UNAUTHORIZED) self.respond(response, addr) return # 2. Check if the 5-tuple is currently in use if relay_allocation: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *turn.ERR_ALLOCATION_MISMATCH) self.respond(response, addr) return # 3. Check REQUESTED-TRANSPORT attribute requested_transport = msg.get_attr(turn.ATTR_REQUESTED_TRANSPORT) if not requested_transport: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *stun.ERR_BAD_REQUEST) self.respond(response, addr) return elif requested_transport.protocol != turn.TRANSPORT_UDP: response = msg.create_response(stun.CLASS_RESPONSE_ERROR) response.add_attr(ErrorCode, *turn.ERR_UNSUPPORTED_TRANSPORT_PROTOCOL) self.respond(response, addr) return # 4. handle DONT-FRAGMENT attribute # 5. Check RESERVATION-TOKEN attribute reservation_token = msg.get_attr(turn.ATTR_RESERVATION_TOKEN) even_port = msg.get_attr(turn.ATTR_EVEN_PORT) if reservation_token: if even_port: pass # TODO: reject with 400 relay_addr = self.get_reserved_transport_address(reservation_token) # TODO: check that token is in range and has not expired # and that corresponding relayed address is still available # if token not valid, reject with 508 # 7. reject with 486 if username allocation quota reached # 8. reject with 300 if we want to redirect to another server RFC5389 # 6. Check EVEN-PORT else: relay, token = self._allocate_relay_addr(even_port, addr) relay.transaction_id = msg.transaction_id relay_addr = relay.relay_addr # Determine initial time-to-expiry time_to_expiry = self._time_to_expiry(msg.get_attr(turn.ATTR_LIFETIME)) response = msg.create_response(stun.CLASS_RESPONSE_SUCCESS) response.add_attr(XorRelayedAddress, *relay_addr) if token: response.add_attr(ReservationToken, token) response.add_attr(Lifetime, time_to_expiry) family = Address.aftof(self.transport.addressFamily) host, port = self.overrides.get('mapped_address', addr) response.add_attr(XorMappedAddress, family, port, host) self.respond(response, addr)