// 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); }
bool UDPPusher::onPush() { uint32_t length; if (fread(&length, 1, sizeof(length), mFile) < sizeof(length)) { LOGI("No more data to push."); return false; } length = fromlel(length); CHECK_GT(length, 0u); sp<ABuffer> buffer = new ABuffer(length); if (fread(buffer->data(), 1, length, mFile) < length) { LOGE("File truncated?."); return false; } ssize_t n = PR_SendTo( mSocket, buffer->data(), buffer->size(), 0, &mRemoteAddr, PR_INTERVAL_NO_WAIT); CHECK_EQ(n, (ssize_t)buffer->size()); uint32_t timeMs; if (fread(&timeMs, 1, sizeof(timeMs), mFile) < sizeof(timeMs)) { LOGI("No more data to push."); return false; } timeMs = fromlel(timeMs); CHECK_GE(timeMs, mFirstTimeMs); timeMs -= mFirstTimeMs; int64_t whenUs = mFirstTimeUs + timeMs * 1000ll; int64_t nowUs = ALooper::GetNowUs(); (new AMessage(kWhatPush, id()))->post(whenUs - nowUs); return true; }
void UDPPusher::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatPush: { if (!onPush() && !(ntohs(mRemoteAddr.sin_port) & 1)) { LOGI("emulating BYE packet"); sp<ABuffer> buffer = new ABuffer(8); uint8_t *data = buffer->data(); *data++ = (2 << 6) | 1; *data++ = 203; *data++ = 0; *data++ = 1; *data++ = 0x8f; *data++ = 0x49; *data++ = 0xc0; *data++ = 0xd0; buffer->setRange(0, 8); struct sockaddr_in tmp = mRemoteAddr; tmp.sin_port = htons(ntohs(mRemoteAddr.sin_port) | 1); ssize_t n = PR_SendTo( mSocket, buffer->data(), buffer->size(), 0, &tmp, PR_INTERVAL_NO_WAIT); CHECK_EQ(n, (ssize_t)buffer->size()); } break; } default: TRESPASS(); break; } }
// 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); }
/* * ar_send_res_msg * * When sending queries to nameservers listed in the resolv.conf file, * don't send a query to every one, but increase the number sent linearly * to match the number of resends. This increase only occurs if there are * multiple nameserver entries in the resolv.conf file. * The return value is the number of messages successfully sent to * nameservers or -1 if no successful sends. */ int Resolver :: ar_send_res_msg(char *msg, int len, int rcount) { int i; int sent = 0; #ifdef HPUX /* VB: _res on HP is thread specific and needs to be initialized on every thread */ if (!(_res.options & RES_INIT)) { res_init(); } #endif #ifdef Linux res_init_tls(); #endif if (!msg) return -1; if (_res.options & RES_PRIMARY) rcount = 1; rcount = (_res.nscount > rcount) ? rcount : _res.nscount; if (ar_vc) { ar_reinfo.re_sent++; sent++; if (PR_Send(afd, msg, len, 0, PR_INTERVAL_NO_TIMEOUT) == -1) { int errtmp = errno; PR_ASSERT(0); (void)PR_Close(afd); errno = errtmp; afd = NULL; } } else for (i = 0; i < rcount; i++) { struct sockaddr_in sin = _res.nsaddr_list[i]; // XXX jpierre is this legal ??? PRNetAddr *paddr = (PRNetAddr *)&sin; #ifdef AIX PRNetAddr addr; /* For AIX struct sockaddr_in { uchar_t sin_len; * sa_family_t sin_family; * in_port_t sin_port; * struct in_addr sin_addr; uchar_t sin_zero[8]; }; * struct in_addr { in_addr_t s_addr; }; * typedef uint32_t in_addr_t; * * In NSPR PRNetAddr is : * union PRNetAddr { struct { PRUint16 family; * char data[14]; } raw; * struct { PRUint16 family; PRUint16 port; * PRUint32 ip; char pad[8]; } inet; ... } */ PR_ASSERT(sin.sin_family == AF_INET); /* xxx strange when request is sent to IPV6 address * then also address family is AF_INET */ memset(&addr, 0, sizeof(addr)); addr.raw.family = sin.sin_family; addr.inet.family = sin.sin_family; addr.inet.port = sin.sin_port; addr.inet.ip = sin.sin_addr.s_addr; memcpy(addr.inet.pad, sin.sin_zero, 8); paddr = &addr; #endif if (PR_SendTo(afd, msg, len, 0, paddr, PR_INTERVAL_NO_TIMEOUT) == len) { ar_reinfo.re_sent++; sent++; } else { PR_ASSERT(0); } } return (sent) ? sent : -1; }
/* * UDP_Client * Client Thread * Create a socket and bind an address * Communicate with the server at the address specified in the argument. * Fill in a buffer, write data to server, read it back and check * for data corruption. * Close the socket */ static void PR_CALLBACK UDP_Client(void *arg) { Client_Param *cp = (Client_Param *) arg; PRFileDesc *sockfd; buffer *in_buf, *out_buf; union PRNetAddr netaddr; PRInt32 bytes, i, rv; bytes = cp->datalen; out_buf = PR_NEW(buffer); if (out_buf == NULL) { fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); failed_already=1; return; } in_buf = PR_NEW(buffer); if (in_buf == NULL) { fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); failed_already=1; return; } if ((sockfd = PR_NewUDPSocket()) == NULL) { fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n"); failed_already=1; return; } /* * bind an address for the client, let the system chose the port * number */ memset(&netaddr, 0 , sizeof(netaddr)); netaddr.inet.family = AF_INET; netaddr.inet.ip = PR_htonl(INADDR_ANY); netaddr.inet.port = PR_htons(0); if (PR_Bind(sockfd, &netaddr) < 0) { fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n"); perror("PR_Bind"); return; } if (PR_GetSockName(sockfd, &netaddr) < 0) { fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n"); failed_already=1; return; } DPRINTF(("PR_Bind: UDP Client netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", netaddr.inet.ip, netaddr.inet.port)); netaddr.inet.family = cp->server_addr.inet.family; netaddr.inet.port = cp->server_addr.inet.port; netaddr.inet.ip = cp->server_addr.inet.ip; if (cp->udp_connect) { if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0){ fprintf(stderr,"prsocket_test: PR_Connect failed\n"); failed_already=1; return; } } for (i = 0; i < num_udp_datagrams_per_client; i++) { /* * fill in random data */ DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx bytes = 0x%lx\n", PR_GetCurrentThread(), out_buf->data, bytes)); memset(out_buf->data, ((PRInt32) (&netaddr)) + i, bytes); /* * write to server */ if (cp->udp_connect) rv = PR_Send(sockfd, out_buf->data, bytes, 0, PR_INTERVAL_NO_TIMEOUT); else rv = PR_SendTo(sockfd, out_buf->data, bytes, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT); if (rv != bytes) { return; } DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n", PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data)))); if (cp->udp_connect) rv = PR_Recv(sockfd, in_buf->data, bytes, 0, PR_INTERVAL_NO_TIMEOUT); else rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT); if (rv != bytes) { return; } DPRINTF(("UDP_Client [0x%lx]: in_buf = 0x%lx in_buf[0] = 0x%lx\n", PR_GetCurrentThread(), in_buf, (*((int *) in_buf->data)))); /* * verify the data read */ if (memcmp(in_buf->data, out_buf->data, bytes) != 0) { fprintf(stderr,"prsocket_test: ERROR - UDP data corruption\n"); failed_already=1; return; } } PR_Close(sockfd); PR_DELETE(in_buf); PR_DELETE(out_buf); /* * Decrement exit_counter and notify parent thread */ PR_EnterMonitor(cp->exit_mon); --(*cp->exit_counter); PR_Notify(cp->exit_mon); PR_ExitMonitor(cp->exit_mon); PR_DELETE(cp); DPRINTF(("UDP_Client [0x%x] exiting\n", PR_GetCurrentThread())); }
/* * UDP Server * Server Thread * Bind an address to a socket, read data from clients and send data * back to clients */ static void PR_CALLBACK UDP_Server(void *arg) { Server_Param *sp = (Server_Param *) arg; PRFileDesc *sockfd; buffer *in_buf; PRNetAddr netaddr; PRInt32 bytes, i, rv; bytes = sp->datalen; /* * Create a udp socket */ if ((sockfd = PR_NewUDPSocket()) == NULL) { fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n"); failed_already=1; return; } memset(&netaddr, 0 , sizeof(netaddr)); netaddr.inet.family = AF_INET; netaddr.inet.port = PR_htons(UDP_SERVER_PORT); netaddr.inet.ip = PR_htonl(INADDR_ANY); /* * try a few times to bind server's address, if addresses are in * use */ i = 0; while (PR_Bind(sockfd, &netaddr) < 0) { if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { netaddr.inet.port += 2; if (i++ < SERVER_MAX_BIND_COUNT) continue; } fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n"); perror("PR_Bind"); failed_already=1; return; } if (PR_GetSockName(sockfd, &netaddr) < 0) { fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n"); failed_already=1; return; } if (netaddr.inet.port != PR_htons(UDP_SERVER_PORT)) { fprintf(stderr,"prsocket_test: ERROR - tried to bind to UDP " "port %hu, but was bound to port %hu\n", UDP_SERVER_PORT, PR_ntohs(netaddr.inet.port)); failed_already=1; return; } DPRINTF(("PR_Bind: UDP Server netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", netaddr.inet.ip, netaddr.inet.port)); udp_server_addr = netaddr; /* * We can't use the IP address returned by PR_GetSockName in * netaddr.inet.ip because netaddr.inet.ip is returned * as 0 (= INADDR_ANY). */ udp_server_addr.inet.ip = PR_htonl(INADDR_LOOPBACK); /* * Wake up parent thread because server address is bound and made * available in the global variable 'udp_server_addr' */ PR_PostSem(sp->addr_sem); bytes = sp->datalen; in_buf = PR_NEW(buffer); if (in_buf == NULL) { fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); failed_already=1; return; } /* * Receive datagrams from clients and send them back, unmodified, to the * clients */ memset(&netaddr, 0 , sizeof(netaddr)); for (i = 0; i < (num_udp_clients * num_udp_datagrams_per_client); i++) { DPRINTF(("UDP_Server: calling PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n", netaddr.inet.ip, netaddr.inet.port, rv, in_buf->data, in_buf->data[0])); rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT); DPRINTF(("UDP_Server: PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n", netaddr.inet.ip, netaddr.inet.port, rv, in_buf->data, in_buf->data[0])); if (rv != bytes) { return; } rv = PR_SendTo(sockfd, in_buf->data, bytes, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT); if (rv != bytes) { return; } } PR_DELETE(in_buf); /* * Decrement exit_counter and notify parent thread */ PR_EnterMonitor(sp->exit_mon); --(*sp->exit_counter); PR_Notify(sp->exit_mon); PR_ExitMonitor(sp->exit_mon); DPRINTF(("UDP_Server [0x%x] exiting\n", PR_GetCurrentThread())); }
int main(int argc, char* argv[]) { if (test_common_init(&argc, &argv) != 0) return -1; int returnCode = 0; nsresult rv = NS_OK; PRFileDesc *serverFD = nullptr; do { // act both as a scope for nsCOMPtrs to be released before XPCOM // shutdown, as well as a easy way to abort the test PRStatus status = PR_SUCCESS; nsCOMPtr<nsIServiceManager> servMan; NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr); nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan); UDP_ASSERT(registrar, "Null nsIComponentRegistrar"); if (registrar) registrar->AutoRegister(nullptr); // listen for a incoming UDP connection on localhost serverFD = PR_OpenUDPSocket(PR_AF_INET); UDP_ASSERT(serverFD, "Cannot open UDP socket for listening"); PRSocketOptionData socketOptions; socketOptions.option = PR_SockOpt_Nonblocking; socketOptions.value.non_blocking = false; status = PR_SetSocketOption(serverFD, &socketOptions); UDP_ASSERT_PRSTATUS("Failed to set server socket as blocking"); PRNetAddr addr; status = PR_InitializeNetAddr(PR_IpAddrLoopback, UDP_PORT, &addr); UDP_ASSERT_PRSTATUS("Failed to initialize loopback address"); status = PR_Bind(serverFD, &addr); UDP_ASSERT_PRSTATUS("Failed to bind server socket"); // dummy IOService to get around bug 379890 nsCOMPtr<nsISupports> ios = do_GetService("@mozilla.org/network/io-service;1"); // and have a matching UDP connection for the client nsCOMPtr<nsISocketTransportService> sts = do_GetService("@mozilla.org/network/socket-transport-service;1", &rv); UDP_ASSERT_NSRESULT("Cannot get socket transport service"); nsCOMPtr<nsISocketTransport> transport; const char *protocol = "udp"; rv = sts->CreateTransport(&protocol, 1, NS_LITERAL_CSTRING("localhost"), UDP_PORT, nullptr, getter_AddRefs(transport)); UDP_ASSERT_NSRESULT("Cannot create transport"); uint32_t count, read; const uint32_t data = 0xFF0056A9; // write to the output stream nsCOMPtr<nsIOutputStream> outstream; rv = transport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(outstream)); UDP_ASSERT_NSRESULT("Cannot open output stream"); rv = outstream->Write((const char*)&data, sizeof(uint32_t), &count); UDP_ASSERT_NSRESULT("Cannot write to output stream"); UDP_ASSERT(count == sizeof(uint32_t), "Did not write enough bytes to output stream"); // read from NSPR to check it's the same count = PR_RecvFrom(serverFD, &read, sizeof(uint32_t), 0, &addr, 1); UDP_ASSERT(count == sizeof(uint32_t), "Did not read enough bytes from NSPR"); status = (read == data ? PR_SUCCESS : PR_FAILURE); UDP_ASSERT_PRSTATUS("Did not read expected data from NSPR"); // write to NSPR count = PR_SendTo(serverFD, &data, sizeof(uint32_t), 0, &addr, 1); status = (count == sizeof(uint32_t) ? PR_SUCCESS : PR_FAILURE); UDP_ASSERT_PRSTATUS("Did not write enough bytes to NSPR"); // read from stream nsCOMPtr<nsIInputStream> instream; rv = transport->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(instream)); UDP_ASSERT_NSRESULT("Cannot open input stream"); rv = instream->Read((char*)&read, sizeof(uint32_t), &count); UDP_ASSERT_NSRESULT("Cannot read from input stream"); UDP_ASSERT(count == sizeof(uint32_t), "Did not read enough bytes from input stream"); UDP_ASSERT(read == data, "Did not read expected data from stream"); } while (false); // release all XPCOM things if (serverFD) { PRStatus status = PR_Close(serverFD); if (status != PR_SUCCESS) { PRErrorCode err = PR_GetError(); fprintf(stderr, "FAIL: Cannot close server: (%08x) %s\n", err, PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); } } rv = NS_ShutdownXPCOM(nullptr); NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed"); return returnCode; }