一、 背景

当dns被污染后怎么也打不开网页,即使后来dns获取正确了,你会发现有段时间页无法访问该网页。有时候必须手动在浏览器端清除缓存才能正常访问。但是除了应用端的缓存外系统端还会有缓存的。

我们做个实验:

dns查询log                                                                                             ping操作

由上面的操作我们可以看到 在第一次ping seewo.com的时候  左边dnsmasq的log中显示有query[A] seewo.com 查询操作的,然后返回了对应的ip:113.96.140.33 。 当我们进行第二次ping操作的时候 可以看到左边没有再次进行查询的操作了。这就坐实了在dnsmasq之前 是有缓存的。

二、DNS缓存位置查找

有了以上的思路,我们就可以着手查找哪里存在DNS缓存了。一开始一脸懵,那我们就先看看问题一是怎么请求的吧。一般我们会调用lookupAllHostAddr函数


public static InetAddress[] getAllByName(String host)throws UnknownHostException {// Android-changed: Resolves a hostname using Libcore.os.// Also, returns both the Inet4 and Inet6 loopback for null/empty hostreturn impl.lookupAllHostAddr(host, NETID_UNSET).clone();
}

接着在实现类里面:

@Overridepublic InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException {if (host == null || host.isEmpty()) {// Android-changed: Return both the Inet4 and Inet6 loopback addresses// when host == null or empty.return loopbackAddresses();}// Is it a numeric address?InetAddress result = InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(host);if (result != null) {return new InetAddress[] { result };}return lookupHostByName(host, netId);}

上面通过result 来判断从哪条路径获取InetAddress[]       之后在java缓冲区再分析lookupHostByName。

1.               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        开始 native层缓冲区寻找       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我们先不管具体路径,先看看拿到InetAddress[]之后如何获取对应IP:

Log.e(TAG, "$hostname: ${it.hostAddress}")

可以看到其是通过  InetAddress的getHostAddress() 接口拿到的DNS解析的IP,


@Override
public String getHostAddress() {// Android-changed: getnameinfo returns smarter representations than getHostAddress()// return holder6.getHostAddress();return Libcore.os.getnameinfo(this, NI_NUMERICHOST); // Can't throw.
}

在实现类里面我们继续找getnameinfo方法

libcore/luni/src/main/java/libcore/io/ForwardingOs.java:


public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }

JNI方法

libcore/luni/src/main/native/libcore_io_Linux.cpp


static jstring Linux_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint flags) {sockaddr_storage ss;socklen_t sa_len;if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, ss, sa_len)) {return NULL;}char buf[NI_MAXHOST]; // NI_MAXHOST is longer than INET6_ADDRSTRLEN.errno = 0;int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), sa_len, buf, sizeof(buf), NULL, 0, flags);if (rc != 0) {throwGaiException(env, "getnameinfo", rc);return NULL;}return env->NewStringUTF(buf);
}

getnameinfo 是个native方法

bionic/libc/dns/net/getnameinfo.c

