【Android】(十三) DNS-Resolver

Dns-Reslover 初始化

init 进程启动后,解析 init.rc,启动了 netd

//system/netd/server/main.cpp
bool initDnsResolver() {
    ResolverNetdCallbacks callbacks = {
        .check_calling_permission = &checkCallingPermissionCallback,
        .get_network_context = &getNetworkContextCallback,
        .log = &logCallback,
        .tagSocket = &tagSocketCallback,
        .evaluate_domain_name = &evaluateDomainNameCallback,
    };
    return resolv_init(&callbacks);
}

int main(){
    //初始化配置,初始化必要的类
    if (!initDnsResolver()) {
        ALOGE("Unable to init resolver");
        exit(1);
    }
    //注册,启动MDnsService,NetdNativeService,NetdHwService等服务,生存Binder对象供客户调用
}

netd 启动过程中初始化了 Dns-Reslover。初始化过程调用了 initReslover,构造了解析器的回调函数,调用 resolv_init 具体进行初始化。

bool resolv_init(const ResolverNetdCallbacks* callbacks) {
    //..
    android::net::gDnsResolv = android::net::DnsResolver::getInstance();
    return android::net::gDnsResolv->start();
}

resolv_init 中初始化 Dns 日志系统配置,设置解析器回调函数。接着准备启动Dns-Reslover服务。

Dns-Reslover 启动

//packages/modules/DnsResolver/DnsResolver.cpp
bool DnsResolver::start() {
    if (!verifyCallbacks()) {
        LOG(ERROR) << __func__ << ": Callback verification failed";
        return false;
    }
    if (mDnsProxyListener.startListener()) {
        PLOG(ERROR) << __func__ << ": Unable to start DnsProxyListener";
        return false;
    }
    binder_status_t ret;
    if ((ret = DnsResolverService::start()) != STATUS_OK) {
        LOG(ERROR) << __func__ << ": Unable to start DnsResolverService: " << ret;
        return false;
    }
    return true;
}

DnsReslover 启动分为三步,首先检查回调函数是否有效,然后启动 DnsProxyListener 监听Dns解析请求,然后启动DnsReslover 服务。

DnsProxyListener 启动

//system/core/libsysutils/src/SocketListener.cpp
int SocketListener::startListener(int backlog) {
    
    if (mListen && listen(mSock, backlog) < 0) {//mListen==true
        SLOGE("Unable to listen on socket (%s)", strerror(errno));
        return -1;
    } else if (!mListen)
        mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
    
    if (pipe2(mCtrlPipe, O_CLOEXEC)) {
        SLOGE("pipe failed (%s)", strerror(errno));
        return -1;
    }

    if (pthread_create(&mThread, nullptr, SocketListener::threadStart, this)) {
        SLOGE("pthread_create (%s)", strerror(errno));
        return -1;
    }
    return 0;
}

创建一个监听 socket,创建一个线程间的通信管道。创建一个监听线程,专门处理解析请求。

//system/core/libsysutils/src/SocketListener.cpp
void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);

    me->runListener();
    pthread_exit(nullptr);
    return nullptr;
}

void SocketListener::runListener() {
    while (true) {
        //...
        for (SocketClient* c : pending) {
            // Process it, if false is returned, remove from the map
            SLOGV("processing fd %d", c->getSocket());
            if (!onDataAvailable(c)) {
                release(c, false);
            }
            c->decRef();
        }
    }
}

线程中处理监听线程,管道和 client 上的所有事件,将有数据到达的 client 挂在一个链表上,依次处理到达的数据。

DnsResloverService启动

//packages/modules/DnsResolver/DnsResolverService.cpp
binder_status_t DnsResolverService::start() {
    std::shared_ptr<DnsResolverService> resolverService =
            ::ndk::SharedRefBase::make<DnsResolverService>();
    binder_status_t status =
            AServiceManager_addService(resolverService->asBinder().get(), getServiceName());
    if (status != STATUS_OK) {
        return status;
    }
    ABinderProcess_startThreadPool();
    return STATUS_OK;
}

服务注册到 Binder 服务管理中,可供其他进程调用。启动 Binder 进程的线程池,以便处理来自客户端的请求。

Dns 查询

1 来自上层的DNS请求

1 Getaddrinfo

1.1 下发请求

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//libcore/ojluni/src/main/java/java/net/InetAddress.java
public static InetAddress getByName(String host)
    throws UnknownHostException {
    // Android-changed: Rewritten on the top of Libcore.os.
    return impl.lookupAllHostAddr(host, NETID_UNSET)[0];
}

