Merge "DO NOT MERGE - Increase resovler apex minSdkVersion to 29"
diff --git a/DnsTlsQueryMap.cpp b/DnsTlsQueryMap.cpp
index 79060a4..c4ae450 100644
--- a/DnsTlsQueryMap.cpp
+++ b/DnsTlsQueryMap.cpp
@@ -37,10 +37,12 @@
         LOG(WARNING) << "All query IDs are in use";
         return nullptr;
     }
-    Query q = { .newId = static_cast<uint16_t>(newId), .query = query };
-    std::map<uint16_t, QueryPromise>::iterator it;
-    bool inserted;
-    std::tie(it, inserted) = mQueries.emplace(newId, q);
+
+    // Make a copy of the query.
+    std::vector<uint8_t> tmp(query.base(), query.base() + query.size());
+    Query q = {.newId = static_cast<uint16_t>(newId), .query = std::move(tmp)};
+
+    const auto [it, inserted] = mQueries.try_emplace(newId, q);
     if (!inserted) {
         LOG(ERROR) << "Failed to store pending query";
         return nullptr;
@@ -137,7 +139,7 @@
     }
     Result r = { .code = Response::success, .response = std::move(response) };
     // Rewrite ID to match the query
-    const uint8_t* data = it->second.query.query.base();
+    const uint8_t* data = it->second.query.query.data();
     r.response[0] = data[0];
     r.response[1] = data[1];
     LOG(DEBUG) << "Sending result to dispatcher";
diff --git a/DnsTlsQueryMap.h b/DnsTlsQueryMap.h
index a6227f4..f744234 100644
--- a/DnsTlsQueryMap.h
+++ b/DnsTlsQueryMap.h
@@ -34,15 +34,19 @@
 // All methods are thread-safe and non-blocking.
 class DnsTlsQueryMap {
   public:
+    enum class Response : uint8_t { success, network_error, limit_error, internal_error };
+
     struct Query {
         // The new ID number assigned to this query.
         uint16_t newId;
         // A query that has been passed to recordQuery(), with its original ID number.
-        const netdutils::Slice query;
+        const std::vector<uint8_t> query;
     };
 
-    typedef DnsTlsServer::Response Response;
-    typedef DnsTlsServer::Result Result;
+    struct Result {
+        Response code;
+        std::vector<uint8_t> response;
+    };
 
     struct QueryFuture {
         QueryFuture(Query query, std::future<Result> result)
diff --git a/DnsTlsServer.h b/DnsTlsServer.h
index 82d1a45..8484b98 100644
--- a/DnsTlsServer.h
+++ b/DnsTlsServer.h
@@ -37,13 +37,6 @@
     // Allow sockaddr_storage to be promoted to DnsTlsServer automatically.
     DnsTlsServer(const sockaddr_storage& ss) : ss(ss) {}
 
-    enum class Response : uint8_t { success, network_error, limit_error, internal_error };
-
-    struct Result {
-        Response code;
-        std::vector<uint8_t> response;
-    };
-
     // The server location, including IP and port.
     sockaddr_storage ss = {};
 
diff --git a/DnsTlsTransport.cpp b/DnsTlsTransport.cpp
index 9766b65..54a7f89 100644
--- a/DnsTlsTransport.cpp
+++ b/DnsTlsTransport.cpp
@@ -56,9 +56,9 @@
     return mConnectCounter;
 }
 
-bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query q) {
+bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query& q) {
     // Strip off the ID number and send the new ID instead.
-    bool sent = mSocket->query(q.newId, netdutils::drop(q.query, 2));
+    const bool sent = mSocket->query(q.newId, netdutils::drop(netdutils::makeSlice(q.query), 2));
     if (sent) {
         mQueries.markTried(q.newId);
     }
diff --git a/DnsTlsTransport.h b/DnsTlsTransport.h
index 5f959da..c0fbaef 100644
--- a/DnsTlsTransport.h
+++ b/DnsTlsTransport.h
@@ -46,8 +46,8 @@
         : mMark(mark), mServer(server), mFactory(factory) {}
     ~DnsTlsTransport();
 
-    typedef DnsTlsServer::Response Response;
-    typedef DnsTlsServer::Result Result;
+    using Response = DnsTlsQueryMap::Response;
+    using Result = DnsTlsQueryMap::Result;
 
     // Given a |query|, this method sends it to the server and returns the result asynchronously.
     std::future<Result> query(const netdutils::Slice query) EXCLUDES(mLock);
@@ -86,7 +86,7 @@
     std::unique_ptr<IDnsTlsSocket> mSocket GUARDED_BY(mLock);
 
     // Send a query to the socket.
-    bool sendQuery(const DnsTlsQueryMap::Query q) REQUIRES(mLock);
+    bool sendQuery(const DnsTlsQueryMap::Query& q) REQUIRES(mLock);
 
     // The number of times an attempt to connect the nameserver.
     int mConnectCounter GUARDED_BY(mLock) = 0;
diff --git a/res_cache.cpp b/res_cache.cpp
index 5f87cca..4c7e187 100644
--- a/res_cache.cpp
+++ b/res_cache.cpp
@@ -1261,7 +1261,7 @@
         LOG(INFO) << __func__ << ": NOT IN CACHE (STALE ENTRY " << *lookup << "DISCARDED)";
         res_pquery(e->query, e->querylen);
         _cache_remove_p(cache, lookup);
-        return RESOLV_CACHE_NOTFOUND;
+        return (flags & ANDROID_RESOLV_NO_CACHE_STORE) ? RESOLV_CACHE_SKIP : RESOLV_CACHE_NOTFOUND;
     }
 
     *answerlen = e->answerlen;