int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen,char* serv, size_t servlen, int flags)
{return android_getnameinfofornet(sa, salen, host, hostlen, serv, servlen, flags,NETID_UNSET, MARK_UNSET);
}int android_getnameinfofornet(const struct sockaddr* sa, socklen_t salen, char* host,size_t hostlen, char* serv, size_t servlen, int flags, unsigned netid,unsigned mark)
{switch (sa->sa_family) {case AF_INET:case AF_INET6:return getnameinfo_inet(sa, salen, host, hostlen,serv, servlen, flags, netid, mark);case AF_LOCAL:return getnameinfo_local(sa, salen, host, hostlen,serv, servlen, flags);default:return EAI_FAMILY;}
}/** getnameinfo_inet():* Format an IPv4 or IPv6 sockaddr into a printable string.*/
static int
getnameinfo_inet(const struct sockaddr* sa, socklen_t salen,char *host, socklen_t hostlen,char *serv, socklen_t servlen,int flags, unsigned netid, unsigned mark)
{const struct afd *afd;struct servent *sp;struct hostent *hp;u_short port;int family, i;const char *addr;uint32_t v4a;char numserv[512];char numaddr[512];/* sa is checked below *//* host may be NULL *//* serv may be NULL */if (sa == NULL)return EAI_FAIL;family = sa->sa_family;for (i = 0; afdl[i].a_af; i++)if (afdl[i].a_af == family) {afd = &afdl[i];goto found;}return EAI_FAMILY;found:// http://b/1889275: callers should be allowed to provide too much// space, but not too little.if (salen < afd->a_socklen) {return EAI_FAMILY;}/* network byte order */port = ((const struct sockinet *)(const void *)sa)->si_port;addr = (const char *)(const void *)sa + afd->a_off;if (serv == NULL || servlen == 0) {/** do nothing in this case.* in case you are wondering if "&&" is more correct than* "||" here: rfc2553bis-03 says that serv == NULL OR* servlen == 0 means that the caller does not want the result.*/} else {if (flags & NI_NUMERICSERV)sp = NULL;else {sp = getservbyport(port,(flags & NI_DGRAM) ? "udp" : "tcp");}if (sp) {if (strlen(sp->s_name) + 1 > (size_t)servlen)return EAI_MEMORY;strlcpy(serv, sp->s_name, servlen);} else {snprintf(numserv, sizeof(numserv), "%u", ntohs(port));if (strlen(numserv) + 1 > (size_t)servlen)return EAI_MEMORY;strlcpy(serv, numserv, servlen);}}switch (sa->sa_family) {case AF_INET:v4a = (uint32_t)ntohl(((const struct sockaddr_in *)(const void *)sa)->sin_addr.s_addr);if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))flags |= NI_NUMERICHOST;v4a >>= IN_CLASSA_NSHIFT;if (v4a == 0)flags |= NI_NUMERICHOST;break;
#ifdef INET6case AF_INET6:{const struct sockaddr_in6 *sin6;sin6 = (const struct sockaddr_in6 *)(const void *)sa;switch (sin6->sin6_addr.s6_addr[0]) {case 0x00:if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr));else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr));else if (IN6_IS_ADDR_WKP(&sin6->sin6_addr));elseflags |= NI_NUMERICHOST;break;default:if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {flags |= NI_NUMERICHOST;}else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))flags |= NI_NUMERICHOST;break;}}break;
#endif}if (host == NULL || hostlen == 0) {/** do nothing in this case.* in case you are wondering if "&&" is more correct than* "||" here: rfc2553bis-03 says that host == NULL or* hostlen == 0 means that the caller does not want the result.*/} else if (flags & NI_NUMERICHOST) {size_t numaddrlen;/* NUMERICHOST and NAMEREQD conflicts with each other */if (flags & NI_NAMEREQD)return EAI_NONAME;switch(afd->a_af) {
#ifdef INET6case AF_INET6:{int error;if ((error = ip6_parsenumeric(sa, addr, host,hostlen, flags)) != 0)return(error);break;}
#endifdefault:if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))== NULL)return EAI_SYSTEM;numaddrlen = strlen(numaddr);if (numaddrlen + 1 > (size_t)hostlen) /* don't forget terminator */return EAI_MEMORY;strlcpy(host, numaddr, hostlen);break;}} else {// This code should only run in the app context, not inside netd, so netid is// the app's netid.  netd doesn't use getnameinfo for network requests.const struct android_net_context netcontext = { .app_netid = netid, .app_mark = mark };hp = android_gethostbyaddrfornetcontext_proxy(addr, afd->a_addrlen, afd->a_af, &netcontext);                     //  注意这个位置if (hp) {
#if 0/** commented out, since "for local host" is not* implemented here - see RFC2553 p30*/if (flags & NI_NOFQDN) {char *p;p = strchr(hp->h_name, '.');if (p)TODO: Before uncommenting rewrite to avoid modifying hp.*p = '\0';}
#endifif (strlen(hp->h_name) + 1 > (size_t)hostlen) {return EAI_MEMORY;}strlcpy(host, hp->h_name, hostlen);} else {if (flags & NI_NAMEREQD)return EAI_NONAME;switch(afd->a_af) {
#ifdef INET6case AF_INET6:{int error;if ((error = ip6_parsenumeric(sa, addr, host,hostlen,flags)) != 0)return(error);break;}
#endifdefault:if (inet_ntop(afd->a_af, addr, host,hostlen) == NULL)return EAI_SYSTEM;break;}}}return(0);
}

注意上面的android_gethostbyaddrfornetcontext_proxy

bionic/libc/dns/net/gethnamaddr.c

static struct hostent*
android_gethostbyaddrfornetcontext_proxy_internal(const void* addr, socklen_t len, int af,struct hostent *hp, char *hbuf, size_t hbuflen, int *he,const struct android_net_context *netcontext)
{FILE* proxy = fdopen(__netdClientDispatch.dnsOpenProxy(), "r+");                            //  万物皆文件 之后看看打开的是什么if (proxy == NULL) {// Either we're not supposed to be using the proxy or the proxy is unavailable.return android_gethostbyaddrfornetcontext_real(addr,len, af, hp, hbuf, hbuflen, he, netcontext);}char buf[INET6_ADDRSTRLEN];  //big enough for IPv4 and IPv6const char * addrStr = inet_ntop(af, addr, buf, sizeof(buf));if (addrStr == NULL) {fclose(proxy);return NULL;}unsigned netid = __netdClientDispatch.netIdForResolv(netcontext->app_netid);if (fprintf(proxy, "gethostbyaddr %s %d %d %u",   //注意这里很重要 这里是socket查询命令  之后对gethostbyaddr 指令进行解析addrStr, len, af, netid) < 0) {fclose(proxy);return NULL;}if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {fclose(proxy);return NULL;}struct hostent *result = android_read_hostent(proxy, hp, hbuf, hbuflen, he);                  // 接收结果fclose(proxy);return result;
}