程序调用 InetAddress 类中的 getByName 方法来获取一个主机名的 IP 地址。getByName 内部调用到Inet6AddressImpl 中的 lookupAllHostAddr 方法。InetAdress类中还有一些其他方法内部都调用了 Inet6AddressImpl.lookupAllHostAddr,这些方法都是以下的流程。

//libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java
public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException {
    //如果已经是IP地址,直接return
    //...
    return lookupHostByName(host, netId);
}

private static InetAddress[] lookupHostByName(String host, int netId)
        throws UnknownHostException {
    //...
    StructAddrinfo hints = new StructAddrinfo();
    hints.ai_flags = AI_ADDRCONFIG;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);
    //...
    return addresses;
}

如果已经是IP地址,直接 return。接下来去cahce中查找,找不到再调用到 Libcore.os 中的 android_getaddrinfo JNI 方法。Libcore 中的所有的 JNI 方法定义在这个文件中:libcore/luni/src/main/java/libcore/io/Linux.java 。实现如下:

//libcore/luni/src/main/native/libcore_io_Linux.cpp
static jobjectArray Linux_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode,
        jobject javaHints, jint netId) {
    ScopedUtfChars node(env, javaNode);
    //...
    addrinfo* addressList = NULL;
    errno = 0;
    int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
    //...
    // Prepare output array.
}

其中调用了 android_getaddrinfofornet 方法,实现 Dns 解析。

//bionic/libc/dns/net/getaddrinfo.c
int android_getaddrinfofornet(const char *hostname, const char *servname,
    const struct addrinfo *hints, unsigned netid, unsigned mark, struct addrinfo **res)
{
        struct android_net_context netcontext = {
                .app_netid = netid,
                .app_mark = mark,
                .dns_netid = netid,
                .dns_mark = mark,
                .uid = NET_CONTEXT_INVALID_UID,
        };
        return android_getaddrinfofornetcontext(hostname, servname, hints, &netcontext, res);
}

int android_getaddrinfofornetcontext(const char *hostname, const char *servname,
    const struct addrinfo *hints, const struct android_net_context *netcontext,
    struct addrinfo **res)
{
#if defined(__ANDROID__)
    int gai_error = android_getaddrinfo_proxy(
            hostname, servname, hints, res, netcontext->app_netid);
    if (gai_error != EAI_SYSTEM) {
            return gai_error;
    }
#endif
    //...
}

android_getaddrinfofornet 调用android_getaddrinfofornetcontext,而android_getaddrinfofornetcontext调用了 android_getaddrinfo_proxy 去连接到 DnsProxyListener ,并构造一个 getaddrinfo 命令下发。接下来具体看一下 android_getaddrinfo_proxy。

//bionic/libc/dns/net/getaddrinfo.c
static int android_getaddrinfo_proxy(
    const char *hostname, const char *servname,
    const struct addrinfo *hints, struct addrinfo **res, unsigned netid)
{
        //...
        FILE* proxy = fdopen(__netdClientDispatch.dnsOpenProxy(), "r+");
        if (proxy == NULL) {
                return EAI_SYSTEM;
        }
        netid = __netdClientDispatch.netIdForResolv(netid);

        // Send the request.
        if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d %u",
                    hostname == NULL ? "^" : hostname,
                    servname == NULL ? "^" : servname,
                    hints == NULL ? -1 : hints->ai_flags,
                    hints == NULL ? -1 : hints->ai_family,
                    hints == NULL ? -1 : hints->ai_socktype,
                    hints == NULL ? -1 : hints->ai_protocol,
                    netid) < 0) {
                goto exit;
        }
        //...
        //receive result
}

主要是在构造命令,连接DnsProxyListener,发送命令并接受请求。再看一下 __netdClientDispatch.dnsOpenProxy的实现

//bionic/libc/bionic/NetdClient.cpp
template <typename FunctionType>
static void netdClientInitFunction(void* handle, const char* symbol, FunctionType* function) {
    typedef void (*InitFunctionType)(FunctionType*);
    InitFunctionType initFunction = reinterpret_cast<InitFunctionType>(dlsym(handle, symbol));
    if (initFunction != nullptr) {
        initFunction(function);
    }
}