diff --git a/res_init.cpp b/res_init.cpp
index 16fd98c..049e225 100644
--- a/res_init.cpp
+++ b/res_init.cpp
@@ -94,23 +94,11 @@
 
 void res_init(ResState* statp, const struct android_net_context* _Nonnull netcontext,
               android::net::NetworkDnsEventReported* _Nonnull event) {
-    memset(statp, 0, sizeof *statp);
-
     statp->netid = netcontext->dns_netid;
     statp->uid = netcontext->uid;
     statp->pid = netcontext->pid;
+    statp->nscount = 1;
     statp->id = arc4random_uniform(65536);
-    statp->_mark = netcontext->dns_mark;
-    statp->netcontext_flags = netcontext->flags;
-    statp->event = event;
-
-    statp->ndots = 1;
-    statp->_vcsock = -1;
-
-    for (int ns = 0; ns < MAXNS; ns++) {
-        statp->nssocks[ns] = -1;
-    }
-
     // The following dummy initialization is probably useless because
     // it's overwritten later by resolv_populate_res_for_net().
     // TODO: check if it's safe to remove.
@@ -120,28 +108,13 @@
             .sin.sin_port = htons(NAMESERVER_PORT),
     };
     memcpy(&statp->nsaddrs, &u, sizeof(u));
-    statp->nscount = 1;
-}
 
-/*
- * This routine is for closing the socket if a virtual circuit is used and
- * the program wants to close it.  This provides support for endhostent()
- * which expects to close the socket.
- *
- * This routine is not expected to be user visible.
- */
-void res_nclose(res_state statp) {
-    int ns;
-
-    if (statp->_vcsock >= 0) {
-        (void) close(statp->_vcsock);
-        statp->_vcsock = -1;
-        statp->_flags &= ~RES_F_VC;
+    for (auto& sock : statp->nssocks) {
+        sock.reset();
     }
-    for (ns = 0; ns < MAXNS; ns++) {
-        if (statp->nssocks[ns] != -1) {
-            close(statp->nssocks[ns]);
-            statp->nssocks[ns] = -1;
-        }
-    }
+    statp->ndots = 1;
+    statp->_mark = netcontext->dns_mark;
+    statp->tcp_nssock.reset();
+    statp->event = event;
+    statp->netcontext_flags = netcontext->flags;
 }
diff --git a/res_send.cpp b/res_send.cpp
index 66548bd..332bb78 100644
--- a/res_send.cpp
+++ b/res_send.cpp
@@ -555,7 +555,7 @@
             }
             if (resplen < 0) {
                 _resolv_cache_query_failed(statp->netid, buf, buflen, flags);
-                res_nclose(statp);
+                statp->closeSockets();
                 return -terrno;
             };
 
@@ -565,11 +565,11 @@
             if (cache_status == RESOLV_CACHE_NOTFOUND) {
                 resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
             }
-            res_nclose(statp);
+            statp->closeSockets();
             return (resplen);
         }  // for each ns
     }  // for each retry