上面会fopen __netdClientDispatch.dnsOpenProxy 这个鬼东西 看看是什么

bionic/libc/bionic/NetdClient.cp

static void netdClientInitImpl() {// Prevent netd from looping back fwmarkd connections to itself. It would work, but it's// a deadlock hazard and unnecessary overhead for the resolver.if (getuid() == 0 && strcmp(basename(getprogname()), "netd") == 0) {async_safe_format_log(ANDROID_LOG_INFO, "netdClient","Skipping libnetd_client init since *we* are netd");return;}void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);if (netdClientHandle == NULL) {// If the library is not available, it's not an error. We'll just use// default implementations of functions that it would've overridden.return;}netdClientInitFunction(netdClientHandle, "netdClientInitAccept4",&__netdClientDispatch.accept4);netdClientInitFunction(netdClientHandle, "netdClientInitConnect",&__netdClientDispatch.connect);netdClientInitFunction(netdClientHandle, "netdClientInitNetIdForResolv",&__netdClientDispatch.netIdForResolv);netdClientInitFunction(netdClientHandle, "netdClientInitSocket", &__netdClientDispatch.socket);netdClientInitFunction(netdClientHandle, "netdClientInitDnsOpenProxy",&__netdClientDispatch.dnsOpenProxy);
}

netdClientInitDnsOpenProxy    原来要跟netd联动了 我们继续查看netd中的代码 会发现这隐秘的py交易:

system/netd/client/NetdClient.cpp

extern "C" void netdClientInitDnsOpenProxy(DnsOpenProxyType* function) {if (function) {*function = dns_open_proxy;}
}int dns_open_proxy() {const char* cache_mode = getenv("ANDROID_DNS_MODE");const bool use_proxy = (cache_mode == NULL || strcmp(cache_mode, "local") != 0);if (!use_proxy) {errno = ENOSYS;return -1;}const auto socketFunc = libcSocket ? libcSocket : socket;int s = socketFunc(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);if (s == -1) {return -1;}const int one = 1;setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));static const struct sockaddr_un proxy_addr = {.sun_family = AF_UNIX,.sun_path = "/dev/socket/dnsproxyd",                                                             // socket节点 netd初试化的时候已经创建了};const auto connectFunc = libcConnect ? libcConnect : connect;if (TEMP_FAILURE_RETRY(                                                                                  // 尝试连接socketconnectFunc(s, (const struct sockaddr*) &proxy_addr, sizeof(proxy_addr))) != 0) {// Store the errno for connect because we only care about why we can't connect to dnsproxydint storedErrno = errno;close(s);errno = storedErrno;return -1;}return s;
}

所以最终就会通过socket和netd建立连接 , dnsproxydk系统启动时就会创建,netd里面会监听此节点的请求。具体流程请自行搜索其他分析文章。

建立链接之后 我们上面提到的gethostbyaddr 命令就会找到具体执行的地方了:

system/netd/resolv/DnsProxyListener.cpp

