Bug 1274605 dont update dns generations on validated renewals r=dragana
authorPatrick McManus <mcmanus@ducksong.com>
Thu, 07 Jul 2016 21:04:36 -0400
changeset 304507 bfc92f65ea8e
parent 304506 67352c9ced46
child 304508 c4fc457de3a7
push id30428
push userkwierso@gmail.com
push dateMon, 11 Jul 2016 20:56:18 +0000
treeherdermozilla-central@7eeec8b44848 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1274605
milestone50.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 1274605 dont update dns generations on validated renewals r=dragana
netwerk/dns/DNS.cpp
netwerk/dns/DNS.h
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsHostResolver.cpp
--- a/netwerk/dns/DNS.cpp
+++ b/netwerk/dns/DNS.cpp
@@ -238,17 +238,40 @@ NetAddr::operator == (const NetAddr& oth
   } else if (this->raw.family == AF_LOCAL) {
     return PL_strncmp(this->local.path, other.local.path,
                       ArrayLength(this->local.path));
 #endif
   }
   return false;
 }
 
-
+bool
+NetAddr::operator < (const NetAddr& other) const
+{
+    if (this->raw.family != other.raw.family) {
+        return this->raw.family < other.raw.family;
+    } else if (this->raw.family == AF_INET) {
+        if (this->inet.ip == other.inet.ip) {
+            return this->inet.port < other.inet.port;
+        } else {
+            return this->inet.ip < other.inet.ip;
+        }
+    } else if (this->raw.family == AF_INET6) {
+        int cmpResult = memcmp(&this->inet6.ip, &other.inet6.ip,
+                               sizeof(this->inet6.ip));
+        if (cmpResult) {
+            return cmpResult < 0;
+        } else if (this->inet6.port != other.inet6.port) {
+            return this->inet6.port < other.inet6.port;
+        } else {
+            return this->inet6.flowinfo < other.inet6.flowinfo;
+        }
+    }
+    return false;
+}
 
 NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
 {
   PRNetAddrToNetAddr(prNetAddr, &mAddress);
 }
 
 NetAddrElement::NetAddrElement(const NetAddrElement& netAddr)
 {
--- a/netwerk/dns/DNS.h
+++ b/netwerk/dns/DNS.h
@@ -104,18 +104,19 @@ union NetAddr {
     uint32_t scope_id;              /* set of interfaces for a scope */
   } inet6;
 #if defined(XP_UNIX)
   struct {                          /* Unix domain socket address */
     uint16_t family;                /* address family (AF_UNIX) */
     char path[104];                 /* null-terminated pathname */
   } local;
 #endif
-  // introduced to support nsTArray<NetAddr> (for DNSRequestParent.cpp)
+  // introduced to support nsTArray<NetAddr> comparisons and sorting
   bool operator == (const NetAddr& other) const;
+  bool operator < (const NetAddr &other) const;
 };
 
 // This class wraps a NetAddr union to provide C++ linked list
 // capabilities and other methods. It is created from a PRNetAddr,
 // which is converted to a mozilla::dns::NetAddr.
 class NetAddrElement : public LinkedListElement<NetAddrElement> {
 public:
   explicit NetAddrElement(const PRNetAddr *prNetAddr);
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -142,36 +142,34 @@ nsDNSRecord::GetNextAddr(uint16_t port, 
         }
 
         mHostRecord->addr_info_lock.Unlock();
 
         if (!mIter) {
             mDone = true;
             return NS_ERROR_NOT_AVAILABLE;
         }
-    }
-    else {
+    } else {
         mHostRecord->addr_info_lock.Unlock();
 
         if (!mHostRecord->addr) {
             // Both mHostRecord->addr_info and mHostRecord->addr are null.
             // This can happen if mHostRecord->addr_info expired and the
             // attempt to reresolve it failed.
             return NS_ERROR_NOT_AVAILABLE;
         }
         memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
         mDone = true;
     }
 
     // set given port
     port = htons(port);
     if (addr->raw.family == AF_INET) {
         addr->inet.port = port;
-    }
-    else if (addr->raw.family == AF_INET6) {
+    } else if (addr->raw.family == AF_INET6) {
         addr->inet6.port = port;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDNSRecord::GetAddresses(nsTArray<NetAddr> & aAddressArray)
@@ -338,18 +336,16 @@ nsDNSAsyncRequest::OnLookupComplete(nsHo
 {
     // need to have an owning ref when we issue the callback to enable
     // the caller to be able to addref/release multiple times without
     // destroying the record prematurely.
     nsCOMPtr<nsIDNSRecord> rec;
     if (NS_SUCCEEDED(status)) {
         NS_ASSERTION(hostRecord, "no host record");
         rec = new nsDNSRecord(hostRecord);
-        if (!rec)
-            status = NS_ERROR_OUT_OF_MEMORY;
     }
 
     mListener->OnLookupComplete(this, rec, status);
     mListener = nullptr;
 
     // release the reference to ourselves that was added before we were
     // handed off to the host resolver.
     NS_RELEASE_THIS();
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -65,16 +65,17 @@ static const unsigned int NEGATIVE_RECOR
 #define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
 
 PR_STATIC_ASSERT (HighThreadThreshold <= MAX_RESOLVER_THREADS);
 
 //----------------------------------------------------------------------------
 
 static LazyLogModule gHostResolverLog("nsHostResolver");
 #define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gHostResolverLog, mozilla::LogLevel::Debug)
 
 #define LOG_HOST(host, interface) host,                                        \
                  (interface && interface[0] != '\0') ? " on interface " : "",  \
                  (interface && interface[0] != '\0') ? interface : ""
 
 //----------------------------------------------------------------------------
 
 static inline void
@@ -1207,47 +1208,104 @@ nsHostResolver::PrepareRecordExpiration(
     }
 #endif
 
     rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
     LOG(("Caching host [%s%s%s] record for %u seconds (grace %d).",
          LOG_HOST(rec->host, rec->netInterface), lifetime, grace));
 }
 
+static bool
+different_rrset(AddrInfo *rrset1, AddrInfo *rrset2)
+{
+    if (!rrset1 || !rrset2) {
+        return true;
+    }
+
+    LOG(("different_rrset %s\n", rrset1->mHostName));
+    nsTArray<NetAddr> orderedSet1;
+    nsTArray<NetAddr> orderedSet2;
+
+    for (NetAddrElement *element = rrset1->mAddresses.getFirst();
+         element; element = element->getNext()) {
+        if (LOG_ENABLED()) {
+            char buf[128];
+            NetAddrToString(&element->mAddress, buf, 128);
+            LOG(("different_rrset add to set 1 %s\n", buf));
+        }
+        orderedSet1.InsertElementAt(orderedSet1.Length(), element->mAddress);
+    }
+
+    for (NetAddrElement *element = rrset2->mAddresses.getFirst();
+         element; element = element->getNext()) {
+        if (LOG_ENABLED()) {
+            char buf[128];
+            NetAddrToString(&element->mAddress, buf, 128);
+            LOG(("different_rrset add to set 2 %s\n", buf));
+        }
+        orderedSet2.InsertElementAt(orderedSet2.Length(), element->mAddress);
+    }
+
+    if (orderedSet1.Length() != orderedSet2.Length()) {
+        LOG(("different_rrset true due to length change\n"));
+        return true;
+    }
+    orderedSet1.Sort();
+    orderedSet2.Sort();
+
+    for (uint32_t i = 0; i < orderedSet1.Length(); ++i) {
+        if (!(orderedSet1[i] == orderedSet2[i])) {
+            LOG(("different_rrset true due to content change\n"));
+            return true;
+        }
+    }
+    LOG(("different_rrset false\n"));
+    return false;
+}
+
 //
 // OnLookupComplete() checks if the resolving should be redone and if so it
 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
-//
-
+// takes ownership of AddrInfo parameter
 nsHostResolver::LookupStatus
-nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* result)
+nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* newRRSet)
 {
     // get the list of pending callbacks for this lookup, and notify
     // them that the lookup is complete.
     PRCList cbs;
     PR_INIT_CLIST(&cbs);
     {
         MutexAutoLock lock(mLock);
 
         if (rec->mResolveAgain && (status != NS_ERROR_ABORT)) {
+            LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
             rec->mResolveAgain = false;
+            delete newRRSet;
             return LOOKUP_RESOLVEAGAIN;
         }
 
         // grab list of callbacks to notify
         MoveCList(rec->callbacks, cbs);
 
         // update record fields.  We might have a rec->addr_info already if a
         // previous lookup result expired and we're reresolving it..
         AddrInfo  *old_addr_info;
         {
             MutexAutoLock lock(rec->addr_info_lock);
-            old_addr_info = rec->addr_info;
-            rec->addr_info = result;
-            rec->addr_info_gencnt++;
+            if (different_rrset(rec->addr_info, newRRSet)) {
+                LOG(("nsHostResolver record %p new gencnt\n", rec));
+                old_addr_info = rec->addr_info;
+                rec->addr_info = newRRSet;
+                rec->addr_info_gencnt++;
+            } else {
+                if (rec->addr_info && newRRSet) {
+                    rec->addr_info->ttl = newRRSet->ttl;
+                }
+                old_addr_info = newRRSet;
+            }
         }
         delete old_addr_info;
 
         rec->negative = !rec->addr_info;
         PrepareRecordExpiration(rec);
         rec->resolving = false;
         
         if (rec->usingAnyThread) {