Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d08fbea authored by Ken Chen's avatar Ken Chen
Browse files

Support variety of IPv6 addresses on RDNS-on-cache

IPv6 address has multiple representation formats. The following
addresses are the same:

  "2001:db8::102:304"
  "2001:0db8:0000:0000:0000:0000:0102:0304"
  "2001:db8::1.2.3.4"

In original design, the RDNS-on-cache can only match IPv6 address with
format "2001:db8::102:304", but not with the others. It is because all
different representations are converted to a single binary form while
they are stored in cache. And of course, it will be a single format if
the cached IP address is converted back to text form while doing text
comparison. In this commit, the RDNS-on-cache converts query address to
binary form and binary compares with cached entry, rather than text
comparison. Also, add two test cases.

Bug: 131051938
Test: atest resolv_cache_unit_test.cpp#GetHostByAddrFromCache_InvalidArgs
      atest resolv_cache_unit_test.cpp#GetHostByAddrFromCache
Change-Id: I064f4bafca91ad7dbbd9b37ce3c2d05f8d6b954d
parent effead99
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -104,6 +104,6 @@ LIBNETD_RESOLV_PUBLIC bool resolv_init(const ResolverNetdCallbacks* callbacks);
// Function that performs RDNS in local cache. The |domain_name_size| is the size of domain_name
// buffer, which is recommended to NS_MAXDNAME. Function return false if hostname not found or
// domain_name_size > NS_MAXDNAME.
LIBNETD_RESOLV_PUBLIC bool resolv_gethostbyaddr_from_local_cache(unsigned netId, char domain_name[],
                                                                 unsigned domain_name_size,
                                                                 char* ip_address);
LIBNETD_RESOLV_PUBLIC bool resolv_gethostbyaddr_from_cache(unsigned netId, char domain_name[],
                                                           size_t domain_name_size,
                                                           const char* ip_address, int af);
+3 −2
Original line number Diff line number Diff line
@@ -38,8 +38,9 @@ extern struct ResolvStub {

    bool (*resolv_init)(const ResolverNetdCallbacks& callbacks);

    bool (*resolv_gethostbyaddr_from_local_cache)(unsigned netId, char domain_name[],
                                                  unsigned domain_name_size, char* ip_address);
    bool (*resolv_gethostbyaddr_from_cache)(unsigned netId, char domain_name[],
                                            size_t domain_name_size, const char* ip_address,
                                            int af);
} RESOLV_STUB;

int resolv_stub_init();
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ LIBNETD_RESOLV {
  global:
    resolv_has_nameservers;
    resolv_init;
    resolv_gethostbyaddr_from_local_cache;
    resolv_gethostbyaddr_from_cache;
  local:
    *;
};
+31 −28
Original line number Diff line number Diff line
@@ -1289,10 +1289,16 @@ int resolv_cache_add(unsigned netid, const void* query, int querylen, const void
    return 0;
}