/********************************************************                  GetHostByAddr                      ********************************************************/
DnsProxyListener::GetHostByAddrCmd::GetHostByAddrCmd() : FrameworkCommand("gethostbyaddr") {}                                        // 这里是gethostbyaddr 对应的解析int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli,int argc, char **argv) {logArguments(argc, argv);if (argc != 5) {char* msg = nullptr;asprintf(&msg, "Invalid number of arguments to gethostbyaddr: %i", argc);LOG(WARNING) << "GetHostByAddrCmd::runCommand: " << (msg ? msg : "null");cli->sendMsg(ResponseCode::CommandParameterError, msg, false);free(msg);return -1;}char* addrStr = argv[1];int addrLen = strtol(argv[2], nullptr, 10);int addrFamily = strtol(argv[3], nullptr, 10);uid_t uid = cli->getUid();unsigned netId = strtoul(argv[4], nullptr, 10);const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId);void* addr = malloc(sizeof(in6_addr));errno = 0;int result = inet_pton(addrFamily, addrStr, addr);if (result <= 0) {char* msg = nullptr;asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno));LOG(WARNING) << "GetHostByAddrCmd::runCommand: " << (msg ? msg : "null");cli->sendMsg(ResponseCode::OperationFailed, msg, false);free(addr);free(msg);return -1;}android_net_context netcontext;gResNetdCallbacks.get_network_context(netId, uid, &netcontext);if (useLocalNameservers) {netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;}DnsProxyListener::GetHostByAddrHandler* handler = new DnsProxyListener::GetHostByAddrHandler(cli, addr, addrLen, addrFamily, netcontext);tryThreadOrError(cli, handler);return 0;
}DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, void* address,int addressLen, int addressFamily,const android_net_context& netcontext): mClient(c),mAddress(address),mAddressLen(addressLen),mAddressFamily(addressFamily),mNetContext(netcontext) {}DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() {free(mAddress);
}void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(struct hostent** hpp) {if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) {return;}netdutils::IPPrefix prefix{};if (!getDns64Prefix(mNetContext.dns_netid, &prefix)) {return;}if (!isValidNat64Prefix(prefix)) {return;}struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip());struct sockaddr_in6* v6prefix = (struct sockaddr_in6*) &ss;struct in6_addr v6addr = *(in6_addr*) mAddress;// Check if address has NAT64 prefix. Only /96 IPv6 NAT64 prefixes are supportedif ((v6addr.s6_addr32[0] != v6prefix->sin6_addr.s6_addr32[0]) ||(v6addr.s6_addr32[1] != v6prefix->sin6_addr.s6_addr32[1]) ||(v6addr.s6_addr32[2] != v6prefix->sin6_addr.s6_addr32[2])) {return;}const uid_t uid = mClient->getUid();if (queryLimiter.start(uid)) {// Remove NAT64 prefix and do reverse DNS querystruct in_addr v4addr = {.s_addr = v6addr.s6_addr32[3]};android_gethostbyaddrfornetcontext(&v4addr, sizeof(v4addr), AF_INET, &mNetContext, hpp);queryLimiter.finish(uid);if (*hpp) {// Replace IPv4 address with original queried IPv6 address in place. The space has// reserved by dns_gethtbyaddr() and netbsd_gethostent_r() in// system/netd/resolv/gethnamaddr.cpp.// Note that android_gethostbyaddrfornetcontext returns only one entry in result.memcpy((*hpp)->h_addr_list[0], &v6addr, sizeof(v6addr));(*hpp)->h_addrtype = AF_INET6;(*hpp)->h_length = sizeof(struct in6_addr);}} else {LOG(ERROR) << __func__ << ": from UID " << uid << ", max concurrent queries reached";}
}void DnsProxyListener::GetHostByAddrHandler::run() {Stopwatch s;maybeFixupNetContext(&mNetContext);const uid_t uid = mClient->getUid();hostent* hp = nullptr;int32_t rv = 0;NetworkDnsEventReported dnsEvent;if (queryLimiter.start(uid)) {rv = android_gethostbyaddrfornetcontext(mAddress, mAddressLen, mAddressFamily,&mNetContext, &hp);queryLimiter.finish(uid);} else {rv = EAI_MEMORY;LOG(ERROR) << "GetHostByAddrHandler::run: from UID " << uid<< ", max concurrent queries reached";}doDns64ReverseLookup(&hp);const int latencyUs = int(s.timeTakenUs());LOG(DEBUG) << "GetHostByAddrHandler::run: result: " << (hp ? "success" : gai_strerror(rv));bool success = true;if (hp) {success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;success &= sendhostent(mClient, hp);} else {success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, nullptr, 0) == 0;}if (!success) {LOG(WARNING) << "GetHostByAddrHandler::run: Error writing DNS result to client";}reportDnsEvent(INetdEventListener::EVENT_GETHOSTBYADDR, mNetContext, latencyUs, rv, dnsEvent,(hp && hp->h_name) ? hp->h_name : "null", {}, 0);mClient->decRef();
}

GetHostByAddrHandler会调用 doDns64ReverseLookup 又调用android_gethostbyaddrfornetcontext进行查询

system/netd/resolv/gethnamaddr.cpp