static void netdClientInitImpl() {
    //...
    void* handle = dlopen("libnetd_client.so", RTLD_NOW);
    //...Init其他函数
    netdClientInitFunction(handle, "netdClientInitDnsOpenProxy",
                           &__netdClientDispatch.dnsOpenProxy);
}

dlopen打开的libnetd_client.so定义在netd进程中:

//system/netd/client/Android.bp
cc_library {
    name: "libnetd_client",
    //...
}

dlsym函数获取了libnetd_client.so中的netdClientInitDnsOpenProxy函数地址,其代码实现如下所示:

extern "C" void netdClientInitDnsOpenProxy(DnsOpenProxyType* function) {
    if (function) {
        *function = dns_open_proxy;
    }
}

将 NetdClient.dns_open_proxy 的地址赋给 __netdClientDispatch.dnsOpenProxy 。

int dns_open_proxy() {
    ....
    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",
    };

    const auto connectFunc = libcConnect ? libcConnect : connect;
    if (TEMP_FAILURE_RETRY(
                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;
}

作为客户端打开 /dev/socket/dnsproxyd 并连接。通过这个 Unix 域套接字,上层和 netd 之间通信,实现 DNS 解析。

1.2 处理请求

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//system/core/libsysutils/src/SocketListener.cpp
void SocketListener::runListener() {
    while (true) {
        //...
        for (SocketClient* c : pending) {
            // Process it, if false is returned, remove from the map
            SLOGV("processing fd %d", c->getSocket());
            if (!onDataAvailable(c)) {
                release(c, false);
            }
            c->decRef();
        }
    }
}

DnsProxyListener 监听到来自客户的 Dns 解析请求,于是调用 onDataAvailable 来处理客户数据。

//system/core/libsysutils/src/FrameworkListener.cpp
bool FrameworkListener::onDataAvailable(SocketClient *c) {
    char buffer[CMD_BUF_SIZE];
    int len;
    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
    //...
    for (i = 0; i < len; i++) {
        if (buffer[i] == '\0') {
            //...
            dispatchCommand(c, buffer + offset);
            offset = i + 1;
        }
    }
    //...
}

读取客户数据,及从上层送下来的 Dns 解析命令,处理这些命令字符串。

//system/core/libsysutils/src/FrameworkListener.cpp
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
    int argc = 0;
    char *argv[FrameworkListener::CMD_ARGS_MAX];
    //
    while(*p) {
        //处理字符串
    }                                                                                                                                                                                     90,1          53%
    *q = '\0';
    if (argc >= CMD_ARGS_MAX)
        goto overflow;
    argv[argc++] = strdup(tmp);
    //...
    for (auto* c : mCommands) {
        if (!strcmp(argv[0], c->getCommand())) {
            if (c->runCommand(cli, argc, argv)) {
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
            }
        }
    }
    //...
}
               

找到注册过的命令中跟此命令相匹配的一个,此处是 GetAddrInfoCmd ,执行。

//packages/modules/DnsResolver/DnsProxyListener.cpp
int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient* cli, int argc, char** argv) {
    logArguments(argc, argv);
    //...
    (new GetAddrInfoHandler(cli, name, service, move(hints), netcontext))->spawn();
    return 0;
}

解析命令参数,设置 getaddrinfo 的相关参数,创建一个 handler 线程,专门处理 Dns 解析。

//packages/modules/DnsResolver/DnsProxyListener.cpp
void DnsProxyListener::GetAddrInfoHandler::run() {
    //...
    if (queryLimiter.start(uid)) {
        const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str();
        const char* service = mService.starts_with('^') ? nullptr : mService.c_str();
        if (evaluate_domain_name(mNetContext, host)) {
            rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, &result, &event);
        } else {
            rv = EAI_SYSTEM;
        }
        queryLimiter.finish(uid);
    } else {
        rv = EAI_MEMORY;
        LOG(ERROR) << "GetAddrInfoHandler::run: from UID " << uid
                   << ", max concurrent queries reached";
    }

    doDns64Synthesis(&rv, &result, &event);
    //...
    bool success = true;
    if (rv) {
        success = !mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
    } else {
        success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
        addrinfo* ai = result;
        while (ai && success) {
            success = sendBE32(mClient, 1) && sendaddrinfo(mClient, ai);
            ai = ai->ai_next;
        }
        success = success && sendBE32(mClient, 0);
    }
    //...
    freeaddrinfo(result);
}