-    res_nclose(statp);
+    statp->closeSockets();
     terrno = useTcp ? terrno : gotsomewhere ? ETIMEDOUT : ECONNREFUSED;
     // TODO: Remove errno once callers stop using it
     errno = useTcp ? terrno
@@ -631,25 +631,24 @@
     struct timespec now = evNowTime();
 
     /* Are we still talking to whom we want to talk to? */
-    if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
+    if (statp->tcp_nssock >= 0 && (statp->_flags & RES_F_VC) != 0) {
         struct sockaddr_storage peer;
         socklen_t size = sizeof peer;
         unsigned old_mark;
         socklen_t mark_size = sizeof(old_mark);
-        if (getpeername(statp->_vcsock, (struct sockaddr*) (void*) &peer, &size) < 0 ||
-            !sock_eq((struct sockaddr*) (void*) &peer, nsap) ||
-            getsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &old_mark, &mark_size) < 0 ||
+        if (getpeername(statp->tcp_nssock, (struct sockaddr*)(void*)&peer, &size) < 0 ||
+            !sock_eq((struct sockaddr*)(void*)&peer, nsap) ||
+            getsockopt(statp->tcp_nssock, SOL_SOCKET, SO_MARK, &old_mark, &mark_size) < 0 ||
             old_mark != statp->_mark) {
-            res_nclose(statp);
-            statp->_flags &= ~RES_F_VC;
+            statp->closeSockets();
         }
     }
 
