Bug 853423 - Block speculative connections for local IP addresses r=mcmanus
authorSteve Workman <sworkman@mozilla.com>
Sat, 26 Oct 2013 13:27:23 -0700
changeset 166180 8ca46c86c440ca0a3d735391519ceea2a5ae7f30
parent 166179 7027f0e1334c078dfd156790ff6d61b12a009e18
child 166181 34169163c4f30e6e4708485a0cb31ca288972364
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs853423
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 853423 - Block speculative connections for local IP addresses r=mcmanus
CLOBBER
netwerk/base/public/nsISocketTransport.idl
netwerk/base/src/nsSocketTransport2.cpp
netwerk/dns/DNS.cpp
netwerk/dns/DNS.h
netwerk/protocol/http/nsHttpConnectionInfo.cpp
netwerk/protocol/http/nsHttpConnectionInfo.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 914270 needs a clobber since moving variables to moz.build always requires a clobber (bug 852814)
+Bug 853423 - New code (inc. new function IsIPAddrLocal) is not being included in incremental builds.
--- a/netwerk/base/public/nsISocketTransport.idl
+++ b/netwerk/base/public/nsISocketTransport.idl
@@ -172,16 +172,22 @@ interface nsISocketTransport : nsITransp
 
     /**
      * If set, we will skip all IPv4 addresses the host may have and only
      * connect to IPv6 ones.
      */
     const unsigned long DISABLE_IPV4 = (1 << 4);
 
     /**
+     * If set, indicates that the socket should not connect if the hostname
+     * resolves to an RFC1918 address or IPv6 equivalent.
+     */
+    const unsigned long DISABLE_RFC1918 = (1 << 5);
+
+    /**
      * Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or
      * IPTOS_CLASS_CSx (or IPTOS_DSCP_EF, but currently no supported
      * services require expedited-forwarding).
      * Not setting this value will leave the socket with the default
      * ToS value, which on most systems if IPTOS_CLASS_CS0 (formerly
      * IPTOS_PREC_ROUTINE).
      */
     attribute octet QoSBits;
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -1156,16 +1156,38 @@ nsSocketTransport::InitiateSocket()
     if (gIOService->IsOffline()) {
         bool isLocal;
 
         IsLocal(&isLocal);
         if (!isLocal)
             return NS_ERROR_OFFLINE;
     }
 
+    // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
+    // connected - Bug 853423.
+    if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
+        IsIPAddrLocal(&mNetAddr)) {
+#ifdef PR_LOGGING
+        if (SOCKET_LOG_ENABLED()) {
+            nsAutoCString netAddrCString;
+            netAddrCString.SetCapacity(kIPv6CStrBufSize);
+            if (!NetAddrToString(&mNetAddr,
+                                 netAddrCString.BeginWriting(),
+                                 kIPv6CStrBufSize))
+                netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
+            SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
+                        "speculative connection for host [%s:%d] proxy "
+                        "[%s:%d] with Local IP address [%s]",
+                        mHost.get(), mPort, mProxyHost.get(), mProxyPort,
+                        netAddrCString.get()));
+        }
+#endif
+        return NS_ERROR_CONNECTION_REFUSED;
+    }
+
     //
     // find out if it is going to be ok to attach another socket to the STS.
     // if not then we have to wait for the STS to tell us that it is ok.
     // the notification is asynchronous, which means that when we could be
     // in a race to call AttachSocket once notified.  for this reason, when
     // we get notified, we just re-enter this function.  as a result, we are
     // sure to ask again before calling AttachSocket.  in this way we deal
     // with the race condition.  though it isn't the most elegant solution,
--- a/netwerk/dns/DNS.cpp
+++ b/netwerk/dns/DNS.cpp
@@ -172,16 +172,42 @@ bool IsIPAddrAny(const NetAddr *addr)
 bool IsIPAddrV4Mapped(const NetAddr *addr)
 {
   if (addr->raw.family == AF_INET6) {
     return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip);
   }
   return false;
 }
 
