Merge "Support DNS64 synthesis using externally-discovered prefixes."
diff --git a/Dns64Configuration.cpp b/Dns64Configuration.cpp
index f646d81..5b3849b 100644
--- a/Dns64Configuration.cpp
+++ b/Dns64Configuration.cpp
@@ -37,11 +37,13 @@
 
 namespace android {
 
+using android::base::StringPrintf;
 using android::net::NetworkDnsEventReported;
 using netdutils::DumpWriter;
 using netdutils::IPAddress;
 using netdutils::IPPrefix;
 using netdutils::ScopedAddrinfo;
+using netdutils::setThreadName;
 
 namespace net {
 
@@ -64,7 +66,7 @@
 
     // Note that capturing |cfg| in this lambda creates a copy.
     std::thread discovery_thread([this, cfg, netId] {
-        netdutils::setThreadName(android::base::StringPrintf("Nat64Pfx_%u", netId).c_str());
+        setThreadName(StringPrintf("Nat64Pfx_%u", netId).c_str());
 
         // Make a mutable copy rather than mark the whole lambda mutable.
         // No particular reason.
diff --git a/DnsTlsDispatcher.cpp b/DnsTlsDispatcher.cpp
index 8747e46..1fa4001 100644
--- a/DnsTlsDispatcher.cpp
+++ b/DnsTlsDispatcher.cpp
@@ -147,6 +147,10 @@
                                                   const Slice query, const Slice ans, int* resplen,
                                                   bool* connectTriggered) {
     int connectCounter;
+
+    // TODO: This can cause the resolver to create multiple connections to the same DoT server
+    // merely due to different mark, such as the bit explicitlySelected unset.
+    // See if we can save them and just create one connection for one DoT server.
     const Key key = std::make_pair(mark, server);
     Transport* xport;
     {
diff --git a/DnsTlsSocket.cpp b/DnsTlsSocket.cpp
index 9ce8ec2..cab678d 100644
--- a/DnsTlsSocket.cpp
+++ b/DnsTlsSocket.cpp
@@ -32,7 +32,6 @@
 #include "DnsTlsSessionCache.h"
 #include "IDnsTlsSocketObserver.h"
 
-#include <Fwmark.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <netdutils/SocketOption.h>
@@ -49,9 +48,11 @@
 
 namespace android {
 
+using base::StringPrintf;
 using netdutils::enableSockopt;
 using netdutils::enableTcpKeepAlives;
 using netdutils::isOk;
+using netdutils::setThreadName;
 using netdutils::Slice;
 using netdutils::Status;
 
@@ -70,13 +71,6 @@
     return TEMP_FAILURE_RETRY(poll(&fds, 1, timeoutMs));
 }
 
-std::string markToFwmarkString(unsigned mMark) {
-    Fwmark mark;
-    mark.intValue = mMark;
-    return android::base::StringPrintf("%d, %d, %d, %d, %d", mark.netId, mark.explicitlySelected,
-                                       mark.protectedFromVpn, mark.permission, mark.uidBillingDone);
-}
-
 }  // namespace
 
 Status DnsTlsSocket::tcpConnect() {
@@ -243,9 +237,9 @@
     }
 
     for (;;) {
-        LOG(DEBUG) << " Calling SSL_connect with " << markToFwmarkString(mMark);
+        LOG(DEBUG) << " Calling SSL_connect with mark 0x" << std::hex << mMark;
         int ret = SSL_connect(ssl.get());
-        LOG(DEBUG) << " SSL_connect returned " << ret << " with " << markToFwmarkString(mMark);
+        LOG(DEBUG) << " SSL_connect returned " << ret << " with mark 0x" << std::hex << mMark;
         if (ret == 1) break;  // SSL handshake complete;
 
         const int ssl_err = SSL_get_error(ssl.get(), ret);
@@ -255,8 +249,8 @@
                 // the TCP connection handshake, the device is waiting for the SSL handshake reply
                 // from the server.
                 if (int err = waitForReading(fd, mServer.connectTimeout.count()); err <= 0) {
-                    PLOG(WARNING) << "SSL_connect read error " << err << ", "
-                                  << markToFwmarkString(mMark);
+                    PLOG(WARNING) << "SSL_connect read error " << err << ", mark 0x" << std::hex
+                                  << mMark;
                     return nullptr;
                 }
                 break;
@@ -264,14 +258,14 @@
                 // If no application data is sent during the TCP connection handshake, the
                 // device is waiting for the connection established to perform SSL handshake.
                 if (int err = waitForWriting(fd, mServer.connectTimeout.count()); err <= 0) {
-                    PLOG(WARNING) << "SSL_connect write error " << err << ", "
-                                  << markToFwmarkString(mMark);
+                    PLOG(WARNING) << "SSL_connect write error " << err << ", mark 0x" << std::hex
+                                  << mMark;
                     return nullptr;
                 }
                 break;
             default:
-                PLOG(WARNING) << "SSL_connect ssl error =" << ssl_err << ", "
-                              << markToFwmarkString(mMark);
+                PLOG(WARNING) << "SSL_connect ssl error =" << ssl_err << ", mark 0x" << std::hex
+                              << mMark;
                 return nullptr;
         }
     }
@@ -321,9 +315,7 @@
     std::deque<std::vector<uint8_t>> q;
     const int timeout_msecs = DnsTlsSocket::kIdleTimeout.count() * 1000;
 
-    Fwmark mark;
-    mark.intValue = mMark;
-    netdutils::setThreadName(android::base::StringPrintf("TlsListen_%u", mark.netId).c_str());
+    setThreadName(StringPrintf("TlsListen_%u", mMark & 0xffff).c_str());
     while (true) {
         // poll() ignores negative fds
         struct pollfd fds[2] = { { .fd = -1 }, { .fd = -1 } };
diff --git a/DnsTlsTransport.cpp b/DnsTlsTransport.cpp
index 54a7f89..d0098c2 100644
--- a/DnsTlsTransport.cpp
+++ b/DnsTlsTransport.cpp
@@ -18,7 +18,6 @@
 
 #include "DnsTlsTransport.h"
 
-#include <Fwmark.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <arpa/inet.h>
@@ -28,6 +27,9 @@
 #include "DnsTlsSocketFactory.h"
 #include "IDnsTlsSocketFactory.h"
 
+using android::base::StringPrintf;
+using android::netdutils::setThreadName;
+
 namespace android {
 namespace net {
 
@@ -112,9 +114,7 @@
 
 void DnsTlsTransport::doReconnect() {
     std::lock_guard guard(mLock);
-    Fwmark mark;
-    mark.intValue = mMark;
-    netdutils::setThreadName(android::base::StringPrintf("TlsReconn_%u", mark.netId).c_str());
+    setThreadName(StringPrintf("TlsReconn_%u", mMark & 0xffff).c_str());
     if (mClosing) {
         return;
     }
diff --git a/PrivateDnsConfiguration.cpp b/PrivateDnsConfiguration.cpp
index 2263153..182a5ac 100644
--- a/PrivateDnsConfiguration.cpp
+++ b/PrivateDnsConfiguration.cpp
@@ -31,6 +31,8 @@
 #include "resolv_cache.h"
 #include "util.h"
 
+using android::base::StringPrintf;
+using android::netdutils::setThreadName;
 using std::chrono::milliseconds;
 
 namespace android {
@@ -180,7 +182,7 @@
 
     // Note that capturing |server| and |netId| in this lambda create copies.
     std::thread validate_thread([this, server, netId, mark] {
-        netdutils::setThreadName(android::base::StringPrintf("TlsVerify_%u", netId).c_str());
+        setThreadName(StringPrintf("TlsVerify_%u", netId).c_str());
 
         // cat /proc/sys/net/ipv4/tcp_syn_retries yields "6".
         //
diff --git a/ResolverController.cpp b/ResolverController.cpp
index 13600b8..be0b989 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -24,7 +24,6 @@
 
 #include <netdb.h>
 
-#include <Fwmark.h>
 #include <aidl/android/net/IDnsResolver.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
@@ -202,21 +201,22 @@
 int ResolverController::setResolverConfiguration(const ResolverParamsParcel& resolverParams) {
     using aidl::android::net::IDnsResolver;
 
-    // At private DNS validation time, we only know the netId, so we have to guess/compute the
-    // corresponding socket mark.
-    Fwmark fwmark;
-    fwmark.netId = resolverParams.netId;
-    fwmark.explicitlySelected = true;
-    fwmark.protectedFromVpn = true;
-    fwmark.permission = PERMISSION_SYSTEM;
+    // Expect to get the mark with system permission.
+    android_net_context netcontext;
+    gResNetdCallbacks.get_network_context(resolverParams.netId, 0 /* uid */, &netcontext);
 
     // Allow at most MAXNS private DNS servers in a network to prevent too many broken servers.
     std::vector<std::string> tlsServers = resolverParams.tlsServers;
     if (tlsServers.size() > MAXNS) {
         tlsServers.resize(MAXNS);
     }
+
+    // Use app_mark for DoT connection. Using dns_mark might result in reaching the DoT servers
+    // through a different network. For example, on a VPN with no DNS servers (Do53), if the VPN
+    // applies to UID 0, dns_mark is assigned for default network rathan the VPN. (note that it's
+    // possible that a VPN doesn't have any DNS servers but DoT servers in DNS strict mode)
     const int err =
-            gPrivateDnsConfiguration.set(resolverParams.netId, fwmark.intValue, tlsServers,
+            gPrivateDnsConfiguration.set(resolverParams.netId, netcontext.app_mark, tlsServers,
                                          resolverParams.tlsName, resolverParams.caCertificate);
 
     if (err != 0) {
diff --git a/res_send.cpp b/res_send.cpp
index a10bbd1..2b84e82 100644
--- a/res_send.cpp
+++ b/res_send.cpp
@@ -130,6 +130,7 @@
 using android::net::IV_IPV4;
 using android::net::IV_IPV6;
 using android::net::IV_UNKNOWN;
+using android::net::LinuxErrno;
 using android::net::NetworkDnsEventReported;
 using android::net::NS_T_INVALID;
 using android::net::NsRcode;
@@ -499,7 +500,8 @@
     int retryTimes = (flags & ANDROID_RESOLV_NO_RETRY) ? 1 : params.retry_count;
     int useTcp = buflen > PACKETSZ;
     int gotsomewhere = 0;
-    int terrno = ETIMEDOUT;
+    // Use an impossible error code as default value
+    int terrno = ETIME;
 
     for (int attempt = 0; attempt < retryTimes; ++attempt) {
         for (size_t ns = 0; ns < statp->nsaddrs.size(); ++ns) {
@@ -521,6 +523,8 @@
             Stopwatch queryStopwatch;
             int retry_count_for_event = 0;
             size_t actualNs = ns;
+            // Use an impossible error code as default value
+            terrno = ETIME;
             if (useTcp) {
                 // TCP; at most one attempt per server.
                 attempt = retryTimes;
@@ -558,6 +562,7 @@
             dnsQueryEvent->set_rcode(static_cast<NsRcode>(*rcode));
             dnsQueryEvent->set_protocol(query_proto);
             dnsQueryEvent->set_type(getQueryType(buf, buflen));
+            dnsQueryEvent->set_linux_errno(static_cast<LinuxErrno>(terrno));
 
             // Only record stats the first time we try a query. This ensures that
             // queries that deterministically fail (e.g., a name that always returns
@@ -643,6 +648,7 @@
     // It should never happen, but just in case.
     if (ns >= statp->nsaddrs.size()) {
         LOG(ERROR) << __func__ << ": Out-of-bound indexing: " << ns;
+        *terrno = EINVAL;
         return -1;
     }
 
@@ -675,15 +681,14 @@
 
         statp->tcp_nssock.reset(socket(nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0));
         if (statp->tcp_nssock < 0) {
+            *terrno = errno;
+            PLOG(DEBUG) << __func__ << ": socket(vc): ";
             switch (errno) {
                 case EPROTONOSUPPORT:
                 case EPFNOSUPPORT:
                 case EAFNOSUPPORT:
-                    PLOG(DEBUG) << __func__ << ": socket(vc): ";
                     return 0;
                 default:
-                    *terrno = errno;
-                    PLOG(DEBUG) << __func__ << ": socket(vc): ";
                     return -1;
             }
         }
@@ -831,6 +836,7 @@
         *delay = res_stats_calculate_rtt(&done, &start_time);
         *rcode = anhp->rcode;
     }
+    *terrno = 0;
     return (resplen);
 }
 
@@ -972,6 +978,7 @@
     // It should never happen, but just in case.
     if (*ns >= statp->nsaddrs.size()) {
         LOG(ERROR) << __func__ << ": Out-of-bound indexing: " << ns;
+        *terrno = EINVAL;
         return -1;
     }
 
@@ -984,15 +991,14 @@
     if (statp->nssocks[*ns] == -1) {
         statp->nssocks[*ns].reset(socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0));
         if (statp->nssocks[*ns] < 0) {
+            *terrno = errno;
+            PLOG(DEBUG) << __func__ << ": socket(dg): ";
             switch (errno) {
                 case EPROTONOSUPPORT:
                 case EPFNOSUPPORT:
                 case EAFNOSUPPORT:
-                    PLOG(DEBUG) << __func__ << ": socket(dg): ";
                     return (0);
                 default:
-                    *terrno = errno;
-                    PLOG(DEBUG) << __func__ << ": socket(dg): ";
                     return (-1);
             }
         }
@@ -1001,6 +1007,7 @@
         if (statp->_mark != MARK_UNSET) {
             if (setsockopt(statp->nssocks[*ns], SOL_SOCKET, SO_MARK, &(statp->_mark),
                            sizeof(statp->_mark)) < 0) {
+                *terrno = errno;
                 statp->closeSockets();
                 return -1;
             }
@@ -1010,11 +1017,13 @@
         // ICMP port-unreachable error. This way we can detect the absence of
         // a nameserver without timing out.
         if (random_bind(statp->nssocks[*ns], nsap->sa_family) < 0) {
+            *terrno = errno;
             dump_error("bind(dg)", nsap, nsaplen);
             statp->closeSockets();
             return (0);
         }
         if (connect(statp->nssocks[*ns], nsap, (socklen_t)nsaplen) < 0) {
+            *terrno = errno;
             dump_error("connect(dg)", nsap, nsaplen);
             statp->closeSockets();
             return (0);
@@ -1022,6 +1031,7 @@
         LOG(DEBUG) << __func__ << ": new DG socket";
     }
     if (send(statp->nssocks[*ns], (const char*)buf, (size_t)buflen, 0) != buflen) {
+        *terrno = errno;
         PLOG(DEBUG) << __func__ << ": send: ";
         statp->closeSockets();
         return 0;
@@ -1037,6 +1047,7 @@
         if (!result.has_value()) {
             const bool isTimeout = (result.error().code() == ETIMEDOUT);
             *rcode = (isTimeout) ? RCODE_TIMEOUT : *rcode;
+            *terrno = (isTimeout) ? ETIMEDOUT : errno;
             *gotsomewhere = (isTimeout) ? 1 : *gotsomewhere;
             // Leave the UDP sockets open on timeout so we can keep listening for
             // a late response from this server while retrying on the next server.
@@ -1052,6 +1063,7 @@
             int resplen =
                     recvfrom(fd, (char*)ans, (size_t)anssiz, 0, (sockaddr*)(void*)&from, &fromlen);
             if (resplen <= 0) {
+                *terrno = errno;
                 PLOG(DEBUG) << __func__ << ": recvfrom: ";
                 continue;
             }
@@ -1080,6 +1092,7 @@
                 res_pquery(ans, (resplen > anssiz) ? anssiz : resplen);
                 // record the error
                 statp->_flags |= RES_F_EDNS0ERR;
+                *terrno = EREMOTEIO;
                 continue;
             }
 
@@ -1095,6 +1108,7 @@
                 // To get the rest of answer,
                 // use TCP with same server.
                 LOG(DEBUG) << __func__ << ": truncated answer";
+                *terrno = E2BIG;
                 *v_circuit = 1;
                 return 1;
             }
@@ -1103,6 +1117,7 @@
 
             *rcode = anhp->rcode;
             *ns = receivedFromNs;
+            *terrno = 0;
             return resplen;
         }
         if (!needRetry) return 0;
diff --git a/resolv_cache_unit_test.cpp b/resolv_cache_unit_test.cpp
index 6e7ffee..40f1ec6 100644
--- a/resolv_cache_unit_test.cpp
+++ b/resolv_cache_unit_test.cpp
@@ -22,7 +22,6 @@
 #include <ctime>
 #include <thread>
 
-#include <aidl/android/net/IDnsResolver.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android/multinetwork.h>
@@ -39,7 +38,6 @@
 
 using namespace std::chrono_literals;
 
-using aidl::android::net::IDnsResolver;
 using android::netdutils::IPSockAddr;
 
 constexpr int TEST_NETID = 30;
@@ -839,64 +837,6 @@
     EXPECT_STREQ(answer, domain_name);
 }
 
-TEST_F(ResolvCacheTest, GetNetworkTypesForNet) {
-    const SetupParams setup = {
-            .servers = {"127.0.0.1", "::127.0.0.2", "fe80::3"},
-            .domains = {"domain1.com", "domain2.com"},
-            .params = kParams,
-            .transportTypes = {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN}};
-    EXPECT_EQ(0, cacheCreate(TEST_NETID));
-    EXPECT_EQ(0, cacheSetupResolver(TEST_NETID, setup));
-    EXPECT_EQ(android::net::NT_WIFI_VPN, resolv_get_network_types_for_net(TEST_NETID));
-}
-
-TEST_F(ResolvCacheTest, ConvertTransportsToNetworkType) {
-    static const struct TestConfig {
-        int32_t networkType;
-        std::vector<int32_t> transportTypes;
-    } testConfigs[] = {
-            {android::net::NT_CELLULAR, {IDnsResolver::TRANSPORT_CELLULAR}},
-            {android::net::NT_WIFI, {IDnsResolver::TRANSPORT_WIFI}},
-            {android::net::NT_BLUETOOTH, {IDnsResolver::TRANSPORT_BLUETOOTH}},
-            {android::net::NT_ETHERNET, {IDnsResolver::TRANSPORT_ETHERNET}},
-            {android::net::NT_VPN, {IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_WIFI_AWARE, {IDnsResolver::TRANSPORT_WIFI_AWARE}},
-            {android::net::NT_LOWPAN, {IDnsResolver::TRANSPORT_LOWPAN}},
-            {android::net::NT_CELLULAR_VPN,
-             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_CELLULAR_VPN,
-             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_CELLULAR}},
-            {android::net::NT_WIFI_VPN,
-             {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_WIFI_VPN,
-             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI}},
-            {android::net::NT_BLUETOOTH_VPN,
-             {IDnsResolver::TRANSPORT_BLUETOOTH, IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_BLUETOOTH_VPN,
-             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_BLUETOOTH}},
-            {android::net::NT_ETHERNET_VPN,
-             {IDnsResolver::TRANSPORT_ETHERNET, IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_ETHERNET_VPN,
-             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_ETHERNET}},
-            {android::net::NT_UNKNOWN, {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_UNKNOWN,
-             {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_LOWPAN}},
-            {android::net::NT_UNKNOWN, {}},
-            {android::net::NT_UNKNOWN,
-             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_BLUETOOTH,
-              IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_WIFI_CELLULAR_VPN,
-             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_WIFI,
-              IDnsResolver::TRANSPORT_VPN}},
-            {android::net::NT_WIFI_CELLULAR_VPN,
-             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI,
-              IDnsResolver::TRANSPORT_CELLULAR}},
-    };
-    for (const auto& config : testConfigs) {
-        EXPECT_EQ(config.networkType, convert_network_type(config.transportTypes));
-    }
-}
-
 namespace {
 
 constexpr int EAI_OK = 0;
diff --git a/resolv_unit_test.cpp b/resolv_unit_test.cpp
index e0166ea..3274ed8 100644
--- a/resolv_unit_test.cpp
+++ b/resolv_unit_test.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "resolv"
 
+#include <aidl/android/net/IDnsResolver.h>
 #include <android-base/stringprintf.h>
 #include <arpa/inet.h>
 #include <gmock/gmock-matchers.h>
@@ -36,6 +37,7 @@
 namespace android {
 namespace net {
 
+using aidl::android::net::IDnsResolver;
 using android::base::StringPrintf;
 using android::net::NetworkDnsEventReported;
 using android::netdutils::ScopedAddrinfo;
@@ -122,16 +124,6 @@
     }
 
     int SetResolvers() {
-        const std::vector<std::string> servers = {test::kDefaultListenAddr};
-        const std::vector<std::string> domains = {"example.com"};
-        const res_params params = {
-                .sample_validity = 300,
-                .success_threshold = 25,
-                .min_samples = 8,
-                .max_samples = 8,
-                .base_timeout_msec = 1000,
-                .retry_count = 2,
-        };
         return resolv_set_nameservers(TEST_NETID, servers, domains, params);
     }
 
@@ -142,10 +134,21 @@
             .dns_mark = MARK_UNSET,
             .uid = NET_CONTEXT_INVALID_UID,
     };
+    const std::vector<std::string> servers = {test::kDefaultListenAddr};
+    const std::vector<std::string> domains = {"example.com"};
+    const res_params params = {
+            .sample_validity = 300,
+            .success_threshold = 25,
+            .min_samples = 8,
+            .max_samples = 8,
+            .base_timeout_msec = 1000,
+            .retry_count = 2,
+    };
 };
 
 class ResolvGetAddrInfoTest : public TestBase {};
 class GetHostByNameForNetContextTest : public TestBase {};
+class ResolvCommonFunctionTest : public TestBase {};
 
 TEST_F(ResolvGetAddrInfoTest, InvalidParameters) {
     // Both null "netcontext" and null "res" of resolv_getaddrinfo() are not tested
@@ -410,6 +413,7 @@
                  dns_server_index: 0,
                  connected: 0,
                  latency_micros: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -421,6 +425,7 @@
                  dns_server_index: 0,
                  connected: 0,
                  latency_micros: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -432,6 +437,7 @@
                  dns_server_index: 0,
                  connected: 0,
                  latency_micros: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -443,6 +449,7 @@
                  dns_server_index: 0,
                  connected: 0,
                  latency_micros: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -486,6 +493,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -496,6 +504,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -515,6 +524,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -525,6 +535,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -660,6 +671,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -670,6 +682,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 }
                 {
                  rcode: 255,
@@ -680,6 +693,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -690,6 +704,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -700,6 +715,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -710,6 +726,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 }
                 {
                  rcode: 255,
@@ -720,6 +737,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -730,6 +748,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -740,6 +759,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -750,6 +770,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 }
                 {
                  rcode: 255,
@@ -760,6 +781,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -770,6 +792,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -780,6 +803,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -790,6 +814,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 }
                 {
                  rcode: 255,
@@ -800,6 +825,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                 {
                  rcode: 255,
@@ -810,6 +836,7 @@
                  retry_times: 1,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 110,
                 },
                ]
              }
@@ -1002,6 +1029,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 7,
                 },
                 {
                  rcode: 0,
@@ -1012,6 +1040,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -1022,6 +1051,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -1041,6 +1071,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 7,
                 },
                 {
                  rcode: 0,
@@ -1051,6 +1082,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 },
                 {
                  rcode: 0,
@@ -1061,6 +1093,7 @@
                  retry_times: 0,
                  dns_server_index: 0,
                  connected: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -1128,6 +1161,7 @@
                  dns_server_index: 0,
                  connected: 0,
                  latency_micros: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -1148,6 +1182,7 @@
                  dns_server_index: 0,
                  connected: 0,
                  latency_micros: 0,
+                 linux_errno: 0,
                 }
                ]
              }
@@ -1416,6 +1451,91 @@
     }
 }
 
+TEST_F(ResolvCommonFunctionTest, GetCustTableByName) {
+    const char custAddrV4[] = "1.2.3.4";
+    const char custAddrV6[] = "::1.2.3.4";
+    const char hostnameV4V6[] = "v4v6.example.com.";
+    const aidl::android::net::ResolverOptionsParcel& resolverOptions = {
+            {
+                    {custAddrV4, hostnameV4V6},
+                    {custAddrV6, hostnameV4V6},
+            },
+            aidl::android::net::IDnsResolver::TC_MODE_DEFAULT};
+    const std::vector<int32_t>& transportTypes = {IDnsResolver::TRANSPORT_WIFI};
+    EXPECT_EQ(0, resolv_set_nameservers(TEST_NETID, servers, domains, params, resolverOptions,
+                                        transportTypes));
+    EXPECT_THAT(getCustomizedTableByName(TEST_NETID, hostnameV4V6),
+                testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
+
+    // Query address by mismatch hostname.
+    ASSERT_TRUE(getCustomizedTableByName(TEST_NETID, "not.in.cust.table").empty());
+
+    // Query address by different netid.
+    ASSERT_TRUE(getCustomizedTableByName(TEST_NETID + 1, hostnameV4V6).empty());
+    resolv_create_cache_for_net(TEST_NETID + 1);
+    EXPECT_EQ(0, resolv_set_nameservers(TEST_NETID + 1, servers, domains, params, resolverOptions,
+                                        transportTypes));
+    EXPECT_THAT(getCustomizedTableByName(TEST_NETID + 1, hostnameV4V6),
+                testing::UnorderedElementsAreArray({custAddrV4, custAddrV6}));
+}
+
+TEST_F(ResolvCommonFunctionTest, GetNetworkTypesForNet) {
+    const aidl::android::net::ResolverOptionsParcel& resolverOptions = {
+            {} /* hosts */, aidl::android::net::IDnsResolver::TC_MODE_DEFAULT};
+    const std::vector<int32_t>& transportTypes = {IDnsResolver::TRANSPORT_WIFI,
+                                                  IDnsResolver::TRANSPORT_VPN};
+    EXPECT_EQ(0, resolv_set_nameservers(TEST_NETID, servers, domains, params, resolverOptions,
+                                        transportTypes));
+    EXPECT_EQ(android::net::NT_WIFI_VPN, resolv_get_network_types_for_net(TEST_NETID));
+}
+
+TEST_F(ResolvCommonFunctionTest, ConvertTransportsToNetworkType) {
+    static const struct TestConfig {
+        int32_t networkType;
+        std::vector<int32_t> transportTypes;
+    } testConfigs[] = {
+            {android::net::NT_CELLULAR, {IDnsResolver::TRANSPORT_CELLULAR}},
+            {android::net::NT_WIFI, {IDnsResolver::TRANSPORT_WIFI}},
+            {android::net::NT_BLUETOOTH, {IDnsResolver::TRANSPORT_BLUETOOTH}},
+            {android::net::NT_ETHERNET, {IDnsResolver::TRANSPORT_ETHERNET}},
+            {android::net::NT_VPN, {IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_AWARE, {IDnsResolver::TRANSPORT_WIFI_AWARE}},
+            {android::net::NT_LOWPAN, {IDnsResolver::TRANSPORT_LOWPAN}},
+            {android::net::NT_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_CELLULAR}},
+            {android::net::NT_WIFI_VPN,
+             {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI}},
+            {android::net::NT_BLUETOOTH_VPN,
+             {IDnsResolver::TRANSPORT_BLUETOOTH, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_BLUETOOTH_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_BLUETOOTH}},
+            {android::net::NT_ETHERNET_VPN,
+             {IDnsResolver::TRANSPORT_ETHERNET, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_ETHERNET_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_ETHERNET}},
+            {android::net::NT_UNKNOWN, {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_UNKNOWN,
+             {IDnsResolver::TRANSPORT_WIFI, IDnsResolver::TRANSPORT_LOWPAN}},
+            {android::net::NT_UNKNOWN, {}},
+            {android::net::NT_UNKNOWN,
+             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_BLUETOOTH,
+              IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_CELLULAR, IDnsResolver::TRANSPORT_WIFI,
+              IDnsResolver::TRANSPORT_VPN}},
+            {android::net::NT_WIFI_CELLULAR_VPN,
+             {IDnsResolver::TRANSPORT_VPN, IDnsResolver::TRANSPORT_WIFI,
+              IDnsResolver::TRANSPORT_CELLULAR}},
+    };
+    for (const auto& config : testConfigs) {
+        EXPECT_EQ(config.networkType, convert_network_type(config.transportTypes));
+    }
+}
+
 // Note that local host file function, files_getaddrinfo(), of resolv_getaddrinfo()
 // is not tested because it only returns a boolean (success or failure) without any error number.
 
diff --git a/tests/resolv_stats_test_utils.cpp b/tests/resolv_stats_test_utils.cpp
index d31e6c0..06eff1b 100644
--- a/tests/resolv_stats_test_utils.cpp
+++ b/tests/resolv_stats_test_utils.cpp
@@ -130,6 +130,8 @@
                 dnsQueryEvent->set_connected(static_cast<bool>(value));
             } else if (protoField[1] == "latency_micros" && ParseInt(protoField[2], &value)) {
                 dnsQueryEvent->set_latency_micros(value);
+            } else if (protoField[1] == "linux_errno" && ParseInt(protoField[2], &value)) {
+                dnsQueryEvent->set_linux_errno(static_cast<LinuxErrno>(value));
             }
         }
     }
@@ -153,6 +155,7 @@
     *os << "  dns_server_index: " << event.dns_server_index() << "\n";
     *os << "  connected: " << event.connected() << "\n";
     *os << "  latency_micros: " << event.latency_micros() << "\n";
+    *os << "  linux_errno: " << event.linux_errno() << "\n";
     *os << "}";
 }
 
diff --git a/tests/resolv_stats_test_utils.h b/tests/resolv_stats_test_utils.h
index cdfbe26..f0e0ea1 100644
--- a/tests/resolv_stats_test_utils.h
+++ b/tests/resolv_stats_test_utils.h
@@ -53,6 +53,8 @@
                     // Removing the latency check, because we can't predict the time.
                     /*  ::testing::Property("latency_micros", &DnsQueryEvent::latency_micros,
                              ::testing::Eq(other.latency_micros())),*/
+                    ::testing::Property("linux_errno", &DnsQueryEvent::linux_errno,
+                                        ::testing::Eq(other.linux_errno())),
                     ::testing::Property("connected", &DnsQueryEvent::connected,
                                         ::testing::Eq(other.connected()))),
             arg, result_listener);