int android_gethostbyaddrfornetcontext(const void* addr, socklen_t len, int af,const struct android_net_context* netcontext, hostent** hp) {return android_gethostbyaddrfornetcontext_proxy(addr, len, af, netcontext, hp);
}static int android_gethostbyaddrfornetcontext_proxy(const void* addr, socklen_t len, int af,const struct android_net_context* netcontext,hostent** hp) {struct res_static* rs = res_get_static();  // For thread-safety.int error = android_gethostbyaddrfornetcontext_proxy_internal(addr, len, af, &rs->host, rs->hostbuf, sizeof(rs->hostbuf), netcontext);if (error == 0) *hp = &rs->host;return error;
}static int android_gethostbyaddrfornetcontext_proxy_internal(const void* addr, socklen_t len, int af, struct hostent* hp, char* hbuf, size_t hbuflen,const struct android_net_context* netcontext) {return android_gethostbyaddrfornetcontext_real(addr, len, af, hp, hbuf, hbuflen, netcontext);
}static int android_gethostbyaddrfornetcontext_real(const void* addr, socklen_t len, int af,struct hostent* hp, char* buf, size_t buflen,const struct android_net_context* netcontext) {const u_char* uaddr = (const u_char*) addr;socklen_t size;struct getnamaddr info;_DIAGASSERT(addr != NULL);if (af == AF_INET6 && len == NS_IN6ADDRSZ &&(IN6_IS_ADDR_LINKLOCAL((const struct in6_addr*) addr) ||IN6_IS_ADDR_SITELOCAL((const struct in6_addr*) addr))) {return EAI_NODATA;}if (af == AF_INET6 && len == NS_IN6ADDRSZ &&(IN6_IS_ADDR_V4MAPPED((const struct in6_addr*) addr) ||IN6_IS_ADDR_V4COMPAT((const struct in6_addr*) addr))) {/* Unmap. */uaddr += NS_IN6ADDRSZ - NS_INADDRSZ;addr = uaddr;af = AF_INET;len = NS_INADDRSZ;}switch (af) {case AF_INET:size = NS_INADDRSZ;break;case AF_INET6:size = NS_IN6ADDRSZ;break;default:return EAI_FAMILY;}if (size != len) {// TODO: Consider converting to a private extended EAI_* error code.// Currently, the EAI_* value has no corresponding error code for invalid argument socket// length. In order to not rely on errno, convert the original error code pair, EAI_SYSTEM// and EINVAL, to EAI_FAIL.return EAI_FAIL;}info.hp = hp;info.buf = buf;info.buflen = buflen;if (_hf_gethtbyaddr(uaddr, len, af, &info)) {int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info);                                  //   到这里终于开始真正的业务了if (error != 0) return error;}return 0;
}

继续调用dns_gethtbyaddr

static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,const android_net_context* netcontext, getnamaddr* info) {char qbuf[MAXDNAME + 1], *qp, *ep;int n;int advance;info->hp->h_length = len;info->hp->h_addrtype = af;switch (info->hp->h_addrtype) {case AF_INET:(void) snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff),(uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff));break;case AF_INET6:qp = qbuf;ep = qbuf + sizeof(qbuf) - 1;for (n = NS_IN6ADDRSZ - 1; n >= 0; n--) {advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.", uaddr[n] & 0xf,((unsigned int) uaddr[n] >> 4) & 0xf);if (advance > 0 && qp + advance < ep)qp += advance;else {// TODO: Consider converting to a private extended EAI_* error code.// Currently, the EAI_* value has no corresponding error code for an internal// out of buffer space. In order to not rely on errno, convert the original// error code EAI_SYSTEM to EAI_MEMORY.return EAI_MEMORY;}}if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {// TODO: Consider converting to a private extended EAI_* error code.// Currently, the EAI_* value has no corresponding error code for an internal// out of buffer space. In order to not rely on errno, convert the original// error code EAI_SYSTEM to EAI_MEMORY.return EAI_MEMORY;}break;default:return EAI_FAMILY;}auto buf = std::make_unique<querybuf>();res_state res = res_get_state();if (!res) return EAI_MEMORY;res_setnetcontext(res, netcontext);int he;n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);      //  重点来啦!!!!!!!!   开始查询了!!!!if (n < 0) {LOG(DEBUG) << __func__ << ": res_nquery failed (" << n << ")";// Note that res_nquery() doesn't set the pair NETDB_INTERNAL and errno.// Return h_errno (he) to catch more detailed errors rather than EAI_NODATA.// See also herrnoToAiErrno().return herrnoToAiErrno(he);}hostent* hp = getanswer(buf.get(), n, qbuf, T_PTR, res, info->hp, info->buf, info->buflen, &he);if (hp == NULL) return herrnoToAiErrno(he);char* bf = (char*) (hp->h_addr_list + 2);size_t blen = (size_t)(bf - info->buf);if (blen + info->hp->h_length > info->buflen) goto nospc;hp->h_addr_list[0] = bf;hp->h_addr_list[1] = NULL;memcpy(bf, uaddr, (size_t) info->hp->h_length);if (info->hp->h_addrtype == AF_INET && (res->options & RES_USE_INET6)) {if (blen + NS_IN6ADDRSZ > info->buflen) goto nospc;map_v4v6_address(bf, bf);hp->h_addrtype = AF_INET6;hp->h_length = NS_IN6ADDRSZ;}/* Reserve enough space for mapping IPv4 address to IPv6 address in place */if (info->hp->h_addrtype == AF_INET) {if (blen + NS_IN6ADDRSZ > info->buflen) goto nospc;// Pad zero to the unused address spacememcpy(bf + NS_INADDRSZ, NAT64_PAD, sizeof(NAT64_PAD));}return 0;nospc:return EAI_MEMORY;
}