bool resolv_gethostbyaddr_from_local_cache(unsigned netid, char domain_name[],
                                           unsigned domain_name_size, char* ip_address) {
bool resolv_gethostbyaddr_from_cache(unsigned netid, char domain_name[], size_t domain_name_size,
                                     const char* ip_address, int af) {
    if (domain_name_size > NS_MAXDNAME) {
        LOG(ERROR) << __func__ << ": invalid domain_name_size " << domain_name_size;
        LOG(WARNING) << __func__ << ": invalid domain_name_size " << domain_name_size;
        return false;
    } else if (ip_address == nullptr || ip_address[0] == '\0') {
        LOG(WARNING) << __func__ << ": invalid ip_address";
        return false;
    } else if (af != AF_INET && af != AF_INET6) {
        LOG(WARNING) << __func__ << ": unsupported AF";
        return false;
    }

@@ -1301,14 +1307,11 @@ bool resolv_gethostbyaddr_from_local_cache(unsigned netid, char domain_name[],

    ns_rr rr;
    ns_msg handle;

    int query_count;
    ns_rr rr_query;

    // For ntop.
    char* resolved_ip = nullptr;
    char buf4[INET_ADDRSTRLEN];
    char buf6[INET6_ADDRSTRLEN];
    struct sockaddr_in sa;
    struct sockaddr_in6 sa6;
    char* addr_buf = nullptr;

    std::lock_guard guard(cache_mutex);

@@ -1336,20 +1339,21 @@ bool resolv_gethostbyaddr_from_local_cache(unsigned netid, char domain_name[],
                continue;
            }

            resolved_ip = nullptr;
            if (ns_rr_type(rr) == ns_t_a) {
                inet_ntop(AF_INET, ns_rr_rdata(rr), buf4, sizeof(buf4));
                resolved_ip = buf4;
            } else if (ns_rr_type(rr) == ns_t_aaaa) {
                inet_ntop(AF_INET6, ns_rr_rdata(rr), buf6, sizeof(buf6));
                resolved_ip = buf6;
            if (ns_rr_type(rr) == ns_t_a && af == AF_INET) {
                addr_buf = (char*)&(sa.sin_addr);
            } else if (ns_rr_type(rr) == ns_t_aaaa && af == AF_INET6) {
                addr_buf = (char*)&(sa6.sin6_addr);
            } else {
                continue;
            }

            if ((resolved_ip != nullptr) && (resolved_ip[0] != '\0')) {
                if (strcmp(resolved_ip, ip_address) == 0) {
                    query_count = ns_msg_count(handle, ns_s_qd);
            if (inet_pton(af, ip_address, addr_buf) != 1) {
                LOG(WARNING) << __func__ << ": inet_pton() fail";
                return false;
            }

            if (memcmp(ns_rr_rdata(rr), addr_buf, ns_rr_rdlen(rr)) == 0) {
                int query_count = ns_msg_count(handle, ns_s_qd);
                for (int i = 0; i < query_count; i++) {
                    memset(&rr_query, 0, sizeof(rr_query));
                    if (ns_parserr(&handle, ns_s_qd, i, &rr_query)) {
@@ -1363,7 +1367,6 @@ bool resolv_gethostbyaddr_from_local_cache(unsigned netid, char domain_name[],
            }
        }
    }
    }

    return false;
}
+79 −0
Original line number Diff line number Diff line
@@ -716,6 +716,85 @@ TEST_F(ResolvCacheTest, GetStats) {
    expectCacheStats("GetStats", TEST_NETID, cacheStats);
}

TEST_F(ResolvCacheTest, GetHostByAddrFromCache_InvalidArgs) {
    char domain_name[NS_MAXDNAME] = {};
    const char query_v4[] = "1.2.3.5";

    // invalid buffer size
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME + 1, nullptr,
                                                 AF_INET));
    EXPECT_STREQ("", domain_name);

    // invalid query
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, nullptr,
                                                 AF_INET));
    EXPECT_STREQ("", domain_name);

    // unsupported AF
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
                                                 AF_UNSPEC));
    EXPECT_STREQ("", domain_name);
}

TEST_F(ResolvCacheTest, GetHostByAddrFromCache) {
    char domain_name[NS_MAXDNAME] = {};
    const char query_v4[] = "1.2.3.5";
    const char query_v6[] = "2001:db8::102:304";
    const char query_v6_unabbreviated[] = "2001:0db8:0000:0000:0000:0000:0102:0304";
    const char query_v6_mixed[] = "2001:db8::1.2.3.4";
    const char answer[] = "existent.in.cache";

    // cache does not exist
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
                                                 AF_INET));
    EXPECT_STREQ("", domain_name);

    // cache is empty
    EXPECT_EQ(0, cacheCreate(TEST_NETID));
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
                                                 AF_INET));
    EXPECT_STREQ("", domain_name);

    // no v4 match in cache
    CacheEntry ce = makeCacheEntry(QUERY, "any.data", ns_c_in, ns_t_a, "1.2.3.4");
    EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
                                                 AF_INET));
    EXPECT_STREQ("", domain_name);

    // v4 match
    ce = makeCacheEntry(QUERY, answer, ns_c_in, ns_t_a, query_v4);
    EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v4,
                                                AF_INET));
    EXPECT_STREQ(answer, domain_name);

    // no v6 match in cache
    memset(domain_name, 0, NS_MAXDNAME);
    EXPECT_FALSE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v6,
                                                 AF_INET6));
    EXPECT_STREQ("", domain_name);

    // v6 match
    ce = makeCacheEntry(QUERY, answer, ns_c_in, ns_t_aaaa, query_v6);
    EXPECT_EQ(0, cacheAdd(TEST_NETID, ce));
    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME, query_v6,
                                                AF_INET6));
    EXPECT_STREQ(answer, domain_name);

    // v6 match with unabbreviated address format
    memset(domain_name, 0, NS_MAXDNAME);
    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME,
                                                query_v6_unabbreviated, AF_INET6));
    EXPECT_STREQ(answer, domain_name);

    // v6 with mixed address format
    memset(domain_name, 0, NS_MAXDNAME);
    EXPECT_TRUE(resolv_gethostbyaddr_from_cache(TEST_NETID, domain_name, NS_MAXDNAME,
                                                query_v6_mixed, AF_INET6));
    EXPECT_STREQ(answer, domain_name);
}

namespace {

constexpr int EAI_OK = 0;