安卓10平台DNS两层缓存

本文探讨了安卓10平台上的DNS缓存问题,详细解析了从java层到native层的DNS查询过程,揭示了DNS缓存存在于系统层面和应用层,并给出了清除DNS缓存的方法,包括清理java层的addressCache和通过ndc命令清除netd服务中的DNS缓存。

一、 背景

当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中的缓存了,十分方便

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值