Bug 963524 - Avoid setting SO_REUSEADDR when binding a UDP socket to port 0, since the linux kernel might select an already open port. r=mcmanus, a=sledru
authorByron Campen [:bwc] <docfaraday@gmail.com>
Wed, 30 Jul 2014 15:02:58 -0700
changeset 208240 92364eef664a
parent 208239 784c7fb4c431
child 208241 333e7f930c63
push id3788
push userryanvm@gmail.com
push date2014-08-06 15:01 +0000
treeherdermozilla-beta@adb28e85421f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus, sledru
bugs963524
milestone32.0
Bug 963524 - Avoid setting SO_REUSEADDR when binding a UDP socket to port 0, since the linux kernel might select an already open port. r=mcmanus, a=sledru
netwerk/base/src/nsUDPSocket.cpp
netwerk/dns/DNS.cpp
netwerk/dns/DNS.h
--- a/netwerk/base/src/nsUDPSocket.cpp
+++ b/netwerk/base/src/nsUDPSocket.cpp
@@ -579,21 +579,31 @@ nsUDPSocket::InitWithAddress(const NetAd
 
   mFD = PR_OpenUDPSocket(aAddr->raw.family);
   if (!mFD)
   {
     NS_WARNING("unable to create UDP socket");
     return NS_ERROR_FAILURE;
   }
 
+  uint16_t port;
+  if (NS_FAILED(net::GetPort(aAddr, &port))) {
+    NS_WARNING("invalid bind address");
+    goto fail;
+  }
+
   PRSocketOptionData opt;
 
-  opt.option = PR_SockOpt_Reuseaddr;
-  opt.value.reuse_addr = true;
-  PR_SetSocketOption(mFD, &opt);
+  // Linux kernel will sometimes hand out a used port if we bind
+  // to port 0 with SO_REUSEADDR
+  if (port) {
+    opt.option = PR_SockOpt_Reuseaddr;
+    opt.value.reuse_addr = true;
+    PR_SetSocketOption(mFD, &opt);
+  }
 
   opt.option = PR_SockOpt_Nonblocking;
   opt.value.non_blocking = true;
   PR_SetSocketOption(mFD, &opt);
 
   PRNetAddr addr;
   PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr);
   NetAddrToPRNetAddr(aAddr, &addr);
@@ -645,26 +655,20 @@ nsUDPSocket::Close()
   }
   return PostEvent(this, &nsUDPSocket::OnMsgClose);
 }
 
 NS_IMETHODIMP
 nsUDPSocket::GetPort(int32_t *aResult)
 {
   // no need to enter the lock here
-  uint16_t port;
-  if (mAddr.raw.family == PR_AF_INET)
-    port = mAddr.inet.port;
-  else if (mAddr.raw.family == PR_AF_INET6)
-    port = mAddr.inet6.port;
-  else
-    return NS_ERROR_NOT_INITIALIZED;
-
-  *aResult = static_cast<int32_t>(NetworkEndian::readUint16(&port));
-  return NS_OK;
+  uint16_t result;
+  nsresult rv = net::GetPort(&mAddr, &result);
+  *aResult = static_cast<int32_t>(result);
+  return rv;
 }
 
 NS_IMETHODIMP
 nsUDPSocket::GetAddress(NetAddr *aResult)
 {
   // no need to enter the lock here
   memcpy(aResult, &mAddr, sizeof(mAddr));
   return NS_OK;
--- a/netwerk/dns/DNS.cpp
+++ b/netwerk/dns/DNS.cpp
@@ -199,16 +199,32 @@ bool IsIPAddrLocal(const NetAddr *addr)
         addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.
       return true;
     }
   }
   // Not an IPv4/6 local address.
   return false;
 }
 
+nsresult
+GetPort(const NetAddr *aAddr, uint16_t *aResult)
+{
+  uint16_t port;
+  if (aAddr->raw.family == PR_AF_INET) {
+    port = aAddr->inet.port;
+  } else if (aAddr->raw.family == PR_AF_INET6) {
+    port = aAddr->inet6.port;
+  } else {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  *aResult = ntohs(port);
+  return NS_OK;
+}
+
 bool
 NetAddr::operator == (const NetAddr& other) const
 {
   if (this->raw.family != other.raw.family) {
     return false;
   } else if (this->raw.family == AF_INET) {
     return (this->inet.port == other.inet.port) &&
            (this->inet.ip == other.inet.ip);
--- a/netwerk/dns/DNS.h
+++ b/netwerk/dns/DNS.h
@@ -161,12 +161,14 @@ bool NetAddrToString(const NetAddr *addr
 bool IsLoopBackAddress(const NetAddr *addr);
 
 bool IsIPAddrAny(const NetAddr *addr);
 
 bool IsIPAddrV4Mapped(const NetAddr *addr);
 
 bool IsIPAddrLocal(const NetAddr *addr);
 
+nsresult GetPort(const NetAddr *aAddr, uint16_t *aResult);
+
 } // namespace net
 } // namespace mozilla
 
 #endif // DNS_h_