以上代码解析 IP 地址,并保存在 result 中,解析结果可能涉及到转换成 IPv4。将解析到的结果发送给 Client。具体的解析函数为 resolv_getaddrinfo。

//packages/modules/DnsResolver/getaddrinfo.cpp
int resolv_getaddrinfo(const char* _Nonnull hostname, const char* servname, const addrinfo* hints,
                       const android_net_context* _Nonnull netcontext, addrinfo** _Nonnull res,
                       NetworkDnsEventReported* _Nonnull event) {
    //...
    addrinfo ai = hints ? *hints : addrinfo{};
    addrinfo sentinel = {};
    addrinfo* cur = &sentinel;
    for (const Explore& ex : explore_options) {
        //...
        addrinfo tmp = ai;
        //..
        error = explore_fqdn(&tmp, hostname, servname, &cur->ai_next, netcontext, event);
        while (cur->ai_next) cur = cur->ai_next;
    }
    //...
    if ((*res = sentinel.ai_next)) return 0;
    //...
}

根据不同的解析选项解析出所有的 addr 并返回。

//packages/modules/DnsResolver/getaddrinfo.cpp
static int explore_fqdn(const addrinfo* pai, const char* hostname, const char* servname,
                        addrinfo** res, const android_net_context* netcontext,
                        NetworkDnsEventReported* event) {
    //...
    if ((error = get_portmatch(pai, servname))) return error;

    if (!files_getaddrinfo(netcontext->dns_netid, hostname, pai, &result)) {
        error = dns_getaddrinfo(hostname, pai, netcontext, &result, event);
    }
    //...
    for (addrinfo* cur = result; cur; cur = cur->ai_next) {
        // canonname should be filled already
        if ((error = get_port(cur, servname, 0))) {
            freeaddrinfo(result);
            return error;
        }
    }
    *res = result;
    return 0;
}

从文件(files_getaddrinfo)或者Dns服务器(dns_getaddrinfo)中获取域名解析结果,并填充端口信息。

int res_nsend(res_state statp, const uint8_t* buf, int buflen, uint8_t* ans, int anssiz, int* rcode,
              uint32_t flags, std::chrono::milliseconds sleepTimeMs) {
    //...
    ResolvCacheStatus cache_status =
            resolv_cache_lookup(statp->netid, buf, buflen, ans, anssiz, &anslen, flags);
    const int32_t cacheLatencyUs = saturate_cast<int32_t>(cacheStopwatch.timeTakenUs());
    if (cache_status == RESOLV_CACHE_FOUND) {
        //cache里成功找到,return
    } else if (cache_status != RESOLV_CACHE_UNSUPPORTED) {
        //配置resolver
    }
    // DoT
    if (!(statp->netcontext_flags & NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS)) {
        bool fallback = false;                                                                                                                                                                               420,5         33%
        int resplen = res_tls_send(statp, Slice(const_cast<uint8_t*>(buf), buflen),
                                   Slice(ans, anssiz), rcode, &fallback);
        //return
    }
    //...
    for (int attempt = 0; attempt < retryTimes; ++attempt) {
        for (size_t ns = 0; ns < statp->nsaddrs.size(); ++ns) {
            if (!usable_servers[ns]) continue;
            if (useTcp) {
                // TCP; at most one attempt per server.
                attempt = retryTimes;
                resplen = send_vc(statp, &params, buf, buflen, ans, anssiz, &terrno, ns,
                                  &query_time, rcode, &delay);
            } else {
                // UDP
                resplen = send_dg(statp, &params, buf, buflen, ans, anssiz, &terrno, &actualNs,
                                  &useTcp, &gotsomewhere, &query_time, rcode, &delay);
                //...
            }
            //...
            if (cache_status == RESOLV_CACHE_NOTFOUND) {
                resolv_cache_add(statp->netid, buf, buflen, ans, resplen);
            }
            statp->closeSockets();
            return (resplen);
        }
    }
    //...
    return -terrno;
}

dns_getaddrinfo 一直调用到 res_nsend,首先查找 cache,如果没有的话就正式向配置好的一些Dns服务器去请求Dns解析。首先从 resoler_state 中查找到可用的服务器信息,然后依次向这些服务器发起 Dns 请求。可以使用 TCP 或者 UDP。

2 Resnsend