开始进行查询了   res_nquery

system/netd/resolv/res_query.cpp

/*
* Formulate a normal query, send, and await answer.
* Returned answer is placed in supplied buffer "answer".
* Perform preliminary check of answer, returning success only
* if no error is indicated and the answer count is nonzero.
* Return the size of the response on success, -1 on error.
* Error number is left in *herrno.
*
* Caller must parse answer and determine whether it answers the question.
*/
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* herrno) // legacy and extended h_errno
// NETD_RESOLV_H_ERRNO_EXT_*
{...
n = res_nsend(statp, buf, n, answer, anslen, &rcode, 0);
...
return n;
}

我们不关心上面的查询逻辑,我们着眼于res_nsend这个函数做了什么

system/netd/resolv/res_send.cpp

int res_nsend(res_state statp, const u_char* buf, int buflen, u_char* ans, int anssiz, int* rcode,uint32_t flags) {int gotsomewhere, terrno, v_circuit, resplen, n;ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;...if (resplen > 0) {if (cache_status == RESOLV_CACHE_NOTFOUND) {_resolv_cache_add(statp->netid, buf, buflen, ans, resplen);}return resplen;}...if (cache_status == RESOLV_CACHE_NOTFOUND) {_resolv_cache_add(statp->netid, buf, buflen, ans, resplen);}..._resolv_cache_query_failed(statp->netid, buf, buflen, flags);return -terrno;
}

调用_resolv_cache_add 将查询到的dns放入缓存

res_cache.cpp

// Head of the list of caches.
static struct resolv_cache_info res_cache_list GUARDED_BY(cache_mutex);typedef struct resolv_cache {int max_entries;int num_entries;Entry mru_list;int last_id;Entry* entries;struct pending_req_info {unsigned int hash;struct pending_req_info* next;} pending_requests;
} Cache;struct resolv_cache_info {unsigned netid;Cache* cache;struct resolv_cache_info* next;int nscount;char* nameservers[MAXNS];struct addrinfo* nsaddrinfo[MAXNS];int revision_id;  // # times the nameservers have been replacedres_params params;struct res_stats nsstats[MAXNS];char defdname[MAXDNSRCHPATH];int dnsrch_offset[MAXDNSRCH + 1];  // offsets into defdnameint wait_for_pending_req_timeout_count;
};

此cache 维护这一个resolv_cache_info结构的全局变量res_cache_list,所有的缓存都在其中,至此 我们通过相当繁琐的代码找到了我们想要得到的缓存区了。

整个过程的大概流程:

2.    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        开始 java层缓冲区寻找       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

上面提到lookupHostByName 我们接着看

/*** Resolves a hostname to its IP addresses using a cache.** @param host the hostname to resolve.* @param netId the network to perform resolution upon.* @return the IP addresses of the host.*/private static InetAddress[] lookupHostByName(String host, int netId)throws UnknownHostException {BlockGuard.getThreadPolicy().onNetwork();// Do we have a result cached?Object cachedResult = addressCache.get(host, netId);     // 这里就是个缓存了if (cachedResult != null) {if (cachedResult instanceof InetAddress[]) {// A cached positive result.return (InetAddress[]) cachedResult;} else {// A cached negative result.throw new UnknownHostException((String) cachedResult);}}try {StructAddrinfo hints = new StructAddrinfo();hints.ai_flags = AI_ADDRCONFIG;hints.ai_family = AF_UNSPEC;// If we don't specify a socket type, every address will appear twice, once// for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family// anyway, just pick one.hints.ai_socktype = SOCK_STREAM;InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);  // 这又是一条android_getaddrinfo的路径 基本和上面的流程一致// TODO: should getaddrinfo set the hostname of the InetAddresses it returns?for (InetAddress address : addresses) {address.holder().hostName = host;address.holder().originalHostName = host;}addressCache.put(host, netId, addresses);return addresses;} catch (GaiException gaiException) {// If the failure appears to have been a lack of INTERNET permission, throw a clear// SecurityException to aid in debugging this common mistake.// http://code.google.com/p/android/issues/detail?id=15722if (gaiException.getCause() instanceof ErrnoException) {int errno = ((ErrnoException) gaiException.getCause()).errno;if (errno == EACCES || errno == EPERM) {throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException);}}// Otherwise, throw an UnknownHostException.String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error);addressCache.putUnknownHost(host, netId, detailMessage);throw gaiException.rethrowAsUnknownHostException(detailMessage);}}

addressCache 是从android_getaddrinfo中获取的。 清除也很方便,直接发个广播就解决了。

三、如何清除DNS cache

知道了上面两个缓冲区,接下来就是处理流程了。废话不多说,直接看代码。   addressCache发送ACTION_CLEAR_DNS_CACHE广播解决。

/*** 清除InetAdress内的dns缓存*/
private void flushVmDnsCache() {/*¦* Tell the VMs to toss their DNS caches¦*/try {final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);/*¦* Connectivity events can happen before boot has completed ...¦*/intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);mContext.sendBroadcastAsUser(intent, UserHandle.ALL);} catch(Exception e) {e.printStackTrace();}
}

