int NrSocket::connect(nr_transport_addr *addr) { ASSERT_ON_THREAD(ststhread_); int r,_status; PRNetAddr naddr; int32_t status; if ((r=nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r); if(!fd_) ABORT(R_EOD); // Note: this just means we tried to connect, not that we // are actually live. connect_invoked_ = true; status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT); if (status != PR_SUCCESS) { if (PR_GetError() == PR_IN_PROGRESS_ERROR) ABORT(R_WOULDBLOCK); ABORT(R_IO_ERROR); } _status=0; abort: return(_status); }
// This should be called on the STS thread. int NrSocket::sendto(const void *msg, size_t len, int flags, nr_transport_addr *to) { ASSERT_ON_THREAD(ststhread_); int r,_status; PRNetAddr naddr; int32_t status; if ((r=nr_transport_addr_to_praddr(to, &naddr))) ABORT(r); if(fd_==nullptr) ABORT(R_EOD); // TODO: Convert flags? status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT); if (status < 0 || (size_t)status != len) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s", to->as_string); ABORT(R_IO_ERROR); } _status=0; abort: return(_status); }
// This should be called on the STS thread. int NrSocket::sendto(const void *msg, size_t len, int flags, nr_transport_addr *to) { ASSERT_ON_THREAD(ststhread_); int r,_status; PRNetAddr naddr; int32_t status; if ((r=nr_transport_addr_to_praddr(to, &naddr))) ABORT(r); if(fd_==nullptr) ABORT(R_EOD); if (nr_is_stun_request_message((UCHAR*)msg, len)) { // Global rate limiting for stun requests, to mitigate the ice hammer DoS // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc) // Tolerate rate of 8k/sec, for one second. static SimpleTokenBucket burst(8192*1, 8192); // Tolerate rate of 3.6k/sec over twenty seconds. static SimpleTokenBucket sustained(3686*20, 3686); // Check number of tokens in each bucket. if (burst.getTokens(UINT32_MAX) < len || sustained.getTokens(UINT32_MAX) < len) { r_log(LOG_GENERIC, LOG_ERR, "Global rate limit for STUN requests exceeded."); MOZ_ASSERT("Global rate limit for STUN requests exceeded. Go bug " "[email protected] if you weren't intentionally spamming " "ICE candidates, or don't know what that means."); ABORT(R_WOULDBLOCK); } // Take len tokens from both buckets. // (not threadsafe, but no problem since this is only called from STS) burst.getTokens(len); sustained.getTokens(len); } // TODO: Convert flags? status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT); if (status < 0 || (size_t)status != len) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s", to->as_string); ABORT(R_IO_ERROR); } _status=0; abort: return(_status); }
static int nr_transport_addr_to_netaddr(nr_transport_addr *addr, net::NetAddr *naddr) { int r, _status; PRNetAddr praddr; if((r = nr_transport_addr_to_praddr(addr, &praddr))) { ABORT(r); } if((r = praddr_to_netaddr(&praddr, naddr))) { ABORT(r); } _status = 0; abort: return(_status); }
// This should be called on the STS thread. int NrSocket::sendto(const void *msg, size_t len, int flags, nr_transport_addr *to) { ASSERT_ON_THREAD(ststhread_); int r,_status; PRNetAddr naddr; int32_t status; if ((r=nr_transport_addr_to_praddr(to, &naddr))) ABORT(r); if(fd_==nullptr) ABORT(R_EOD); if (nr_is_stun_request_message((UCHAR*)msg, len)) { // Global rate limiting for stun requests, to mitigate the ice hammer DoS // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc) // Tolerate rate of 8k/sec, for one second. static SimpleTokenBucket burst(16384*1, 16384); // Tolerate rate of 7.2k/sec over twenty seconds. static SimpleTokenBucket sustained(7372*20, 7372); // Check number of tokens in each bucket. if (burst.getTokens(UINT32_MAX) < len) { r_log(LOG_GENERIC, LOG_ERR, "Short term global rate limit for STUN requests exceeded."); #ifdef MOZILLA_INTERNAL_API nr_socket_short_term_violation_time = TimeStamp::Now(); #endif // Bug 1013007 #if !EARLY_BETA_OR_EARLIER ABORT(R_WOULDBLOCK); #else MOZ_ASSERT(false, "Short term global rate limit for STUN requests exceeded. Go " "bug [email protected] if you weren't intentionally " "spamming ICE candidates, or don't know what that means."); #endif } if (sustained.getTokens(UINT32_MAX) < len) { r_log(LOG_GENERIC, LOG_ERR, "Long term global rate limit for STUN requests exceeded."); #ifdef MOZILLA_INTERNAL_API nr_socket_long_term_violation_time = TimeStamp::Now(); #endif // Bug 1013007 #if !EARLY_BETA_OR_EARLIER ABORT(R_WOULDBLOCK); #else MOZ_ASSERT(false, "Long term global rate limit for STUN requests exceeded. Go " "bug [email protected] if you weren't intentionally " "spamming ICE candidates, or don't know what that means."); #endif } // Take len tokens from both buckets. // (not threadsafe, but no problem since this is only called from STS) burst.getTokens(len); sustained.getTokens(len); } // TODO: Convert flags? status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT); if (status < 0 || (size_t)status != len) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s", to->as_string); ABORT(R_IO_ERROR); } _status=0; abort: return(_status); }
// nr_socket APIs (as member functions) int NrSocket::create(nr_transport_addr *addr) { int r,_status; PRStatus status; PRNetAddr naddr; nsresult rv; nsCOMPtr<nsISocketTransportService> stservice = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (!NS_SUCCEEDED(rv)) { ABORT(R_INTERNAL); } if((r=nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r); switch (addr->protocol) { case IPPROTO_UDP: if (!(fd_ = PR_NewUDPSocket())) { r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket"); ABORT(R_INTERNAL); } break; case IPPROTO_TCP: if (!(fd_ = PR_NewTCPSocket())) { r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket"); ABORT(R_INTERNAL); } break; default: ABORT(R_INTERNAL); } status = PR_Bind(fd_, &naddr); if (status != PR_SUCCESS) { r_log(LOG_GENERIC,LOG_CRIT,"Couldn't bind socket to address %s", addr->as_string); ABORT(R_INTERNAL); } r_log(LOG_GENERIC,LOG_DEBUG,"Creating socket %p with addr %s", fd_, addr->as_string); nr_transport_addr_copy(&my_addr_,addr); /* If we have a wildcard port, patch up the addr */ if(nr_transport_addr_is_wildcard(addr)){ status = PR_GetSockName(fd_, &naddr); if (status != PR_SUCCESS){ r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket"); ABORT(R_INTERNAL); } if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,addr->protocol,1))) ABORT(r); } // Set nonblocking PRSocketOptionData option; option.option = PR_SockOpt_Nonblocking; option.value.non_blocking = PR_TRUE; status = PR_SetSocketOption(fd_, &option); if (status != PR_SUCCESS) { r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking"); ABORT(R_INTERNAL); } // Remember our thread. ststhread_ = do_QueryInterface(stservice, &rv); if (!NS_SUCCEEDED(rv)) ABORT(R_INTERNAL); // Finally, register with the STS rv = stservice->AttachSocket(fd_, this); if (!NS_SUCCEEDED(rv)) { ABORT(R_INTERNAL); } _status = 0; abort: return(_status); }