+bool IsIPAddrLocal(const NetAddr *addr)
+{
+  MOZ_ASSERT(addr);
+
+  // IPv4 RFC1918 and Link Local Addresses.
+  if (addr->raw.family == AF_INET) {
+    uint32_t addr32 = ntohl(addr->inet.ip);
+    if (addr32 >> 24 == 0x0A ||    // 10/8 prefix (RFC 1918).
+        addr32 >> 20 == 0xAC1 ||   // 172.16/12 prefix (RFC 1918).
+        addr32 >> 16 == 0xC0A8 ||  // 192.168/16 prefix (RFC 1918).
+        addr32 >> 16 == 0xA9FE) {  // 169.254/16 prefix (Link Local).
+      return true;
+    }
+  }
+  // IPv6 Unique and Link Local Addresses.
+  if (addr->raw.family == AF_INET6) {
+    uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]);
+    if (addr16 >> 9 == 0xfc >> 1 ||   // fc00::/7 Unique Local Address.
+        addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.
+      return true;
+    }
+  }
+  // Not an IPv4/6 local address.
+  return false;
+}
+
 NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
 {
   PRNetAddrToNetAddr(prNetAddr, &mAddress);
 }
 
 NetAddrElement::NetAddrElement(const NetAddrElement& netAddr)
 {
   mAddress = netAddr.mAddress;
--- a/netwerk/dns/DNS.h
+++ b/netwerk/dns/DNS.h
@@ -155,12 +155,14 @@ void NetAddrToPRNetAddr(const NetAddr *a
 bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize);
 
 bool IsLoopBackAddress(const NetAddr *addr);
 
 bool IsIPAddrAny(const NetAddr *addr);
 
 bool IsIPAddrV4Mapped(const NetAddr *addr);
 
+bool IsIPAddrLocal(const NetAddr *addr);
+
 } // namespace net
 } // namespace mozilla
 
 #endif // DNS_h_
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -3,16 +3,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "nsHttpConnectionInfo.h"
+#include "mozilla/net/DNS.h"
+#include "prnetdb.h"
+
+using namespace mozilla::net;
 
 nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &host, int32_t port,
                                            nsProxyInfo* proxyInfo,
                                            bool usingSSL)
     : mRef(0)
     , mProxyInfo(proxyInfo)
     , mUsingSSL(usingSSL)
     , mUsingConnect(false)
@@ -108,8 +112,24 @@ nsHttpConnectionInfo::Clone() const
 bool
 nsHttpConnectionInfo::UsingProxy()
 {
     if (!mProxyInfo)
         return false;
     return !mProxyInfo->IsDirect();
 }
 
+bool
+nsHttpConnectionInfo::HostIsLocalIPLiteral() const
+{
+    PRNetAddr prAddr;
+    // If the host/proxy host is not an IP address literal, return false.
+    if (ProxyHost()) {
+        if (PR_StringToNetAddr(ProxyHost(), &prAddr) != PR_SUCCESS) {
+          return false;
+        }
+    } else if (PR_StringToNetAddr(Host(), &prAddr) != PR_SUCCESS) {
+        return false;
+    }
+    NetAddr netAddr;
+    PRNetAddrToNetAddr(&prAddr, &netAddr);
+    return IsIPAddrLocal(&netAddr);
+}
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -87,16 +87,19 @@ public:
     void          SetPrivate(bool priv)  { mHashKey.SetCharAt(priv ? 'P' : '.', 3); }
     bool          GetPrivate() const     { return mHashKey.CharAt(3) == 'P'; }
 
     const nsCString &GetHost() { return mHost; }
 
     // Returns true for any kind of proxy (http, socks, etc..)
     bool UsingProxy();
 
+    // Returns true when mHost is an RFC1918 literal.
+    bool HostIsLocalIPLiteral() const;
+
 private:
     mozilla::ThreadSafeAutoRefCnt mRef;
     nsCString              mHashKey;
     nsCString              mHost;
     int32_t                mPort;
     nsCOMPtr<nsProxyInfo>  mProxyInfo;
     bool                   mUsingHttpProxy;
     bool                   mUsingSSL;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -352,16 +352,24 @@ nsHttpConnectionMgr::SpeculativeConnect(
                                         nsIInterfaceRequestor *callbacks,
                                         uint32_t caps)
 {
     MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!");
 
     LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
          ci->HashKey().get()));
 
+    // Hosts that are Local IP Literals should not be speculatively
+    // connected - Bug 853423.
+    if (ci && ci->HostIsLocalIPLiteral()) {
+        LOG(("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
+             "address [%s]", ci->Host()));
+        return NS_OK;
+    }
+
     nsRefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
 
     // Wrap up the callbacks and the target to ensure they're released on the target
     // thread properly.
     nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
     NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
 
     caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
@@ -1979,23 +1987,23 @@ nsresult
 nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
                                      nsAHttpTransaction *trans,
                                      uint32_t caps,
                                      bool speculative)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
+    if (speculative)
+        sock->SetSpeculative(true);
     nsresult rv = sock->SetupPrimaryStreams();
     NS_ENSURE_SUCCESS(rv, rv);
 
     ent->mHalfOpens.AppendElement(sock);
     mNumHalfOpenConns++;
-    if (speculative)
-        sock->SetSpeculative(true);
     return NS_OK;
 }
 
 // This function tries to dispatch the pending spdy transactions on
 // the connection entry sent in as an argument. It will do so on the
 // active spdy connection either in that same entry or in the
 // redirected 'preferred' entry for the same coalescing hash key if
 // coalescing is enabled.
@@ -2706,16 +2714,20 @@ nsHalfOpenSocket::SetupStreams(nsISocket
     if (mEnt->mPreferIPv6) {
         tmpFlags |= nsISocketTransport::DISABLE_IPV4;
     }
     else if (mEnt->mPreferIPv4 ||
              (isBackup && gHttpHandler->FastFallbackToIPv4())) {
         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
     }
 
+    if (IsSpeculative()) {
+        tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
+    }
+
     socketTransport->SetConnectionFlags(tmpFlags);
 
     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
 
     rv = socketTransport->SetEventSink(this, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = socketTransport->SetSecurityCallbacks(this);