一、 背景
当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 host
return impl.lookupAllHostAddr(host, NETID_UNSET).clone();
}
接着在实现类里面:
@Override
public 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 INET6
case 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))
;
else
flags |= 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 INET6
case AF_INET6:
{
int error;
if ((error = ip6_parsenumeric(sa, addr, host,
hostlen, flags)) != 0)
return(error);
break;
}
#endif
default:
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';
}
#endif
if (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 INET6
case AF_INET6:
{
int error;
if ((error = ip6_parsenumeric(sa, addr, host,
hostlen,
flags)) != 0)
return(error);
break;
}
#endif
default:
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 IPv6
const 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( // 尝试连接socket
connectFunc(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 dnsproxyd
int 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 supported
if ((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 query
struct 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 space
memcpy(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 replaced
res_params params;
struct res_stats nsstats[MAXNS];
char defdname[MAXDNSRCHPATH];
int dnsrch_offset[MAXDNSRCH + 1]; // offsets into defdname
int 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=15722
if (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 libcore
InetAddress.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]null
diff --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** a
return 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缓存问题,详细解析了从java层到native层的DNS查询过程,揭示了DNS缓存存在于系统层面和应用层,并给出了清除DNS缓存的方法,包括清理java层的addressCache和通过ndc命令清除netd服务中的DNS缓存。

3486

被折叠的 条评论
为什么被折叠?