2.1 下发请求
//packages/modules/Connectivity/framework/src/android/net/DnsResolver.java
public void query(@Nullable Network network, @NonNull String domain,
        @QueryType int nsType, @QueryFlag int flags,
        @NonNull @CallbackExecutor Executor executor,
        @Nullable CancellationSignal cancellationSignal,
        @NonNull Callback<? super List<InetAddress>> callback) {
    //...
    queryNetwork = (network != null) ? network : getDnsNetwork();
    queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
            flags);
    //...
}

应用调用 DnsResolver. query 方法开始一次 Dns 解析过程。调用 JNI 接口 resNetworkQuery 向下层发送Dns解析请求。

//packages/modules/Connectivity/framework/src/android/net/NetworkUtils.java
private static native FileDescriptor resNetworkQuery(
            long netHandle, String dname, int nsClass, int nsType, int flags) throws ErrnoException;

//packages/modules/Connectivity/framework/jni/android_net_NetworkUtils.cpp
static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jlong netHandle,
        jstring dname, jint ns_class, jint ns_type, jint flags) {
    //...
    int fd = android_res_nquery(netHandle, queryname, ns_class, ns_type, flags);
    //...

    return jniCreateFileDescriptor(env, fd);
}

//frameworks/base/native/android/net.c
int android_res_nquery(net_handle_t network, const char *dname,
        int ns_class, int ns_type, enum ResNsendFlags flags) {
    //...
    return resNetworkQuery(netid, dname, ns_class, ns_type, flags);
}

//system/netd/client/NetdClient.cpp
extern "C" int resNetworkQuery(unsigned netId, const char* dname, int ns_class, int ns_type,
                               uint32_t flags) {
    std::vector<uint8_t> buf(MAX_CMD_SIZE, 0);
    int len = res_mkquery(ns_o_query, dname, ns_class, ns_type, nullptr, 0, nullptr, buf.data(),
                          MAX_CMD_SIZE);
    return resNetworkSend(netId, buf.data(), len, flags);
}

经过一系列调用,最终调用到 resNetworkQuery 函数。在其中根据参数构造了 query 请求。

//system/netd/client/NetdClient.cpp
extern "C" int resNetworkSend(unsigned netId, const uint8_t* msg, size_t msglen, uint32_t flags) {
    // Encode
    netId = getNetworkForResolv(netId);
    const std::string cmd = "resnsend " + std::to_string(netId) + " " + std::to_string(flags) +
                            " " + encodedQuery + '\0';
    //error process
    int fd = dns_open_proxy();
    //error process
    ssize_t rc = sendData(fd, cmd.c_str(), cmd.size());
    //error process
    shutdown(fd, SHUT_WR);
    return fd;
}

resNetworkQuery 调用了 resNetworkSend 完成了最终的 query 请求的发送。先将 query 构造成一个下发的命令,接着连接 DnsProxyLIstener,然后将 cmd 发送到 DnsProxyListener ,由 netd 来处理这个命令。

2.2 处理请求

与 getaddrinfo 大致类似,只不过 ResnsendCmd 调用了 resolv_resnend,而这个函数直接调用 res_nsend。

3 gethostbyaddr

3.1 下发请求
//libcore/ojluni/src/main/java/java/net/InetAddress.java
public String getHostName() {
    // Android-changed: Remove SecurityManager check.
    if (holder().getHostName() == null) {
        holder().hostName = InetAddress.getHostFromNameService(this);
    }
    return holder().getHostName();
}

private static String getHostFromNameService(InetAddress addr) {
    String host = null;
    host = nameService.getHostByAddr(addr.getAddress());
    //...

    return host;
}

private static final NameService nameService = new NameService() {
    public InetAddress[] lookupAllHostAddr(String host, int netId)
            throws UnknownHostException {
        return impl.lookupAllHostAddr(host, netId);
    }
    public String getHostByAddr(byte[] addr)
            throws UnknownHostException {
        return impl.getHostByAddr(addr);
    }
};

程序调用 InetAddress 类中的 getHostName 方法来获取一个 IP 地址对应的主机名。getHostName 内部调用到Inet6AddressImpl 中的 getHostByAddr 方法。InetAdress类中还有一些其他方法内部都调用了 Inet6AddressImpl.getHostByAddr,这些方法都是以下的流程。

//libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java
public String getHostByAddr(byte[] addr) throws UnknownHostException {
    BlockGuard.getThreadPolicy().onNetwork();

    return getHostByAddr0(addr);
}