具体实现方法是AMS中对调用了mProcessList.clearAllDnsCacheLocked() 对每个进程都调用了clearDnsCache()

frameworks/base/services/core/java/com/android/server/am/ProcessList.java

    @GuardedBy("mService")void clearAllDnsCacheLocked() {for (int i = mLruProcesses.size() - 1; i >= 0; i--) {ProcessRecord r = mLruProcesses.get(i);if (r.thread != null) {try {r.thread.clearDnsCache();} catch (RemoteException ex) {Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);}}}}

frameworks/base/core/java/android/app/ActivityThread.java

public void clearDnsCache() {// a non-standard API to get this to libcoreInetAddress.clearDnsCache();// Allow libcore to perform the necessary actions as it sees fit upon a network// configuration change.NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
}

libcore/ojluni/src/main/java/java/net/InetAddress.java

public static void clearDnsCache() {impl.clearAddressCache();
}

然后调用实现类里面的clearAddressCache 就能清楚java层的缓存了

netd中在android10没有提供flushdns的接口了,那么我们就自己实现clearnds接口供调用。

commit a64728015d93f0aa9975f16df274af85f879f768
Author: b178903294
Date:   Fri Jul 16 17:03:56 2021 +0800[feature] add cleardns interface[what] 添加cleardns接口[why] 解决网络访问不及时生效的问题[how]nulldiff --git a/resolv/res_cache.cpp b/resolv/res_cache.cpp
index f0ff5641..cadcec07 100644
--- a/resolv/res_cache.cpp
+++ b/resolv/res_cache.cpp
@@ -1653,6 +1653,21 @@ void resolv_delete_cache_for_net(unsigned netid) {}}+void resolv_free_cache_for_net(unsigned netid) {
+    std::lock_guard guard(cache_mutex);
+
+    struct resolv_cache_info* prev_cache_info = &res_cache_list;
+
+    while (prev_cache_info->next) {                                          //  对所有的cache_info进行操作
+        struct resolv_cache_info* cache_info = prev_cache_info->next;
+        LOG(ERROR) << __func__ << " flush cache_info->netid:" << cache_info->netid << " targrt:" << netid;
+
+        cache_flush_locked(cache_info->cache);                               //  我们使用此函数进行flush cache 结构体。
+
+        prev_cache_info = prev_cache_info->next;
+    }
+}
+std::vector<unsigned> resolv_list_caches() {std::lock_guard guard(cache_mutex);struct resolv_cache_info* cache_info = res_cache_list.next;diff --git a/server/NdcDispatcher.cpp b/server/NdcDispatcher.cpp
index 7692a9c4..f6844ac0 100755
--- a/server/NdcDispatcher.cpp
+++ b/server/NdcDispatcher.cpp
@@ -1093,6 +1093,18 @@ int NdcDispatcher::NetworkCommand::runCommand(NdcClient* cli, int argc, char** areturn success(cli);}+    //    0        1       2
+    // network cleardns <netId>
+    if (!strcmp(argv[1], "cleardns")) {
+        if (argc != 3) {
+            return syntaxError(cli, "Incorrect number of arguments");
+        }
+        LOG(LOGLEVEL) << "call cleardns netId:" << netId;
+        unsigned netId = stringToNetId(argv[2]);
+        mDnsResolver->clearDnsCache(netId);
+        return success(cli);
+    }
+//    0       1      2      3// network default  set  <netId>// network default clear

调用ndc network cleardns netid就能清除netd中的缓存了,十分方便

安卓10平台DNS两层缓存相关推荐

  1. ORAN专题系列-28:5G基站如何升级到ORAN基站 - O-RU - 平台和传输层的改进(VLAN, PCP, DHCP, DNS)

    作者主页(文火冰糖的硅基工坊):https://blog.csdn.net/HiWangWenBing 本文网址:https://blog.csdn.net/HiWangWenBing/article ...

  2. [NewLife.XCode]数据层缓存(网站性能翻10倍)

    NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示例代码和 ...

  3. coloros11跟Android,安卓10与安卓11究竟差异在哪里?我们拿这两台新机试了一下

    9 月 24 日,OPPO 带着全新的 ColorOS 11 与我们正式见面,这一基于安卓 11 底层深度打造的新系统给我们带来了不少新特性.而就在 ColorOS 11 发布的前些天,华为也为我们带 ...

  4. 安卓10侧边返回_安卓 10 细节曝光,这两个功能更好用了

    据此前报道,谷歌摒弃以甜点命名系统版本的传统,将新的安卓系统命名为安卓 10.当然,在改名的同时,谷歌也对新的安卓系统做了不少优化. 其一,手势导航在安卓 10 中变得与 iOS 类似,上滑并按住松开 ...

  5. android10和11,安卓10与安卓11究竟差异在哪里?我们拿这两台新机试了一下

    原标题:安卓10与安卓11究竟差异在哪里?我们拿这两台新机试了一下 9 月 24 日,OPPO 带着全新的 ColorOS 11 与我们正式见面,这一基于安卓 11 底层深度打造的新系统给我们带来了不 ...

  6. 高通sdm660平台编译安卓10

    编译安卓10源码 在源码根目录执行 环境初始化 source build/envsetup.sh 可以使用lunch,查看当前有哪些板级编译选项 lunch 完整编译 ./build.sh dist ...

  7. 日志平台(网关层) - 基于Openresty+ELKF+Kafka

    背景介绍 1.问题现状与尝试 没有做日志记录的线上系统,绝对是给系统运维人员留下的坑.尤其是前后端分离的项目,后端的接口日志可以解决对接.测试和运维时的很多问题.之前项目上发布的接口都是通过Oracl ...

  8. 百度万亿流量的转发引擎BFE开源了!华为折叠屏曝光,采用传统翻盖式手机折叠方案;微软将关闭安卓iOS平台Cortana……...

    关注并标星星CSDN云计算 速递.最新.绝对有料.这里有企业新动.这里有业界要闻,打起十二分精神,紧跟fashion你可以的! 每周两次,打卡即read   更快.更全了解泛云圈精彩news   go ...

  9. [云计算]两层网络、三层网络的理解

    最近参与一个华为HCS云平台的规划设计,主要是辅助角色.过往的经验中,对于网络知识理论学得多,实践比较少,借此项目从新学习了相关的网络知识.本文转自两层网络.三层网络的理解_cj2580的博客-CSD ...

最新文章

  1. BZOJ.3227.[SDOI2008]红黑树tree(树形DP 思路)
  2. Oracle分页查询语句
  3. 如何添加任意一个UI component到SAP CRM的overview页面上
  4. 1062. Talent and Virtue (25)
  5. windows系统改装为linux系统_Linux怎么克隆系统?备份系统跟Windows系统有区别吗?...
  6. ifound Android wifi,方正新品记录仪iFound V1号称黑夜变白天,真的假的?
  7. 联发科推全新P系芯片 聚焦中端市场
  8. storm能不能测试wadl_测试网红燃油宝到底能不能除积碳,看完不花冤枉钱
  9. 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常
  10. 实例开发:ASP.NET创建网络相册
  11. 蓝桥杯2020年第十一届C++省赛第四题-蛇形填数
  12. Linux中vi命令替换字符串的操作
  13. 实现WEB打印的几种方法
  14. 我如何使用smartwatch传感器限制covid 19感染
  15. NYOJ 无主之地1
  16. photoshop标尺工具_如何在Photoshop中使用和掌握非常困难的钢笔工具
  17. 怎么将word2016的背景色改成护眼绿?
  18. MySQL学习记录04where条件子句、联表查询、子查询
  19. Atom 打造无懈可击的 Markdown 编辑器
  20. 洛谷刷题C语言:Bold、饱食、公平の意、DOM、

热门文章

  1. Java工程师笔试题整理[校招篇]
  2. 普通大学生的大学三年程序生活
  3. 【西洋鬼怪】哈利波特与死圣中“圣物”的猜测
  4. MySqL的sql语句优化常用的8种方式
  5. PYTHONPATH 是什么
  6. Vue中computed与watched
  7. IDEA启动tomcat 端口1099被占用(1099 is already in use)
  8. 【论文笔记】Fast and Furious: Real Time End-to-End 3D Detection, Tracking and Motion Forecasting
  9. Jquery的鼠标移入移出事件
  10. HTTP参数污染(HPP)漏洞