Loading res_cache.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -1261,7 +1261,7 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que 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; Loading res_init.cpp +8 −35 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -120,28 +108,13 @@ void res_init(ResState* statp, const struct android_net_context* _Nonnull netcon .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 (ns = 0; ns < MAXNS; ns++) { if (statp->nssocks[ns] != -1) { close(statp->nssocks[ns]); statp->nssocks[ns] = -1; } for (auto& sock : statp->nssocks) { sock.reset(); } statp->ndots = 1; statp->_mark = netcontext->dns_mark; statp->tcp_nssock.reset(); statp->event = event; statp->netcontext_flags = netcontext->flags; } res_send.cpp +134 −151 Original line number Diff line number Diff line Loading @@ -555,7 +555,7 @@ int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int } if (resplen < 0) { _resolv_cache_query_failed(statp->netid, buf, buflen, flags); res_nclose(statp); statp->closeSockets(); return -terrno; }; Loading @@ -565,11 +565,11 @@ int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int 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 Loading Loading @@ -631,25 +631,24 @@ same_ns: 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 || if (getpeername(statp->tcp_nssock, (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 || 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: Loading @@ -662,9 +661,9 @@ same_ns: 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: "; Loading @@ -672,17 +671,17 @@ same_ns: } } 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 Loading @@ -705,10 +704,10 @@ same_ns: {.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); } /* Loading @@ -717,14 +716,14 @@ same_ns: 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 Loading @@ -736,10 +735,8 @@ read_len: */ 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)); Loading @@ -755,18 +752,18 @@ read_len: */ 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); } Loading @@ -779,7 +776,7 @@ read_len: 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 Loading Loading @@ -875,24 +872,39 @@ retry: 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: Loading @@ -911,7 +923,7 @@ static int send_dg(res_state statp, res_params* params, const uint8_t* buf, int 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; } } Loading @@ -921,30 +933,28 @@ static int send_dg(res_state statp, res_params* params, const uint8_t* buf, int // 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; } timespec timeout = get_timeout(statp, params, ns); timespec now = evNowTime(); timespec finish = evAddTime(now, timeout); for (;;) { // Wait for reply. timeout = get_timeout(statp, params, ns); now = evNowTime(); finish = evAddTime(now, timeout); retry: n = retrying_poll(s, POLLIN, &finish); int n = retrying_poll(statp->nssocks[ns], POLLIN, &finish); if (n == 0) { *rcode = RCODE_TIMEOUT; LOG(DEBUG) << __func__ << ": timeout"; Loading @@ -953,99 +963,72 @@ retry: } if (n < 0) { PLOG(DEBUG) << __func__ << ": poll: "; res_nclose(statp); statp->closeSockets(); return 0; } errno = 0; fromlen = sizeof(from); resplen = recvfrom(s, (char*) ans, (size_t) anssiz, 0, (struct sockaddr*) (void*) &from, &fromlen); 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: "; res_nclose(statp); statp->closeSockets(); return 0; } *gotsomewhere = 1; if (resplen < HFIXEDSZ) { /* * Undersized message. */ // Undersized message. LOG(DEBUG) << __func__ << ": undersized: " << resplen; *terrno = EMSGSIZE; res_nclose(statp); statp->closeSockets(); 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:"; if (ignoreInvalidAnswer(statp, from, buf, buflen, ans, anssiz)) { res_pquery(ans, (resplen > anssiz) ? anssiz : resplen); goto retry; 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. */ // 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 */ // record the error statp->_flags |= RES_F_EDNS0ERR; res_nclose(statp); statp->closeSockets(); 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(); 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); res_nclose(statp); statp->closeSockets(); *rcode = anhp->rcode; return 0; } if (anhp->tc) { /* * To get the rest of answer, * use TCP with same server. */ // To get the rest of answer, // use TCP with same server. LOG(DEBUG) << __func__ << ": truncated answer"; *v_circuit = 1; res_nclose(statp); statp->closeSockets(); return 1; } /* * All is well, or the error is fatal. Signal that the * next nameserver ought not be tried. */ // 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) { char hbuf[NI_MAXHOST]; Loading resolv_private.h +27 −17 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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; Loading @@ -82,21 +87,31 @@ union sockaddr_union { constexpr int MAXPACKET = 8 * 1024; struct ResState { 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 std::vector<std::string> search_domains{}; // domains to search sockaddr_union nsaddrs[MAXNS]; int nssocks[MAXNS]; // UDP sockets to nameservers 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 int _vcsock; // TCP socket (but why not one per nameserver?) uint32_t _flags; // See RES_F_* defines below 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 Loading @@ -121,10 +136,6 @@ void _res_stats_set_sample(res_sample* sample, time_t now, int rcode, int rtt); /* 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. * Loading @@ -146,7 +157,6 @@ int res_nquerydomain(res_state, const char*, const char*, int, int, uint8_t*, in 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, Loading tests/dns_responder/dns_responder.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -547,6 +547,10 @@ void DNSResponder::setEdns(Edns edns) { edns_ = edns; } void DNSResponder::setTtl(unsigned ttl) { answer_record_ttl_sec_ = ttl; } bool DNSResponder::running() const { return (udp_socket_.ok()) && (tcp_socket_.ok()); } Loading Loading @@ -783,7 +787,7 @@ bool DNSResponder::addAnswerRecords(const DNSQuestion& question, .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)); Loading Loading
res_cache.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -1261,7 +1261,7 @@ ResolvCacheStatus resolv_cache_lookup(unsigned netid, const void* query, int que 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; Loading
res_init.cpp +8 −35 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -120,28 +108,13 @@ void res_init(ResState* statp, const struct android_net_context* _Nonnull netcon .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 (ns = 0; ns < MAXNS; ns++) { if (statp->nssocks[ns] != -1) { close(statp->nssocks[ns]); statp->nssocks[ns] = -1; } for (auto& sock : statp->nssocks) { sock.reset(); } statp->ndots = 1; statp->_mark = netcontext->dns_mark; statp->tcp_nssock.reset(); statp->event = event; statp->netcontext_flags = netcontext->flags; }
res_send.cpp +134 −151 Original line number Diff line number Diff line Loading @@ -555,7 +555,7 @@ int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int } if (resplen < 0) { _resolv_cache_query_failed(statp->netid, buf, buflen, flags); res_nclose(statp); statp->closeSockets(); return -terrno; }; Loading @@ -565,11 +565,11 @@ int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int 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 Loading Loading @@ -631,25 +631,24 @@ same_ns: 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 || if (getpeername(statp->tcp_nssock, (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 || 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: Loading @@ -662,9 +661,9 @@ same_ns: 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: "; Loading @@ -672,17 +671,17 @@ same_ns: } } 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 Loading @@ -705,10 +704,10 @@ same_ns: {.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); } /* Loading @@ -717,14 +716,14 @@ same_ns: 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 Loading @@ -736,10 +735,8 @@ read_len: */ 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)); Loading @@ -755,18 +752,18 @@ read_len: */ 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); } Loading @@ -779,7 +776,7 @@ read_len: 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 Loading Loading @@ -875,24 +872,39 @@ retry: 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: Loading @@ -911,7 +923,7 @@ static int send_dg(res_state statp, res_params* params, const uint8_t* buf, int 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; } } Loading @@ -921,30 +933,28 @@ static int send_dg(res_state statp, res_params* params, const uint8_t* buf, int // 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; } timespec timeout = get_timeout(statp, params, ns); timespec now = evNowTime(); timespec finish = evAddTime(now, timeout); for (;;) { // Wait for reply. timeout = get_timeout(statp, params, ns); now = evNowTime(); finish = evAddTime(now, timeout); retry: n = retrying_poll(s, POLLIN, &finish); int n = retrying_poll(statp->nssocks[ns], POLLIN, &finish); if (n == 0) { *rcode = RCODE_TIMEOUT; LOG(DEBUG) << __func__ << ": timeout"; Loading @@ -953,99 +963,72 @@ retry: } if (n < 0) { PLOG(DEBUG) << __func__ << ": poll: "; res_nclose(statp); statp->closeSockets(); return 0; } errno = 0; fromlen = sizeof(from); resplen = recvfrom(s, (char*) ans, (size_t) anssiz, 0, (struct sockaddr*) (void*) &from, &fromlen); 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: "; res_nclose(statp); statp->closeSockets(); return 0; } *gotsomewhere = 1; if (resplen < HFIXEDSZ) { /* * Undersized message. */ // Undersized message. LOG(DEBUG) << __func__ << ": undersized: " << resplen; *terrno = EMSGSIZE; res_nclose(statp); statp->closeSockets(); 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:"; if (ignoreInvalidAnswer(statp, from, buf, buflen, ans, anssiz)) { res_pquery(ans, (resplen > anssiz) ? anssiz : resplen); goto retry; 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. */ // 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 */ // record the error statp->_flags |= RES_F_EDNS0ERR; res_nclose(statp); statp->closeSockets(); 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(); 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); res_nclose(statp); statp->closeSockets(); *rcode = anhp->rcode; return 0; } if (anhp->tc) { /* * To get the rest of answer, * use TCP with same server. */ // To get the rest of answer, // use TCP with same server. LOG(DEBUG) << __func__ << ": truncated answer"; *v_circuit = 1; res_nclose(statp); statp->closeSockets(); return 1; } /* * All is well, or the error is fatal. Signal that the * next nameserver ought not be tried. */ // 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) { char hbuf[NI_MAXHOST]; Loading
resolv_private.h +27 −17 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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; Loading @@ -82,21 +87,31 @@ union sockaddr_union { constexpr int MAXPACKET = 8 * 1024; struct ResState { 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 std::vector<std::string> search_domains{}; // domains to search sockaddr_union nsaddrs[MAXNS]; int nssocks[MAXNS]; // UDP sockets to nameservers 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 int _vcsock; // TCP socket (but why not one per nameserver?) uint32_t _flags; // See RES_F_* defines below 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 Loading @@ -121,10 +136,6 @@ void _res_stats_set_sample(res_sample* sample, time_t now, int rcode, int rtt); /* 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. * Loading @@ -146,7 +157,6 @@ int res_nquerydomain(res_state, const char*, const char*, int, int, uint8_t*, in 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, Loading
tests/dns_responder/dns_responder.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -547,6 +547,10 @@ void DNSResponder::setEdns(Edns edns) { edns_ = edns; } void DNSResponder::setTtl(unsigned ttl) { answer_record_ttl_sec_ = ttl; } bool DNSResponder::running() const { return (udp_socket_.ok()) && (tcp_socket_.ok()); } Loading Loading @@ -783,7 +787,7 @@ bool DNSResponder::addAnswerRecords(const DNSQuestion& question, .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)); Loading