-    if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
-        if (statp->_vcsock >= 0) res_nclose(statp);
+    if (statp->tcp_nssock < 0 || (statp->_flags & RES_F_VC) == 0) {
+        if (statp->tcp_nssock >= 0) statp->closeSockets();
 
-        statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
-        if (statp->_vcsock < 0) {
+        statp->tcp_nssock.reset(socket(nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0));
+        if (statp->tcp_nssock < 0) {
             switch (errno) {
                 case EPROTONOSUPPORT:
                 case EPFNOSUPPORT:
@@ -662,9 +661,9 @@
                     return -1;
             }
         }
-        resolv_tag_socket(statp->_vcsock, statp->uid, statp->pid);
+        resolv_tag_socket(statp->tcp_nssock, statp->uid, statp->pid);
         if (statp->_mark != MARK_UNSET) {
-            if (setsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &statp->_mark,
+            if (setsockopt(statp->tcp_nssock, SOL_SOCKET, SO_MARK, &statp->_mark,
                            sizeof(statp->_mark)) < 0) {
                 *terrno = errno;
                 PLOG(DEBUG) << __func__ << ": setsockopt: ";
@@ -672,17 +671,17 @@
             }
         }
         errno = 0;
-        if (random_bind(statp->_vcsock, nsap->sa_family) < 0) {
+        if (random_bind(statp->tcp_nssock, nsap->sa_family) < 0) {
             *terrno = errno;
             dump_error("bind/vc", nsap, nsaplen);
-            res_nclose(statp);
+            statp->closeSockets();
             return (0);
         }
-        if (connect_with_timeout(statp->_vcsock, nsap, (socklen_t) nsaplen,
+        if (connect_with_timeout(statp->tcp_nssock, nsap, (socklen_t)nsaplen,
                                  get_timeout(statp, params, ns)) < 0) {
             *terrno = errno;
             dump_error("connect/vc", nsap, nsaplen);
-            res_nclose(statp);
+            statp->closeSockets();
             /*
              * The way connect_with_timeout() is implemented prevents us from reliably
              * determining whether this was really a timeout or e.g. ECONNREFUSED. Since
@@ -705,10 +704,10 @@
             {.iov_base = &len, .iov_len = INT16SZ},
             {.iov_base = const_cast<uint8_t*>(buf), .iov_len = static_cast<size_t>(buflen)},
     };
-    if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
+    if (writev(statp->tcp_nssock, iov, 2) != (INT16SZ + buflen)) {
         *terrno = errno;
         PLOG(DEBUG) << __func__ << ": write failed: ";
-        res_nclose(statp);
+        statp->closeSockets();
         return (0);
     }
     /*
@@ -717,14 +716,14 @@
 read_len:
     cp = ans;
     len = INT16SZ;
-    while ((n = read(statp->_vcsock, (char*) cp, (size_t) len)) > 0) {
+    while ((n = read(statp->tcp_nssock, (char*)cp, (size_t)len)) > 0) {
         cp += n;
         if ((len -= n) == 0) break;
     }
     if (n <= 0) {
         *terrno = errno;
         PLOG(DEBUG) << __func__ << ": read failed: ";
-        res_nclose(statp);
+        statp->closeSockets();
         /*
          * A long running process might get its TCP
          * connection reset if the remote server was
@@ -736,10 +735,8 @@
          */
         if (*terrno == ECONNRESET && !connreset) {
             connreset = 1;
-            res_nclose(statp);
             goto same_ns;
         }
-        res_nclose(statp);
         return (0);
     }
     uint16_t resplen = ntohs(*reinterpret_cast<const uint16_t*>(ans));
@@ -755,18 +752,18 @@
          */
         LOG(DEBUG) << __func__ << ": undersized: " << len;
         *terrno = EMSGSIZE;
-        res_nclose(statp);
+        statp->closeSockets();
         return (0);
     }
     cp = ans;
-    while (len != 0 && (n = read(statp->_vcsock, (char*) cp, (size_t) len)) > 0) {
+    while (len != 0 && (n = read(statp->tcp_nssock, (char*)cp, (size_t)len)) > 0) {
         cp += n;
         len -= n;
     }
     if (n <= 0) {
         *terrno = errno;
         PLOG(DEBUG) << __func__ << ": read(vc): ";
-        res_nclose(statp);
+        statp->closeSockets();
         return (0);
     }
 
@@ -779,7 +776,7 @@
         while (len != 0) {
             char junk[PACKETSZ];
 
-            n = read(statp->_vcsock, junk, (len > sizeof junk) ? sizeof junk : len);
+            n = read(statp->tcp_nssock, junk, (len > sizeof junk) ? sizeof junk : len);
             if (n > 0)
                 len -= n;
             else
@@ -875,24 +872,39 @@
     return n;
 }
 
+bool ignoreInvalidAnswer(res_state statp, const sockaddr_storage& from, const uint8_t* buf,
+                         int buflen, uint8_t* ans, int anssiz) {
+    const HEADER* hp = (const HEADER*)(const void*)buf;
+    HEADER* anhp = (HEADER*)(void*)ans;
+    if (hp->id != anhp->id) {
+        // response from old query, ignore it.
+        LOG(DEBUG) << __func__ << ": old answer:";
+        return true;
+    }
+    if (!res_ourserver_p(statp, (sockaddr*)(void*)&from)) {
+        // response from wrong server? ignore it.
+        LOG(DEBUG) << __func__ << ": not our server:";
+        return true;
+    }
+    if (!res_queriesmatch(buf, buf + buflen, ans, ans + anssiz)) {
+        // response contains wrong query? ignore it.
+        LOG(DEBUG) << __func__ << ": wrong query name:";
+        return true;
+    }
+    return false;
+}
+
 static int send_dg(res_state statp, res_params* params, const uint8_t* buf, int buflen,
                    uint8_t* ans, int anssiz, int* terrno, int ns, int* v_circuit, int* gotsomewhere,
                    time_t* at, int* rcode, int* delay) {
-    *at = time(NULL);
+    *at = time(nullptr);
     *delay = 0;
-    const HEADER* hp = (const HEADER*) (const void*) buf;
-    HEADER* anhp = (HEADER*) (void*) ans;
-    const struct sockaddr* nsap;
-    int nsaplen;
-    struct timespec now, timeout, finish, done;
-    struct sockaddr_storage from;
-    socklen_t fromlen;
-    int resplen, n, s;
 
-    nsap = get_nsaddr(statp, (size_t) ns);
-    nsaplen = sockaddrSize(nsap);
+    const sockaddr* nsap = get_nsaddr(statp, (size_t)ns);
+    const int nsaplen = sockaddrSize(nsap);
+
     if (statp->nssocks[ns] == -1) {
-        statp->nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+        statp->nssocks[ns].reset(socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0));
         if (statp->nssocks[ns] < 0) {
             switch (errno) {
                 case EPROTONOSUPPORT:
@@ -911,7 +923,7 @@
         if (statp->_mark != MARK_UNSET) {
             if (setsockopt(statp->nssocks[ns], SOL_SOCKET, SO_MARK, &(statp->_mark),
                            sizeof(statp->_mark)) < 0) {
-                res_nclose(statp);
+                statp->closeSockets();
                 return -1;
             }
         }
@@ -921,130 +933,101 @@
         // a nameserver without timing out.
         if (random_bind(statp->nssocks[ns], nsap->sa_family) < 0) {
             dump_error("bind(dg)", nsap, nsaplen);
-            res_nclose(statp);
+            statp->closeSockets();
             return (0);
         }
         if (connect(statp->nssocks[ns], nsap, (socklen_t)nsaplen) < 0) {
             dump_error("connect(dg)", nsap, nsaplen);
-            res_nclose(statp);
+            statp->closeSockets();
             return (0);
         }
         LOG(DEBUG) << __func__ << ": new DG socket";
     }
-    s = statp->nssocks[ns];
-    if (send(s, (const char*) buf, (size_t) buflen, 0) != buflen) {
+    if (send(statp->nssocks[ns], (const char*)buf, (size_t)buflen, 0) != buflen) {
         PLOG(DEBUG) << __func__ << ": send: ";
-        res_nclose(statp);
+        statp->closeSockets();
         return 0;
     }
 
-    // Wait for reply.
-    timeout = get_timeout(statp, params, ns);
-    now = evNowTime();
-    finish = evAddTime(now, timeout);
-retry:
-    n = retrying_poll(s, POLLIN, &finish);
+    timespec timeout = get_timeout(statp, params, ns);
+    timespec now = evNowTime();
+    timespec finish = evAddTime(now, timeout);
+    for (;;) {
+        // Wait for reply.
+        int n = retrying_poll(statp->nssocks[ns], POLLIN, &finish);
+        if (n == 0) {
+            *rcode = RCODE_TIMEOUT;
+            LOG(DEBUG) << __func__ << ": timeout";
+            *gotsomewhere = 1;
+            return 0;
+        }
+        if (n < 0) {
+            PLOG(DEBUG) << __func__ << ": poll: ";
+            statp->closeSockets();
+            return 0;
+        }
 
-    if (n == 0) {
-        *rcode = RCODE_TIMEOUT;
-        LOG(DEBUG) << __func__ << ": timeout";
+        errno = 0;
+        sockaddr_storage from;
+        socklen_t fromlen = sizeof(from);
+        int resplen = recvfrom(statp->nssocks[ns], (char*)ans, (size_t)anssiz, 0,
+                               (sockaddr*)(void*)&from, &fromlen);
+        if (resplen <= 0) {
+            PLOG(DEBUG) << __func__ << ": recvfrom: ";
+            statp->closeSockets();
+            return 0;
+        }
         *gotsomewhere = 1;
-        return 0;
+        if (resplen < HFIXEDSZ) {
+            // Undersized message.
+            LOG(DEBUG) << __func__ << ": undersized: " << resplen;
+            *terrno = EMSGSIZE;
+            statp->closeSockets();
+            return 0;
+        }
+
+        if (ignoreInvalidAnswer(statp, from, buf, buflen, ans, anssiz)) {
+            res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+            continue;
+        }
+
+        HEADER* anhp = (HEADER*)(void*)ans;
+        if (anhp->rcode == FORMERR && (statp->netcontext_flags & NET_CONTEXT_FLAG_USE_EDNS)) {
+            //  Do not retry if the server do not understand EDNS0.
+            //  The case has to be captured here, as FORMERR packet do not
+            //  carry query section, hence res_queriesmatch() returns 0.
+            LOG(DEBUG) << __func__ << ": server rejected query with EDNS0:";
+            res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+            // record the error
+            statp->_flags |= RES_F_EDNS0ERR;
+            statp->closeSockets();
+            return 0;
+        }
+
+        timespec done = evNowTime();
+        *delay = _res_stats_calculate_rtt(&done, &now);
+        if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || anhp->rcode == REFUSED) {
+            LOG(DEBUG) << __func__ << ": server rejected query:";
+            res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
+            statp->closeSockets();
+            *rcode = anhp->rcode;
+            return 0;
+        }
+        if (anhp->tc) {
+            // To get the rest of answer,
+            // use TCP with same server.
+            LOG(DEBUG) << __func__ << ": truncated answer";
+            *v_circuit = 1;
+            statp->closeSockets();
+            return 1;
+        }
+        // All is well, or the error is fatal. Signal that the
+        // next nameserver ought not be tried.
+        if (resplen > 0) {
+            *rcode = anhp->rcode;
+        }
+        return resplen;
     }
-    if (n < 0) {
-        PLOG(DEBUG) << __func__ << ": poll: ";
-        res_nclose(statp);
-        return 0;
-    }
-    errno = 0;
-    fromlen = sizeof(from);
-    resplen = recvfrom(s, (char*) ans, (size_t) anssiz, 0, (struct sockaddr*) (void*) &from,
-                       &fromlen);
-    if (resplen <= 0) {
-        PLOG(DEBUG) << __func__ << ": recvfrom: ";
-        res_nclose(statp);
-        return 0;
-    }
-    *gotsomewhere = 1;
-    if (resplen < HFIXEDSZ) {
-        /*
-         * Undersized message.
-         */
-        LOG(DEBUG) << __func__ << ": undersized: " << resplen;
-        *terrno = EMSGSIZE;
-        res_nclose(statp);
-        return 0;
-    }
-    if (hp->id != anhp->id) {
-        /*
-         * response from old query, ignore it.
-         * XXX - potential security hazard could
-         *	 be detected here.
-         */
-        LOG(DEBUG) << __func__ << ": old answer:";
-        res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
-        goto retry;
-    }
-    if (!res_ourserver_p(statp, (struct sockaddr*)(void*)&from)) {
-        /*
-         * response from wrong server? ignore it.
-         * XXX - potential security hazard could
-         *	 be detected here.
-         */
-        LOG(DEBUG) << __func__ << ": not our server:";
-        res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
-        goto retry;
-    }
-    if (anhp->rcode == FORMERR && (statp->netcontext_flags & NET_CONTEXT_FLAG_USE_EDNS)) {
-        /*
-         * Do not retry if the server do not understand EDNS0.
-         * The case has to be captured here, as FORMERR packet do not
-         * carry query section, hence res_queriesmatch() returns 0.
-         */
-        LOG(DEBUG) << __func__ << ": server rejected query with EDNS0:";
-        res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
-        /* record the error */
-        statp->_flags |= RES_F_EDNS0ERR;
-        res_nclose(statp);
-        return 0;
-    }
-    if (!res_queriesmatch(buf, buf + buflen, ans, ans + anssiz)) {
-        /*
-         * response contains wrong query? ignore it.
-         * XXX - potential security hazard could
-         *	 be detected here.
-         */
-        LOG(DEBUG) << __func__ << ": wrong query name:";
-        res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
-        goto retry;
-    }
-    done = evNowTime();
-    *delay = _res_stats_calculate_rtt(&done, &now);
-    if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || anhp->rcode == REFUSED) {
-        LOG(DEBUG) << __func__ << ": server rejected query:";
-        res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
-        res_nclose(statp);
-        *rcode = anhp->rcode;
-        return 0;
-    }
-    if (anhp->tc) {
-        /*
-         * To get the rest of answer,
-         * use TCP with same server.
-         */
-        LOG(DEBUG) << __func__ << ": truncated answer";
-        *v_circuit = 1;
-        res_nclose(statp);
-        return 1;
-    }
-    /*
-     * All is well, or the error is fatal.  Signal that the
-     * next nameserver ought not be tried.
-     */
-    if (resplen > 0) {
-        *rcode = anhp->rcode;
-    }
-    return resplen;
 }
 
 static void dump_error(const char* str, const struct sockaddr* address, int alen) {
diff --git a/resolv_private.h b/resolv_private.h
index a062c1a..e217087 100644
--- a/resolv_private.h
+++ b/resolv_private.h
@@ -49,6 +49,7 @@
 #pragma once
 
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <net/if.h>
 #include <time.h>
 #include <string>
@@ -73,6 +74,10 @@
 #define RES_TIMEOUT 5000 /* min. milliseconds between retries */
 #define RES_DFLRETRY 2    /* Default #/tries. */
 
+// Flags for res_state->_flags
+#define RES_F_VC 0x00000001        // socket is TCP
+#define RES_F_EDNS0ERR 0x00000004  // EDNS0 caused errors
+
 // Holds either a sockaddr_in or a sockaddr_in6.
 union sockaddr_union {
     struct sockaddr sa;
@@ -82,21 +87,31 @@
 constexpr int MAXPACKET = 8 * 1024;
 
 struct ResState {
-    unsigned netid;                           // NetId: cache key and socket mark
-    uid_t uid;                                // uid of the app that sent the DNS lookup
-    pid_t pid;                                // pid of the app that sent the DNS lookup
-    int nscount;                              // number of name srvers
-    uint16_t id;                              // current message id
-    std::vector<std::string> search_domains;  // domains to search
+    void closeSockets() {
+        tcp_nssock.reset();
+        _flags &= ~RES_F_VC;
+
+        for (auto& sock : nssocks) {
+            sock.reset();
+        }
+    }
+    // clang-format off
+    unsigned netid;                             // NetId: cache key and socket mark
+    uid_t uid;                                  // uid of the app that sent the DNS lookup
+    pid_t pid;                                  // pid of the app that sent the DNS lookup
+    int nscount;                                // number of name srvers
+    uint16_t id;                                // current message id
+    std::vector<std::string> search_domains{};  // domains to search
     sockaddr_union nsaddrs[MAXNS];
-    int nssocks[MAXNS];                       // UDP sockets to nameservers
-    unsigned ndots : 4;                       // threshold for initial abs. query
-    unsigned _mark;                           // If non-0 SET_MARK to _mark on all request sockets
-    int _vcsock;                              // TCP socket (but why not one per nameserver?)
-    uint32_t _flags;                          // See RES_F_* defines below
+    android::base::unique_fd nssocks[MAXNS];    // UDP sockets to nameservers
+    unsigned ndots : 4;                         // threshold for initial abs. query
+    unsigned _mark;                             // If non-0 SET_MARK to _mark on all request sockets
+    android::base::unique_fd tcp_nssock;        // TCP socket (but why not one per nameserver?)
+    uint32_t _flags = 0;                        // See RES_F_* defines below
     android::net::NetworkDnsEventReported* event;
     uint32_t netcontext_flags;
-    int tc_mode;
+    int tc_mode = 0;
+    // clang-format on
 };
 
 // TODO: remove these legacy aliases
@@ -121,10 +136,6 @@
 
 /* End of stats related definitions */
 
-// Flags for res_state->_flags
-#define RES_F_VC 0x00000001        // socket is TCP
-#define RES_F_EDNS0ERR 0x00000004  // EDNS0 caused errors
-
 /*
  * Error code extending h_errno codes defined in bionic/libc/include/netdb.h.
  *
@@ -146,7 +157,6 @@
 int res_nmkquery(int op, const char* qname, int cl, int type, const uint8_t* data, int datalen,
                  uint8_t* buf, int buflen, int netcontext_flags);
 int res_nsend(res_state, const uint8_t*, int, uint8_t*, int, int*, uint32_t);
-void res_nclose(res_state);
 int res_nopt(res_state, int, uint8_t*, int, int);
 
 int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hints,
diff --git a/resolv_tls_unit_test.cpp b/resolv_tls_unit_test.cpp
index ed9c218..5a03db7 100644
--- a/resolv_tls_unit_test.cpp
+++ b/resolv_tls_unit_test.cpp
@@ -180,9 +180,9 @@
     DnsTlsTransport transport(SERVER1, MARK, &factory);
     for (int i = 0; i < 100; ++i) {
         // Send a query.
-        std::future<DnsTlsServer::Result> f = transport.query(makeSlice(QUERY));
+        std::future<DnsTlsTransport::Result> f = transport.query(makeSlice(QUERY));
         // Wait for the response.
-        DnsTlsServer::Result r = f.get();
+        DnsTlsTransport::Result r = f.get();
         EXPECT_EQ(DnsTlsTransport::Response::success, r.code);
 
         // All queries should have an observed ID of zero, because it is returned to the ID pool
@@ -887,9 +887,9 @@
     EXPECT_EQ(1, all[1].newId);
     EXPECT_EQ(2, all[2].newId);
 
-    EXPECT_EQ(makeSlice(q0), all[0].query);
-    EXPECT_EQ(makeSlice(q1), all[1].query);
-    EXPECT_EQ(makeSlice(q2), all[2].query);
+    EXPECT_EQ(q0, all[0].query);
+    EXPECT_EQ(q1, all[1].query);
+    EXPECT_EQ(q2, all[2].query);
 
     bytevec a0 = make_query(0, SIZE);
     bytevec a1 = make_query(1, SIZE);
diff --git a/tests/dns_responder/dns_responder.cpp b/tests/dns_responder/dns_responder.cpp
index 62b783c..89e7b19 100644
--- a/tests/dns_responder/dns_responder.cpp
+++ b/tests/dns_responder/dns_responder.cpp
@@ -547,6 +547,10 @@
     edns_ = edns;
 }
 
+void DNSResponder::setTtl(unsigned ttl) {
+    answer_record_ttl_sec_ = ttl;
+}
+
 bool DNSResponder::running() const {
     return (udp_socket_.ok()) && (tcp_socket_.ok());
 }
@@ -783,7 +787,7 @@
                     .name = {.name = it->first.name},
                     .rtype = it->first.type,
                     .rclass = ns_class::ns_c_in,
-                    .ttl = kAnswerRecordTtlSec,  // seconds
+                    .ttl = answer_record_ttl_sec_,  // seconds
             };
             if (!fillRdata(it->second, record)) return false;
             answers->push_back(std::move(record));
diff --git a/tests/dns_responder/dns_responder.h b/tests/dns_responder/dns_responder.h
index 7382364..4153a7f 100644
--- a/tests/dns_responder/dns_responder.h
+++ b/tests/dns_responder/dns_responder.h
@@ -175,6 +175,7 @@
     void setResponseProbability(double response_probability);
     void setResponseProbability(double response_probability, int protocol);
     void setEdns(Edns edns);
+    void setTtl(unsigned ttl);
     bool running() const;
     bool startServer();
     bool stopServer();
@@ -293,6 +294,8 @@
     // returning error_rcode_ or no response.
     std::atomic<double> response_probability_udp_ = 1.0;
 
+    std::atomic<unsigned> answer_record_ttl_sec_ = kAnswerRecordTtlSec;
+
     // Maximum number of fds for epoll.
     const int EPOLL_MAX_EVENTS = 2;
 
diff --git a/tests/resolv_integration_test.cpp b/tests/resolv_integration_test.cpp
index 6ea88da..b78395c 100644
--- a/tests/resolv_integration_test.cpp
+++ b/tests/resolv_integration_test.cpp
@@ -2235,6 +2235,52 @@
     EXPECT_EQ(4U, GetNumQueries(dns, another_host_name));
 }
 
+TEST_F(ResolverTest, Async_NoCacheStoreFlagDoesNotRefreshStaleCacheEntry) {
+    constexpr char listen_addr[] = "127.0.0.4";
+    constexpr char host_name[] = "howdy.example.com.";
+    const std::vector<DnsRecord> records = {
+            {host_name, ns_type::ns_t_a, "1.2.3.4"},
+    };
+
+    test::DNSResponder dns(listen_addr);
+    StartDns(dns, records);
+    std::vector<std::string> servers = {listen_addr};
+    ASSERT_TRUE(mDnsClient.SetResolversForNetwork(servers));
+
+    const unsigned SHORT_TTL_SEC = 1;
+    dns.setTtl(SHORT_TTL_SEC);
+
+    // Refer to b/148842821 for the purpose of below test steps.
+    // Basically, this test is used to ensure stale cache case is handled
+    // correctly with ANDROID_RESOLV_NO_CACHE_STORE.
+    int fd = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+    EXPECT_TRUE(fd != -1);
+    expectAnswersValid(fd, AF_INET, "1.2.3.4");
+
+    EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+    dns.clearQueries();
+
+    // Wait until cache expired
+    sleep(SHORT_TTL_SEC + 0.5);
+
+    // Now request the same hostname again.
+    // We should see a new DNS query because the entry in cache has become stale.
+    // Due to ANDROID_RESOLV_NO_CACHE_STORE, this query must *not* refresh that stale entry.
+    fd = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a,
+                         ANDROID_RESOLV_NO_CACHE_STORE);
+    EXPECT_TRUE(fd != -1);
+    expectAnswersValid(fd, AF_INET, "1.2.3.4");
+    EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+    dns.clearQueries();
+
+    // If the cache is still stale, we expect to see one more DNS query
+    // (this time the cache will be refreshed, but we're not checking for it).
+    fd = resNetworkQuery(TEST_NETID, "howdy.example.com", ns_c_in, ns_t_a, 0);
+    EXPECT_TRUE(fd != -1);
+    expectAnswersValid(fd, AF_INET, "1.2.3.4");
+    EXPECT_EQ(1U, GetNumQueries(dns, host_name));
+}
+
 TEST_F(ResolverTest, Async_NoRetryFlag) {
     constexpr char listen_addr0[] = "127.0.0.4";
     constexpr char listen_addr1[] = "127.0.0.6";