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

Commit 947aab0d authored by Hungming Chen's avatar Hungming Chen
Browse files

Expose DNS response code to netd

Currently, DNS query returns error code which may consider rcode only. That may
not respond corresponding error what we need. For example, rcode NOERROR
doesn't really mean no error for the whole DNS query progress. DNS server may
respond a DNS packet without any answer for queried address. In this case,
return error code from h_errno NO_DATA rather than rcode NOERROR.

Changes:
1. Expose timeout error to netd.
2. Not only consider rcode for returning DNS query error code.
3. Enable the following test cases.
   - ResolverTest.GetHostByName2_Dns64Synthesize
   - ResolverTest.GetAddrInfo_Dns64QuerySpecified
   - GetAddrInfoForNetContextTest.AlphabeticalHostname_NoData

Test: built, flashed, booted
      system/netd/tests/runtests.sh pass
      manual test with unmerged commit aosp/861310

Change-Id: I6723d133afbe56728ecaf35245d1037efc8d6da6
parent 93d0afd1
Loading
Loading
Loading
Loading
+23 −24
Original line number Diff line number Diff line
@@ -144,14 +144,10 @@ static struct addrinfo* _gethtent(FILE**, const char*, const struct addrinfo*);
static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo** res);
static int _find_src_addr(const struct sockaddr*, struct sockaddr*, unsigned, uid_t);

// TODO: Consider that refactor res_queryN, res_searchN and res_querydomainN to return one error
// code but two error codes.
static int res_queryN(const char* name, res_target* target, res_state res, int* ai_error,
                      int* herrno);
static int res_searchN(const char* name, res_target* target, res_state res, int* ai_error,
                       int* herrno);
static int res_queryN(const char* name, res_target* target, res_state res, int* herrno);
static int res_searchN(const char* name, res_target* target, res_state res, int* herrno);
static int res_querydomainN(const char* name, const char* domain, res_target* target, res_state res,
                            int* ai_error, int* herrno);
                            int* herrno);

