一、 背景
当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