bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr *remote_addr, const uint8_t *data, uint32_t len) { // Check white list if (white_list_.find(*remote_addr) != white_list_.end()) { return true; } // Check if we had sent any stun request to this destination. If we had sent a request // to this host, we check the transaction id, and we can add this address to whitelist. std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(PendingSTUNRequest(*remote_addr)); if (it != pending_requests_.end()) { if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data); // If it is a STUN response message and we can match its id with one of the pending // requests, we can add this address into whitelist. if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { PendingSTUNRequest pending_req(*remote_addr, msg->id); std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(pending_req); if (it != pending_requests_.end()) { pending_requests_.erase(it); response_allowed_.erase(pending_req); white_list_.insert(*remote_addr); } } else { // If it is a STUN message, but not a response message, we add it into response // allowed list and allow outgoing filter to send a response back. response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id)); } } return true; } return false; }
int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) { int r; if (port_mappings_.empty()) { r = internal_socket_->read(buf, maxlen, len); } else { MOZ_ASSERT(port_mappings_.size() == 1); r = port_mappings_.front()->external_socket_->read(buf, maxlen, len); if (!r && nat_->refresh_on_ingress_) { port_mappings_.front()->last_used_ = PR_IntervalNow(); } } if (r) { return r; } if (nat_->block_tcp_ && !tls_) { // Should cause this socket to be abandoned return R_INTERNAL; } UCHAR *cbuf = static_cast<UCHAR*>(const_cast<void*>(buf)); if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) { // Should cause this socket to be abandoned return R_INTERNAL; } return r; }
int nr_is_stun_indication_message(UCHAR *buf, int len) { UINT2 type; if (sizeof(nr_stun_message_header) > len) return 0; if (!nr_is_stun_message(buf, len)) return 0; memcpy(&type, buf, 2); type = ntohs(type); return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION; }
int nr_is_stun_request_message(UCHAR *buf, int len) { UINT2 type; if (sizeof(nr_stun_message_header) > len) return 0; if (!nr_is_stun_message(buf, len)) return 0; memcpy(&type, buf, 2); type = ntohs(type); return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST; }
int nr_is_stun_response_message(UCHAR *buf, int len) { UINT2 type; if (sizeof(nr_stun_message_header) > len) return 0; if (!nr_is_stun_message(buf, len)) return 0; memcpy(&type, buf, 2); type = ntohs(type); return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE; }
int TestNrSocket::write(const void *msg, size_t len, size_t *written) { UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg)); if (nat_->block_stun_ && nr_is_stun_message(buf, len)) { // Should cause this socket to be abandoned r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s dropping outgoing TCP " "because it is configured to drop STUN", my_addr().as_string); return R_INTERNAL; } if (nat_->block_tcp_ && !tls_) { // Should cause this socket to be abandoned r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s dropping outgoing TCP " "because it is configured to drop TCP", my_addr().as_string); return R_INTERNAL; } if (port_mappings_.empty()) { // The no-nat case, just pass call through. r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing", my_addr().as_string); return internal_socket_->write(msg, len, written); } destroy_stale_port_mappings(); if (port_mappings_.empty()) { r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s dropping outgoing TCP " "because the port mapping was stale", my_addr().as_string); return R_INTERNAL; } // This is TCP only MOZ_ASSERT(port_mappings_.size() == 1); r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s writing", port_mappings_.front()->external_socket_->my_addr().as_string, port_mappings_.front()->remote_address_.as_string); port_mappings_.front()->last_used_ = PR_IntervalNow(); return port_mappings_.front()->external_socket_->write(msg, len, written); }
static void s_cb(int s, int how, void *cb_arg) { #if 0 nr_socket *sock = (nr_socket*)cb_arg; UCHAR buf2[4096]; size_t len; nr_transport_addr addr2; int r; StunMessage req; StunMessage res; //StunAddress4 from; fprintf(stderr,"TURN_SERVER_UTIL: SERVER CB\n"); if(r=nr_socket_recvfrom(sock,buf2,sizeof(buf2),&len,0,&addr2)){ fprintf(stderr,"Error in recvfrom\n"); exit(1); } memset(&req, 0, sizeof(req)); memset(&res, 0, sizeof(res)); //memset(&from, 0, sizeof(from)); //from.addr = ntohl(addr2.u.addr4.sin_addr.s_addr); //from.port = ntohs(addr2.u.addr4.sin_port); if ((r=nr_stun_parse_message((char*)buf2, len, &req))) { fprintf(stderr,"Error in nr_stun_parse_message\n"); exit(1); } if ((r=nr_stun_process_request(&req, buf2, len, &addr2, 0, 0, &res))) { /* failure is expected because the server code doesn't support TURN */ if (r!=R_FAILED) { fprintf(stderr,"Failed to process message!\n"); exit(1); } fprintf(stderr,"TURN_SERVER_UTIL: No problem with parse failure ... process TURN in test server code\n"); } if (!nr_is_stun_message((char*)buf2, len)) { fprintf(stderr,"TURN_SERVER_UTIL: not a STUN/TURN message\n"); /* instead of sending to remoteAddress, just echo the content back * for the purposes of our tests */ goto send; } res.hasFingerprint = 1; switch (req.msgHdr.msgType) { case AllocateRequestMsg: if (! req.hasNonce) { fprintf(stderr, "Received AllocateRequestMsg #1\n"); assert(!req.hasMessageIntegrity); /* TODO: what about username: does it go into the req or not? spec not clear */ res.msgHdr.msgType = AllocateErrorResponseMsg; res.hasUsername = 0; res.hasNonce = 1; strcpy(res.nonce, nonce); res.hasRealm = 1; strcpy(res.realm, "REALM"); res.hasErrorCode = 1; res.errorCode.errorClass = 4; res.errorCode.number = 35; } else { fprintf(stderr, "Received AllocateRequestMsg #2\n"); assert(req.hasUsername); assert(req.hasRealm); assert(req.hasNonce); assert(req.hasMessageIntegrity); res.msgHdr.msgType = AllocateResponseMsg; res.hasUsername = 1; res.hasMessageIntegrity = 1; strcpy(res.username, req.username); #if 0 //TODO: this is totally broken, but TURN is changing and so //TODO: we'll need to take another pass at all this code //TODO: anyhow, so leave it broken for now res.hasRelayAddress = 1; res.relayAddress.family = IPv4Family; res.relayAddress.ipv4.addr = from.addr; res.relayAddress.ipv4.port = from.port; res.hasXorMappedAddress = 1; res.xorMappedAddress.family = IPv4Family; res.xorMappedAddress.ipv4.addr = from.addr; res.xorMappedAddress.ipv4.port = from.port; #endif } break; case SendIndicationMsg: fprintf(stderr, "Received SendIndicationMsg\n"); assert(req.hasRemoteAddress); /* pretend to send empty UDP packet to remoteAddress per the * TURN spec and to wait for a DataIndication resonse */ fprintf(stderr, "Sending UDP packet to REMOTE-ADDRESS...\n"); fprintf(stderr, "Waiting for response from REMOTE-ADDRESS...\n"); /* ok, this is an indication, so formally there is no "response", * but we're going to send a data indication to it, so just pretend * that it's a formal response */ res.msgHdr.msgType = DataIndicationMsg; res.hasRemoteAddress = 1; nr_transport_addr_copy(&res.remoteAddress, &req.remoteAddress); break; case SetActiveDestRequestMsg: fprintf(stderr, "Received SetActiveDestRequestMsg\n"); assert(req.hasRemoteAddress); res.msgHdr.msgType = SetActiveDestResponseMsg; break; default: assert(0); break; } memset(&buf2, 0, sizeof(buf2)); if ((r=nr_stun_encode_message(STUN_MODE_STUN, &res, (char*)buf2, STUN_MAX_MESSAGE_SIZE, &pass, (unsigned int*)&len))) { fprintf(stderr,"Error encoding TURN response\n"); exit(1); } send: fprintf(stderr,"Sending response to %s\n", addr2.as_string); if(r=nr_socket_sendto(sock,buf2,len,0,&addr2)) { fprintf(stderr,"Error sending TURN response\n"); exit(1); } NR_ASYNC_WAIT(s, how, s_cb, cb_arg); #else UNIMPLEMENTED; #endif }
int TestNrSocket::sendto(const void *msg, size_t len, int flags, nr_transport_addr *to) { MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP); UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg)); if (nat_->block_stun_ && nr_is_stun_message(buf, len)) { return 0; } /* TODO: improve the functionality of this in bug 1253657 */ if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) { if (nat_->delay_stun_resp_ms_ && nr_is_stun_response_message(buf, len)) { NR_ASYNC_TIMER_SET(nat_->delay_stun_resp_ms_, process_delayed_cb, new DeferredPacket(this, msg, len, flags, to, internal_socket_), &timer_handle_); return 0; } return internal_socket_->sendto(msg, len, flags, to); } destroy_stale_port_mappings(); if (to->protocol == IPPROTO_UDP && nat_->block_udp_) { // Silently eat the packet return 0; } // Choose our port mapping based on our most selective criteria PortMapping *port_mapping = get_port_mapping(*to, std::max(nat_->filtering_type_, nat_->mapping_type_)); if (!port_mapping) { // See if we have already made the external socket we need to use. PortMapping *similar_port_mapping = get_port_mapping(*to, nat_->mapping_type_); RefPtr<NrSocketBase> external_socket; if (similar_port_mapping) { external_socket = similar_port_mapping->external_socket_; } else { external_socket = create_external_socket(*to); if (!external_socket) { MOZ_ASSERT(false); return R_INTERNAL; } } port_mapping = create_port_mapping(*to, external_socket); port_mappings_.push_back(port_mapping); if (poll_flags() & PR_POLL_READ) { // Make sure the new port mapping is ready to receive traffic if the // TestNrSocket is already waiting. port_mapping->async_wait(NR_ASYNC_WAIT_READ, socket_readable_callback, this, (char*)__FUNCTION__, __LINE__); } } // We probably don't want to propagate the flags, since this is a simulated // external IP address. return port_mapping->sendto(msg, len, *to); }