const char* const ai_errlist[] = {
        "Success",
@@ -1469,13 +1465,12 @@ static int dns_getaddrinfo(const char* name, const addrinfo* pai,
     */
    res_setnetcontext(res, netcontext);

    // Pass ai_error to catch more detailed errors rather than EAI_NODATA.
    int ai_error = EAI_NODATA;
    int herrno = NETDB_INTERNAL;
    if (res_searchN(name, &q, res, &ai_error, &herrno) < 0) {
    if (res_searchN(name, &q, res, &herrno) < 0) {
        free(buf);
        free(buf2);
        return ai_error;  // TODO: Decode error from h_errno like we do below
        // Pass herrno to catch more detailed errors rather than EAI_NODATA.
        return herrnoToAiErrno(herrno);
    }
    ai = getanswer(buf, q.n, q.name, q.qtype, pai, &herrno);
    if (ai) {
@@ -1489,7 +1484,7 @@ static int dns_getaddrinfo(const char* name, const addrinfo* pai,
    free(buf);
    free(buf2);
    if (sentinel.ai_next == NULL) {
        return herrnoToAiError(herrno);
        return herrnoToAiErrno(herrno);
    }

    _rfc6724_sort(&sentinel, netcontext->app_mark, netcontext->uid);
@@ -1594,8 +1589,7 @@ static bool files_getaddrinfo(const char* name, const addrinfo* pai, addrinfo**
 *
 * Caller must parse answer and determine whether it answers the question.
 */
static int res_queryN(const char* name, res_target* target, res_state res, int* ai_error,
                      int* herrno) {
static int res_queryN(const char* name, res_target* target, res_state res, int* herrno) {
    u_char buf[MAXPACKET];
    HEADER* hp;
    int n;
@@ -1642,10 +1636,10 @@ static int res_queryN(const char* name, res_target* target, res_state res, int*
        }

        n = res_nsend(res, buf, n, answer, anslen, &rcode);
        *ai_error = rcodeToAiError(rcode);

        if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
            rcode = hp->rcode; /* record most recent error */
            // Record rcode from DNS response header only if no timeout.
            // Keep rcode timeout for reporting later if any.
            if (rcode != RCODE_TIMEOUT) rcode = hp->rcode; /* record most recent error */
            /* if the query choked with EDNS0, retry without EDNS0 */
            if ((res->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0 &&
                ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) {
@@ -1669,6 +1663,12 @@ static int res_queryN(const char* name, res_target* target, res_state res, int*

    if (ancount == 0) {
        switch (rcode) {
            // Not defined in RFC.
            case RCODE_TIMEOUT:
                // DNS metrics monitors DNS query timeout.
                *herrno = NETD_RESOLV_H_ERRNO_EXT_TIMEOUT;  // extended h_errno.
                break;
            // Defined in RFC 1035 section 4.1.1.
            case NXDOMAIN:
                *herrno = HOST_NOT_FOUND;
                break;
@@ -1696,8 +1696,7 @@ static int res_queryN(const char* name, res_target* target, res_state res, int*
 * If enabled, implement search rules until answer or unrecoverable failure
 * is detected.  Error code, if any, is left in *herrno.
 */
static int res_searchN(const char* name, res_target* target, res_state res, int* ai_error,
                       int* herrno) {
static int res_searchN(const char* name, res_target* target, res_state res, int* herrno) {
    const char *cp, *const *domain;
    HEADER* hp;
    u_int dots;
@@ -1722,7 +1721,7 @@ static int res_searchN(const char* name, res_target* target, res_state res, int*
     */
    saved_herrno = -1;
    if (dots >= res->ndots) {
        ret = res_querydomainN(name, NULL, target, res, ai_error, herrno);
        ret = res_querydomainN(name, NULL, target, res, herrno);
        if (ret > 0) return (ret);
        saved_herrno = *herrno;
        tried_as_is++;
@@ -1745,7 +1744,7 @@ static int res_searchN(const char* name, res_target* target, res_state res, int*
        _resolv_populate_res_for_net(res);

        for (domain = (const char* const*) res->dnsrch; *domain && !done; domain++) {
            ret = res_querydomainN(name, *domain, target, res, ai_error, herrno);
            ret = res_querydomainN(name, *domain, target, res, herrno);
            if (ret > 0) return ret;

            /*
@@ -1798,7 +1797,7 @@ static int res_searchN(const char* name, res_target* target, res_state res, int*
     * name or whether it ends with a dot.
     */
    if (!tried_as_is) {
        ret = res_querydomainN(name, NULL, target, res, ai_error, herrno);
        ret = res_querydomainN(name, NULL, target, res, herrno);
        if (ret > 0) return ret;
    }

@@ -1824,7 +1823,7 @@ static int res_searchN(const char* name, res_target* target, res_state res, int*
 * removing a trailing dot from name if domain is NULL.
 */
static int res_querydomainN(const char* name, const char* domain, res_target* target, res_state res,
                            int* ai_error, int* herrno) {
                            int* herrno) {
    char nbuf[MAXDNAME];
    const char* longname = nbuf;
    size_t n, d;
@@ -1860,5 +1859,5 @@ static int res_querydomainN(const char* name, const char* domain, res_target* ta
        }
        snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);
    }
    return res_queryN(longname, target, res, ai_error, herrno);
    return res_queryN(longname, target, res, herrno);
}
+21 −39
Original line number Diff line number Diff line
@@ -865,25 +865,18 @@ static int _dns_gethtbyname(const char* name, int addr_type, getnamaddr* info) {
        return EAI_MEMORY;
    }

    int ai_error = EAI_NODATA;
    int herrno = NETDB_INTERNAL;
    n = res_nsearch(res, name, C_IN, type, buf->buf, (int) sizeof(buf->buf), &ai_error, &herrno);
    n = res_nsearch(res, name, C_IN, type, buf->buf, (int) sizeof(buf->buf), &herrno);
    if (n < 0) {
        free(buf);
        debugprintf("res_nsearch failed (%d)\n", res, n);

        // If server responds empty answer with rcode NOERROR, adjust the error so netd will
        // get the nulltpr hp.
        // TODO: Adjust the error closed to res_nsend instead of here after h_errno is removed.
        if (ai_error == 0) {
            return herrnoToAiError(herrno);
        }
        return ai_error;
        // Pass herrno to catch more detailed errors rather than EAI_NODATA.
        return herrnoToAiErrno(herrno);
    }
    hp = getanswer(buf, n, name, type, res, info->hp, info->buf, info->buflen, &herrno);
    free(buf);
    if (hp == NULL) {
        return herrnoToAiError(herrno);
        return herrnoToAiErrno(herrno);
    }
    return 0;
}
@@ -940,25 +933,17 @@ static int _dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
        return EAI_MEMORY;
    }
    res_setnetcontext(res, netcontext);
    int ai_error = 0;
    int herrno = NETDB_INTERNAL;
    n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int) sizeof(buf->buf), &ai_error, &herrno);
    n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int) sizeof(buf->buf), &herrno);
    if (n < 0) {
        free(buf);
        debugprintf("res_nquery failed (%d)\n", res, n);

        // TODO: Consider to return more meaningful error codes.
        // Currently, doesn't consider ai_error as _dns_gethtbyname() does because current ai_error
        // comes from rcode only and rcode NOERROR doesn't really mean no error for the whole DNS
        // query progress. DNS server may respond a DNS packet without any answer for queried
        // address. In this case, return error code from h_errno NO_DATA rather than rcode NOERROR
        // (ai_error).
        return herrnoToAiError(herrno);
        return herrnoToAiErrno(herrno);
    }
    hp = getanswer(buf, n, qbuf, T_PTR, res, info->hp, info->buf, info->buflen, &herrno);
    free(buf);
    if (hp == NULL) {
        return herrnoToAiError(herrno);
        return herrnoToAiErrno(herrno);
    }

    char* bf = (char*) (hp->h_addr_list + 2);
@@ -1023,26 +1008,23 @@ static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t
    return error;
}

int herrnoToAiError(int herror) {
    switch (herror) {
        case HOST_NOT_FOUND:
int herrnoToAiErrno(int herrno) {
    switch (herrno) {
        // extended h_errno
        case NETD_RESOLV_H_ERRNO_EXT_TIMEOUT:
            return NETD_RESOLV_TIMEOUT;
        // legacy h_errno
        case NETDB_SUCCESS:
            return 0;
        case HOST_NOT_FOUND:  // TODO: Perhaps convert HOST_NOT_FOUND to EAI_NONAME instead
        case NO_DATA:         // NO_ADDRESS
            return EAI_NODATA;
        case TRY_AGAIN:
            return EAI_AGAIN;
        case NETDB_INTERNAL:
            return EAI_SYSTEM;  // see errno for detail
        case NO_RECOVERY:
        default:
            return EAI_FAIL;
    }
}
 No newline at end of file

int rcodeToAiError(int rcode) {
    // Catch the two cases (success, timeout). For other cases, just set it EAI_NODATA
    // as EAI_NODATA is returned in dns_getaddrinfo() when res_searchN() returns -1.
    switch (rcode) {
        case NOERROR:
            return 0;
        case RCODE_TIMEOUT:
            return NETD_RESOLV_TIMEOUT;
        default:
            return EAI_NODATA;
    }
}
+177 −108

File changed.

Preview size limit exceeded, changes collapsed.

+31 −13
Original line number Diff line number Diff line
@@ -111,8 +111,8 @@ int res_nquery(res_state statp, const char* name, // domain name
               int cl, int type,                   // class and type of query
               u_char* answer,                     // buffer to put answer
               int anslen,                         // size of answer buffer
               int* ai_error,                      // error will be set based on rcode
               int* herrno)                        // legacy h_errno
               int* herrno)                        // legacy and extended h_errno
                                                   // NETD_RESOLV_H_ERRNO_EXT_*
{
    u_char buf[MAXPACKET];
    HEADER* hp = (HEADER*) (void*) answer;
@@ -141,7 +141,6 @@ again:
        return n;
    }
    n = res_nsend(statp, buf, n, answer, anslen, &rcode);
    *ai_error = rcodeToAiError(rcode);
    if (n < 0) {
        /* if the query choked with EDNS0, retry without EDNS0 */
        if ((statp->options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) != 0U &&
@@ -153,7 +152,28 @@ again:
#ifdef DEBUG
        if (statp->options & RES_DEBUG) printf(";; res_query: send error\n");
#endif
        // Note that rcodes SERVFAIL, NOTIMP, REFUSED may cause res_nquery() to return a general
        // error code EAI_AGAIN, but mapping the error code from rcode as res_queryN() does for
        // getaddrinfo(). Different rcodes trigger different behaviors:
        //
        // - SERVFAIL, NOTIMP, REFUSED
        //   These result in send_dg() returning 0, causing res_nsend() to try the next
        //   nameserver. After all nameservers failed, res_nsend() returns -ETIMEDOUT, causing
        //   res_nquery() to return EAI_AGAIN here regardless of the rcode from the DNS response.
        //
        // - NXDOMAIN, FORMERR
        //   These rcodes may cause res_nsend() to return successfully (i.e. the result is a
        //   positive integer). In this case, res_nquery() returns error number by referring
        //   the rcode from the DNS response.
        switch (rcode) {
            case RCODE_TIMEOUT:  // Not defined in RFC.
                // DNS metrics monitors DNS query timeout.
                *herrno = NETD_RESOLV_H_ERRNO_EXT_TIMEOUT;  // extended h_errno.
                break;
            default:
                *herrno = TRY_AGAIN;
                break;
        }
        return n;
    }

@@ -195,8 +215,8 @@ int res_nsearch(res_state statp, const char* name, /* domain name */
                int cl, int type,                  /* class and type of query */
                u_char* answer,                    /* buffer to put answer */
                int anslen,                        /* size of answer */
                int* ai_error,                     /* error will be set based on rcode */
                int* herrno)                       /* legacy h_errno */
                int* herrno)                       /* legacy and extended
                                                      h_errno NETD_RESOLV_H_ERRNO_EXT_* */
{
    const char *cp, *const *domain;
    HEADER* hp = (HEADER*) (void*) answer;
@@ -221,7 +241,7 @@ int res_nsearch(res_state statp, const char* name, /* domain name */
     */
    saved_herrno = -1;
    if (dots >= statp->ndots || trailing_dot) {
        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, ai_error, herrno);
        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, herrno);
        if (ret > 0 || trailing_dot) return ret;
        saved_herrno = *herrno;
        tried_as_is++;
@@ -252,8 +272,7 @@ int res_nsearch(res_state statp, const char* name, /* domain name */
            if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0'))
                root_on_list++;

            ret = res_nquerydomain(statp, name, *domain, cl, type, answer, anslen, ai_error,
                                   herrno);
            ret = res_nquerydomain(statp, name, *domain, cl, type, answer, anslen, herrno);
            if (ret > 0) return ret;

            /*
@@ -306,7 +325,7 @@ int res_nsearch(res_state statp, const char* name, /* domain name */
     */
    if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0U) &&
        !(tried_as_is || root_on_list)) {
        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, ai_error, herrno);
        ret = res_nquerydomain(statp, name, NULL, cl, type, answer, anslen, herrno);
        if (ret > 0) return ret;
    }

@@ -334,8 +353,7 @@ int res_nquerydomain(res_state statp, const char* name, const char* domain, int
                     int type,       /* class and type of query */
                     u_char* answer, /* buffer to put answer */
                     int anslen,     /* size of answer */
                     int* ai_error,  /* error will be set based on rcode */
                     int* herrno)    /* legacy h_errno */
                     int* herrno)    /* legacy and extended h_errno NETD_RESOLV_H_ERRNO_EXT_* */
{
    char nbuf[MAXDNAME];
    const char* longname = nbuf;
@@ -371,5 +389,5 @@ int res_nquerydomain(res_state statp, const char* name, const char* domain, int
        }
        snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain);
    }
    return res_nquery(statp, longname, cl, type, answer, anslen, ai_error, herrno);
    return res_nquery(statp, longname, cl, type, answer, anslen, herrno);
}
+14 −9
Original line number Diff line number Diff line
@@ -211,6 +211,16 @@ void _res_stats_set_sample(res_sample* sample, time_t now, int rcode, int rtt);
#define RES_PRF_TRUNC 0x00008000
/*			0x00010000	*/

/*
 * Error code extending h_errno codes defined in bionic/libc/include/netdb.h.
 *
 * This error code, including legacy h_errno, is returned from res_nquery(), res_nsearch(),
 * res_nquerydomain(), res_queryN(), res_searchN() and res_querydomainN() for DNS metrics.
 *
 * TODO: Consider mapping legacy and extended h_errno into a unified resolver error code mapping.
 */
#define NETD_RESOLV_H_ERRNO_EXT_TIMEOUT RCODE_TIMEOUT

extern const char* const _res_opcodes[];

/* Things involving an internal (static) resolver context. */
@@ -236,11 +246,9 @@ const char* p_section(int, int);
/* Things involving a resolver context. */
int res_ninit(res_state);
void res_pquery(const res_state, const u_char*, int, FILE*);
// TODO: Consider that refactor res_nquery, res_nsearch and res_nquerydomain to return one error
// code but two error codes.
int res_nquery(res_state, const char*, int, int, u_char*, int, int*, int*);
int res_nsearch(res_state, const char*, int, int, u_char*, int, int*, int*);
int res_nquerydomain(res_state, const char*, const char*, int, int, u_char*, int, int*, int*);
int res_nquery(res_state, const char*, int, int, u_char*, int, int*);
int res_nsearch(res_state, const char*, int, int, u_char*, int, int*);
int res_nquerydomain(res_state, const char*, const char*, int, int, u_char*, int, int*);
int res_nmkquery(res_state, int, const char*, int, int, const u_char*, int, const u_char*, u_char*,
                 int);
int res_nsend(res_state, const u_char*, int, u_char*, int, int*);
@@ -260,9 +268,6 @@ int getaddrinfo_numeric(const char* hostname, const char* servname, addrinfo hin
                        addrinfo** result);

// Helper function for converting h_errno to the error codes visible to netd
int herrnoToAiError(int herrno);

// Helper function for converting rcode to the error codes visible to netd
int rcodeToAiError(int rcode);
int herrnoToAiErrno(int herrno);

#endif  // NETD_RESOLV_PRIVATE_H
Loading