private String getHostByAddr0(byte[] addr) throws UnknownHostException {
    InetAddress hostaddr = InetAddress.getByAddress(addr);
    try {
        return Libcore.os.getnameinfo(hostaddr, NI_NAMEREQD);
    } catch (GaiException e) {
        UnknownHostException uhe = new UnknownHostException(hostaddr.toString());
        uhe.initCause(e);
        throw uhe;
    }
}

其中再次调用到 Libcore.os 中的 getnameinfo JNI 方法。Libcore 中的所有的 JNI 方法定义在这个文件中:libcore/luni/src/main/java/libcore/io/Linux.java 。实现如下:

//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);
}

//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://从sa中读就行
                return getnameinfo_local(sa, salen, host, hostlen,
                    serv, servlen, flags);
        default:
                return EAI_FAMILY;
        }
}

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 android_net_context netcontext = { .app_netid = netid, .app_mark = mark };
    hp = android_gethostbyaddrfornetcontext_proxy(addr, afd->a_addrlen, afd->a_af, &netcontext);
    //...
}

一直调用到 getnameinfo_inet 函数,真正去发送 gethostbyaddr 的请求。android_gethostbyaddrfornetcontext_proxy 向 DnsProxyListener 发送请求并返回一个 host 信息的结构体。

//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+");
        //...
        //send
        if (fprintf(proxy, "gethostbyaddr %s %d %d %u",
                        addrStr, len, af, netid) < 0) {
                fclose(proxy);
                return NULL;
        }
        //...
        ///receive
        struct hostent *result = android_read_hostent(proxy, hp, hbuf, hbuflen, he);
        fclose(proxy);
        return result;
}

android_gethostbyaddrfornetcontext_proxy 中调用到 android_gethostbyaddrfornetcontext_proxy_internal。android_gethostbyaddrfornetcontext_proxy_internal 中连接到 DnsProxyListener,并通过 printf

发送了一个 gethostbyaddr 命令。

3.2 处理请求
//packages/modules/DnsResolver/gethnamaddr.cpp
int resolv_gethostbyaddr(const void* addr, socklen_t len, int af, hostent* hp, char* buf,
                         size_t buflen, const struct android_net_context* netcontext,
                         hostent** result, NetworkDnsEventReported* event) {
    //...
    if (_hf_gethtbyaddr(uaddr, len, af, &info)) {
        int error = dns_gethtbyaddr(uaddr, len, af, netcontext, &info, event);
        if (error != 0) return error;
    }
    *result = hp;
    return 0;
}
//packages/modules/DnsResolver/gethnamaddr.cpp
static int dns_gethtbyaddr(const unsigned char* uaddr, int len, int af,
                           const android_net_context* netcontext, getnamaddr* info,
                           NetworkDnsEventReported* event) {
    //...
    n = res_nquery(&res, qbuf, C_IN, T_PTR, buf->buf, (int)sizeof(buf->buf), &he);
}
//packages/modules/DnsResolver/res_query.cpp
int res_nquery(res_state statp, const char* name,  // domain name
               int cl, int type,                   // class and type of query
               uint8_t* 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_nmkquery(QUERY, name, cl, type, /*data=*/nullptr, 0, buf, sizeof(buf),
                     statp->netcontext_flags);
    //...
    n = res_nsend(statp, buf, n, answer, anslen, &rcode, 0);

GethostbyaddrCmd 中调用了 resolv_gethostbyaddr,其中先在本地文件解析,然后使用Dns解析。在Dns解析过程中还是使用了 res_nsend。接下来流程都是一样的。

2 作为AP,接受STA的dns请求

Tethering 进入 TetherModeAliveState 状态是 enter 中 调用了 turnOnMainTetherSettings。

//packages/modules/Connectivity/Tethering/src/com/android/networkstack/tethering/Tethering.java
protected boolean turnOnMainTetherSettings() {
    final TetheringConfiguration cfg = mConfig;
    try {
        mNetd.ipfwdEnableForwarding(TAG);
    } catch (RemoteException | ServiceSpecificException e) {
        mLog.e(e);
        transitionTo(mSetIpForwardingEnabledErrorState);
        return false;
    }

    // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
    // Legacy DHCP server is disabled if passed an empty ranges array
    final String[] dhcpRanges = cfg.enableLegacyDhcpServer
            ? cfg.legacyDhcpRanges : new String[0];
    try {
        NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
    } catch (RemoteException | ServiceSpecificException e) {
        try {
            // Stop and retry.
            mNetd.tetherStop();
            NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
        } catch (RemoteException | ServiceSpecificException ee) {
            mLog.e(ee);
            transitionTo(mStartTetheringErrorState);
            return false;
        }
    }
    mLog.log("SET main tether settings: ON");
    return true;
}

调用 NetdUtils 中的 tetherStart 方法,传入参数 usingLegacyDnsProxy = true,这里的意思是通过 Dnsmasq 来处理Dns 请求,而 dhcpRanges = null,所以不使用 Dnsmasq 来处理 DHCP 请求。tetherStart 中调用到了 Netd 服务中的 tetherStartWithConfiguration 方法。tetherStartWithConfiguration 的具体实现是 Netd 内部的 tetherController 来实现的。其中开启了 Dnsmasq 进程用以处理 DNS 请求。

3 res_nsend 具体实现

DnsPacket

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

http://c.biancheng.net/view/6457.html

Dns配置获得

1 来自上层的DNS请求

ConnectivityService内部有两个Handler:NetworkStateTrackerHandlerInternalHandler

1.InternalHandler

收到**EVENT_PRIVATE_DNS_SETTINGS_CHANGED消息,调用handlePrivateDnsSettingsChanged**

->handlePerNetworkPrivateDnsConfig->updatePrivateDns->updateDnses

onNetworkMonitorCreated被触发后收到EVENT_REGISTER_NETWORK_AGENT消息,调用handleRegisterNetworkAgent->updateNetworkInfo

->handlePerNetworkPrivateDnsConfig->updatePrivateDns->updateDnses

onPrivateDnsValidationEvent被触发收到EVENT_PRIVATE_DNS_VALIDATION_UPDATE消息,调用handlePrivateDnsValidationUpdate****->handleUpdateLinkProperties->updateLinkProperties->updateDnses

2.NetworkStateTrackerHandler

收到***NetworkAgent.EVENT_NETWORK_INFO_CHANGED消息,调用maybeHandleNetworkAgentMessage***

->updateNetworkInfo->handlePerNetworkPrivateDnsConfig->updatePrivateDns->updateDnses

收到**EVENT_PRIVATE_DNS_CONFIG_RESOLVED消息,maybeHandleNetworkMonitorMessage**

->****updatePrivateDns->updateDnses

收到**NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGEmaybeHandleNetworkAgentMessage**

->handleUpdateLinkProperties->updateLinkProperties->updateDnses

收到**EVENT_CAPPORT_DATA_CHANGED消息,调用maybeHandleNetworkMonitorMessage->** **handleCapportApiDataUpdate->handleUpdateLinkProperties->updateLinkProperties->updateDnses**

2 来自其他设备的DNS请求

//packages/modules/Connectivity/Tethering/src/com/android/networkstack/tethering/Tethering.java
public void enter() {
    //...
    if (upstreamWanted()) {
        mUpstreamWanted = true;
        mOffload.start();
        chooseUpstreamType(true);
        mTryCell = false;
    }
    //...
}

之前的流程与 NAT 部分相同,调用到 chooseUpstreamType。经过一系列调用,最终调用到 setDnsForwarders。

//packages/modules/Connectivity/Tethering/src/com/android/networkstack/tethering/Tethering.java
protected void setDnsForwarders(final Network network, final LinkProperties lp) {
    // TODO: Set v4 and/or v6 DNS per available connectivity.
    final Collection<InetAddress> dnses = lp.getDnsServers();
    // TODO: Properly support the absence of DNS servers.
    final String[] dnsServers;
    if (dnses != null && !dnses.isEmpty()) {
        dnsServers = new String[dnses.size()];
        int i = 0;
        for (InetAddress dns : dnses) {
            dnsServers[i++] = dns.getHostAddress();
        }
    } else {
        dnsServers = mConfig.defaultIPv4DNS;
    }
    final int netId = (network != null) ? network.getNetId() : NETID_UNSET;
    try {
        mNetd.tetherDnsSet(netId, dnsServers);
        mLog.log(String.format(
                "SET DNS forwarders: network=%s dnsServers=%s",
                network, Arrays.toString(dnsServers)));
    } catch (RemoteException | ServiceSpecificException e) {
        // TODO: Investigate how this can fail and what exactly
        // happens if/when such failures occur.
        mLog.e("setting DNS forwarders failed, " + e);
        transitionTo(mSetDnsForwardersErrorState);
    }
}

其中调用了 Netd服务的 tetherDnsSet 方法,进行Dns的配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值