原文地址:http://blog.csdn.net/insswer/article/details/17382535
1. Change of Android4.3
在Android4.3以前,如果系統需要備份/恢復,防火牆以及DNS解析管理,Linux內核微調等,是需要ROOT權限才能進行的。在Android4.3中,Google修改了這一策略,Google向用戶提供API和擴展來完成這些事情。其中DNS解析就是這一改變中的一環。
Bionic是Android自己的C庫版本。
在早期版本的Android中,DNS解析的方式類似於Ubuntu等發行版Linux。都是通過resovl.conf文件進行域名解析的。在老版本Android的bionic/libc/docs/overview.txt中可以看到,Android的DNS也是採用NetBSD-derived resolver library來實現,不同的是,bionic對其進行了一些修改。這些修改包括:
1. resovle.conf文件的位置不再是/etc/resolv.conf,在Android中改爲了/system/etc/resolv.conf。
2. 從系統屬性(SystemProperties)中讀取DNS服務器,比如“net.dns1”,“net.dns2”等。每一個屬性必須包括了DNS服務器的IP地址。
3. 不實現Name ServiceSwitch。
4. 在查詢時,使用一個隨機的查詢ID,而非每次自增1.
5. 在查詢時,將本地客戶端的socket綁定到一個隨機端口以增強安全性。
我們從下面小例子開始分析公共流程中DNS解析所經過的函數,對於Android中JNI和JAVA等層次概念請參考最開始的那一張結構圖:
- //獲得www.taobao.com對應的IP地址,並通過Toast的方式打印出來
- try {
- InetAddress inetAddress = InetAddress.getByName("www.taobao.com");
- Toast.makeText(MainActivity.this, "Address is " + inetAddress.getHostAddress(), Toast.LENGTH_LONG).show(); } catch (UnknownHostException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
以上Java代碼給出了最簡單的一次DNS解析的方法。主要實現是調用InetAddress類的靜態方法getByName,該方法返回一個InetAddress實例,該實例中包括很多關於域名的信息。
- public static InetAddress getByName(String host) throws UnknownHostException {
- return getAllByNameImpl(host)[0];
- }
實際調用getAllByNameImpl函數。該函數內部主要進行三件事情,第一件,如果host是null,那麼調用loopbackAddresses()。如果host是數字形式的地址,那麼調用parseNumericAddressNoThrow解析並返回。如果是一個字符串,則使用lookupHostByName(host)返回一個InetAddress並clone一份返回。
lookupHostByName函數首先host的信息是否存在在緩存當中,如果有則返回。如果沒有則:
- InetAddress[] addresses = Libcore.os.getaddrinfo(host, hints);
getaddrinfo函數是一個native本地函數,聲明如下:
- public native InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException;
在getaddrinfo對應的JNI層函數中,實際調用了下面函數:
- int rc = getaddrinfo(node.c_str(), NULL, &hints, &addressList);
getaddrinfo實現自bionic的netbsd庫,具體文件位於/bionic/libc/netbsd/net中,後面我們會分析Android4.2和Android4.3的代碼,來觀察Google在Android4.3中對DNS解析做了什麼樣的修改。
除了getaddrinfo路徑以外,在Java中InetAddress還有其他方式,比如
- public String getHostName() {
- if (hostname == null) {
- try {
- hostname = getHostByAddrImpl(this).hostName;
- } catch (UnknownHostException ex) {
- hostname = getHostAddress();
- }
- }
- return hostname;
- }
上述方法,調用了getHostByAddrImpl,在getHostByAddrImpl中:
- String hostname = Libcore.os.getnameinfo(address, NI_NAMEREQD);
調用了getnameinfo方法,該方法同樣是一個native函數,在JNI層對應的函數中直接調用了getnameinfo這個bionic庫的函數:
- int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), size, buf, sizeof(buf), NULL, 0, flags);
4. Android4.2和Android4.3 bionic中DNS解析實現的變化
不管是getaddrinfo還是getnameinfo還是gethostbyname,都是實現在bionic庫中,這裏先以getaddrinfo爲例分析Android4.3前後bionic在DNS解析處通用邏輯的變化。先從4.3以前版本開始。
在getaddrinfo中,關鍵的一步如下:
- /*
- * BEGIN ANDROID CHANGES; proxying to the cache
- */
- if (android_getaddrinfo_proxy(hostname, servname, hints, res) == 0) {
- return 0;
- }
注意上面的註釋,ANDROID_CHANGES,Google在Android4.2.2開始已經打算將所有DNS解析的方式向Netd代理的方式過渡了。後面我們還會看到ANDROID_CHANGES。
然後在android_getaddrinfo_proxy中,我們可以看到如下代碼:
- snprintf(propname, sizeof(propname), "net.dns1.%d", getpid());
- if (__system_property_get(propname, propvalue) > 0) {
- return -1;
- }
- // Bogus things we can't serialize. Don't use the proxy.
- if ((hostname != NULL &&
- strcspn(hostname, " \n\r\t^'\"") != strlen(hostname)) ||
- (servname != NULL &&
- strcspn(servname, " \n\r\t^'\"") != strlen(servname))) {
- return -1;
- }
- …
- // Send the request.
- proxy = fdopen(sock, "r+");
- if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d",
- 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) < 0) {
- goto exit;
- }
- // literal NULL byte at end, required by FrameworkListener
- if (fputc(0, proxy) == EOF ||
- fflush(proxy) != 0) {
- goto exit;
- }
Android會首先嚐試從系統屬性(System Property)中讀取DNS服務器的IP地址,然後使用這個DNS服務器來進行DNS解析。如果沒有設置相關係統屬性,則採用Netd的方式來進行DNS解析。由於在使用Netd方式進行解析的時候server name是不能爲NULL的,所以可以看到上面將server name修改成了’^’。在分析Netd代理之前,我們最好停一停,看看Android4.3後,getaddrinfo是怎麼做的。
首先是從JNI層的getaddrinfo的代碼開始:
- int rc = getaddrinfo(node.c_str(), NULL, &hints, &addressList);
和Android4.2.2沒有變化,直接調用了getaddrinfo,其中第二個參數是NULL。
- Int
- getaddrinfo(const char *hostname, const char *servname,
- const struct addrinfo *hints, struct addrinfo **res)
- {
- return android_getaddrinfoforiface(hostname, servname, hints, NULL, 0, res);
- }
直接調用了android_getaddrinfoforiface函數。
- /* 4.3 */
- static int android_getaddrinfo_proxy(
- const char *hostname, const char *servname,
- const struct addrinfo *hints, struct addrinfo **res, const char *iface)
- {
- int sock;
- const int one = 1;
- struct sockaddr_un proxy_addr;
- FILE* proxy = NULL;
- int success = 0;
- *res = NULL;
- if ((hostname != NULL &&
- strcspn(hostname, " \n\r\t^'\"") != strlen(hostname)) ||
- (servname != NULL &&
- strcspn(servname, " \n\r\t^'\"") != strlen(servname))) {
- return EAI_NODATA;
- }
- sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0) {
- return EAI_NODATA;
- }
- …….
很明顯,Android4.3以後刪掉了讀取系統屬性的那一段代碼,這時如果任然採用添加系統屬性的方法來修改DNS服務器將不會產生任何作用。
Android除了使用getaddrinfo函數外,系統代碼還會使用gethostbyname等其他路徑。下面我們再看看gethostbyname路徑在Android4.3前後發生的變化。
在給出代碼之前,先說明下gethostbyname函數內部將調用gethostbyname_internal來真正進行DNS解析。
Android4.2.2:
- static struct hostent *
- gethostbyname_internal(const char *name, int af, res_state res)
- {
- …
- rs->host.h_addrtype = af;
- rs->host.h_length = size;
- /*
- * if there aren’t any dots, it could be a user-level alias.
- * this is also done in res_nquery() since we are not the only
- * function that looks up host names.
- */
- if (!strchr(name, ‘.’) && (cp = __hostalias(name)))
- name = cp;
- /*
- * disallow names consisting only of digits/dots, unless
- * they end in a dot.
- */
- if (isdigit((u_char) name[0]))
- for (cp = name;; ++cp) {
- …
- }
- if (!isdigit((u_char) *cp) && *cp != ‘.’)
- break;
- }
- if ((isxdigit((u_char) name[0]) && strchr(name, ‘:’) != NULL) ||
- name[0] == ‘:’)
- for (cp = name;; ++cp) {
- if (!*cp) {
- …
- }
- if (!isxdigit((u_char) *cp) && *cp != ‘:’ && *cp != ‘.’)
- break;
- }
- hp = NULL;
- h_errno = NETDB_INTERNAL;
- if (nsdispatch(&hp, dtab, NSDB_HOSTS, “gethostbyname”,
- default_dns_files, name, strlen(name), af) != NS_SUCCESS) {
- return NULL;
- }
- h_errno = NETDB_SUCCESS;
- return hp;
- }
先不關心使用的localdns是哪個,在Android4.2.2中,gethostbyname_internal直接調用了nsdispatch來進行域名解析。
下面再看看Android4.3中的變化:
- static struct hostent *
- gethostbyname_internal(const char *name, int af, res_state res, const char *iface, int mark)
- {
- …
- proxy = android_open_proxy();
- if (proxy == NULL) goto exit;
- /* This is writing to system/netd/DnsProxyListener.cpp and changes
- * here need to be matched there */
- if (fprintf(proxy, “gethostbyname %s %s %d”,
- iface == NULL ? “^” : iface,
- name == NULL ? “^” : name,
- af) < 0) {
- goto exit;
- }
- if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {
- goto exit;
- }
- result = android_read_hostent(proxy);
- exit:
- if (proxy != NULL) {
- fclose(proxy);
- }
- return result;
- }
從上面代碼可以看到,Android4.3中徹底全面使用Netd的方式進行了DNS處理。
最後讓我們再看看getnameinfo在bionic的實現。
首先是4.2.2的代碼,路徑上getnameinfo會調用getnameinfo_inet,然後出現下面的代碼:
- #ifdef ANDROID_CHANGES
- struct hostent android_proxy_hostent;
- char android_proxy_buf[MAXDNAME];
- int hostnamelen = android_gethostbyaddr_proxy(android_proxy_buf,
- MAXDNAME, addr, afd->a_addrlen, afd->a_af);
- if (hostnamelen > 0) {
- hp = &android_proxy_hostent;
- hp->h_name = android_proxy_buf;
- } else if (!hostnamelen) {
- hp = NULL;
- } else {
- hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
- }
- #else
- hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
- #endif
具體如何處理根據ANDROID_CHANGES宏決定,如果定義了該宏,則通過Netd的方式進行。如果沒有則直接調用gethostbyaddr,該函數後面會進行實際的dns解析。
再看看Android4.3中的實現:
- int hostnamelen = android_gethostbyaddr_proxy(android_proxy_buf,
- MAXDNAME, addr, afd->a_addrlen, afd->a_af, iface, mark);
強行使用Netd的方式完成DNS的解析。Google在Android4.3後讓DNS解析全部採用Netd代理的方式進行。
Netd是Network Daemon的縮寫,Netd在Android中負責物理端口的網絡操作相關的實現,如Bandwidth,NAT,PPP,soft-ap等。Netd爲Framework隔離了底層網絡接口的差異,提供了統一的調用接口,簡化了整個網絡邏輯的使用。
簡單來說就是Android將監聽/dev/socket/dnsproxyd,如果系統需要DNS解析服務,那麼就需要打開dnsproxyd,然後安裝一定的格式寫入命令,然後監聽等待目標回答。
在分析Netd前,必須知道Netd的權限和所屬。
圖中可以看出,兩者的owner都是root,現在就好理解爲什麼說Android4.3後很多原來功能不需要root的原因了,系統現在採用代理的方式,讓屬於同group的用戶可以藉助Netd來幹一些原來只有root能幹的事情。
Android的初始化大致上可以分爲三個部分:第一部分爲啓動Linux階段,該部分包括bootloader加載kernel與kernel啓動。第二部分爲android的系統啓動,入口爲init程序,這部分包括啓動service manager,啓動Zygote,初始化Java世界等。第三部分爲應用程序啓動,主要爲運行package manager。
與Netd相關聯的是第二部分,也就是init進程。init進程在初始化中會處理/init.rc以及/init.<hardware>.rc兩個初始化腳本,這些腳本決定了Android要啓動哪些系統服務和執行哪些動作。
比如:
- service servicemanager /system/bin/servicemanager
- user system
- critical
- onrestart restart zygote
- onrestart restart media
- service vold /system/bin/vold
- socket vold stream 0660 root mount
- ioprio be 2
- service netd /system/bin/netd
- socket netd stream 0660 root system
- socket dnsproxyd stream 0660 root inet
- service debuggerd /system/bin/debuggerd
- service ril-daemon /system/bin/rild
- socket rild stream 660 root radio
- socket rild-debug stream 660 radio system
- user root
- group radio cache inet misc audio sdcard_rw
通過init.rc,我們可以看到netd和dnsproxy的權限和所屬。直接從代碼開始分析,netd源代碼位於/system/netd/main.cpp,由C++編寫。
從上面框架圖中可以得知,netd由四個大部分組成,一部分是NetlinkManager,一個是CommandListener,然後是DnsProxyListener和MDnsSdListener。在main函數中netd依次初始化四個部件:
- int main() {
- CommandListener *cl;
- NetlinkManager *nm;
- DnsProxyListener *dpl;
- MDnsSdListener *mdnsl;
- if (!(nm = NetlinkManager::Instance())) {
- ALOGE("Unable to create NetlinkManager");
- exit(1);
- };
- …
- cl = new CommandListener(rangeMap);
- nm->setBroadcaster((SocketListener *) cl);
- if (nm->start()) {
- ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
- exit(1);
- }
- setenv("ANDROID_DNS_MODE", "local", 1);
- dpl = new DnsProxyListener(rangeMap);
- if (dpl->startListener()) {
- ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
- exit(1);
- }
- mdnsl = new MDnsSdListener();
- if (mdnsl->startListener()) {
- ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
- exit(1);
- }
- if (cl->startListener()) {
- ALOGE("Unable to start CommandListener (%s)", strerror(errno));
- exit(1);
- }
代碼都很簡單,所以不需要贅述,只不過需要注意那句setenv(“ANDROID_DNS_MODE”,”local”,1),這句在後面有大作用。如果看過bionic代碼的同學可能已經有所領悟了。
DnsProxyListener實際上就是pthread創造的一個線程,該線程僅僅監聽dnsproxyd這個socket。
其他進程如何利用dnsproxyd來進行DNS解析呢?答案很簡單,看到bionic中gethostbyname_internal中的這麼一句:
- if (fprintf(proxy, “gethostbyname %s %s %d”,
- iface == NULL ? “^” : iface,
- name == NULL ? “^” : name,
- af) < 0) {
- goto exit;
- }
其他進程打開dnsproxyd後(必須要同一個組),使用命令的方式來申請DNS解析。DnsProxyListener內部邏輯是很複雜的,這裏沒必要深究。現在看看gethostbyname這個命令如何解析。
Netd當中每一個命令對應一個類,該類繼承自NetdCommand類。除此之外,還需要一個XXXXHandler的類來做實際命令的處理工作。XXXX是命令的名稱,比如對於gethostbyname就有兩個類:GetHostByNameCmd
GetHostByNameHandler。既然XXXXhandler中有兩個公共方法,一個threadStart一個叫start。除此之外,還有個私有方法run。對命令的實際處理就是run方法實現的。
- void DnsProxyListener::GetHostByNameHandler::run() {
- …
- struct hostent* hp;
- hp = android_gethostbynameforiface(mName, mAf, mIface ? mIface : iface, mMark);
- bool success = true;
- if (hp) {
- success = mClient->sendCode(ResponseCode::DnsProxyQueryResult) == 0;
- success &= sendhostent(mClient, hp);
- } else {
- success = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, NULL, 0) == 0;
- }
- if (!success) {
- ALOGW("GetHostByNameHandler: Error writing DNS result to client\n");
- }
- mClient->decRef();
- }
關鍵的兩行代碼是android_gethostbynameforiface和sendBinaryMsg,後者是將前者得到的結果應答給請求DNS解析的進程。
- struct hostent *
- android_gethostbynameforiface(const char *name, int af, const char *iface, int mark)
- {
- struct hostent *hp;
- res_state res = __res_get_state();
- if (res == NULL)
- return NULL;
- hp = gethostbyname_internal(name, af, res, iface, mark);
- __res_put_state(res);
- return hp;
- }
關鍵仍然是調用了gethostbyname_internal。看到這裏,看官們可能就會奇怪了,進程向Netd申請DNS請求的時候,調用的函數就是這個gethostbyname_internal,那麼此時又調用一次豈不是遞歸了?這裏就體現了創造Android工程師的智慧了。第一次調用gethostbyname_internal的時候是進程調用,並且這個時候ANDROID_DNS_MODE沒有設置。第二次調用gethostbyname_internal的時候是Netd調用的,Netd的權限是root的,而且更關鍵的是前面Netd初始化的時候set了ANDROID_DNS_MODE,這兩個不同的地方就影響了整個邏輯。
除此之外,上方android_gethostbynameforiface函數中調用了__res_get_state函數。該函數獲得了一個和線程相關的DNS服務器信息。去哪個local dns查詢就看這個函數返回的res_thread結構了。這部分內容稍後進行分析。我們繼續關注gethostbyname_internal的實現。
- static struct hostent *
- gethostbyname_internal(const char *name, int af, res_state res, const char *iface, int mark)
- {
- const char *cache_mode = getenv("ANDROID_DNS_MODE");
- FILE* proxy = NULL;
- struct hostent *result = NULL;
- if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) {
- res_setiface(res, iface);
- res_setmark(res, mark);
- return gethostbyname_internal_real(name, af, res);
- }
這一次判斷cache_mode的語句將爲true,此時進入gethostbyname_internal_real函數來處理DNS請求,後面就不用多分析了,有興趣的童鞋可以繼續跟隨代碼。後面就是構建DNS請求包和發送DNS請求了。
整個DNS解析的流程我們是清楚了,現在我們就要去想辦法修改DNS服務器了。在android_gethostbynameforiface中,通過_res_thread_get函數獲得__res_state。而在_res_thread_get函數中,用pthread_getspecific來獲得與線程相關聯的
_res_key。此時如果pthread_getspecific返回的是NULL說明該函數是第一次被調用,那麼將會通過_res_thread_alloc分配內存然後進行初始化。初始化關鍵語句是res_ninit,該函數由會調用__res_vinit完成具體工作。
這裏先給出__res_state結構的具體信息:
- struct __res_state {
- char iface[IF_NAMESIZE+1];
- int retrans; /* retransmission time interval */
- int retry; /* number of times to retransmit */
- u_int options; /* option flags - see below. */
- int nscount; /* number of name servers */
- struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */
- #define nsaddr nsaddr_list[0] /* for backward compatibility */
- u_short id; /* current message id */
- char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */
- char defdname[256]; /* default domain (deprecated) */
- u_int pfcode; /* RES_PRF_ flags - see below. */
- unsigned ndots:4; /* threshold for initial abs. query */
- unsigned nsort:4; /* number of elements in sort_list[] */
- char unused[3];
- struct {
- struct in_addr addr;
- uint32_t mask;
- } sort_list[MAXRESOLVSORT];
- res_send_qhook qhook; /* query hook */
- res_send_rhook rhook; /* response hook */
- int res_h_errno; /* last one set for this context */
- int _mark; /* If non-0 SET_MARK to _mark on all request sockets */
- int _vcsock; /* PRIVATE: for res_send VC i/o */
- u_int _flags; /* PRIVATE: see below */
- u_int _pad; /* make _u 64 bit aligned */
- union {
- /* On an 32-bit arch this means 512b total. */
- char pad[72 - 4*sizeof (int) - 2*sizeof (void *)];
- struct {
- uint16_t nscount;
- uint16_t nstimes[MAXNS]; /* ms. */
- int nssocks[MAXNS];
- struct __res_state_ext *ext; /* extention for IPv6 */
- } _ext;
- } _u;
- struct res_static rstatic[1];
- };
關鍵的成員是nsaddr_list,現在需要知道該成員何時何處被初始化了。答案是在前面的__res_vinit函數中,不過在深入之前必須要看看__res_ninit函數的註釋部分。這一部分介紹了初始化的大概邏輯。
- /*
- * Set up default settings. If the configuration file exist, the values
- * there will have precedence. Otherwise, the server address is set to
- * INADDR_ANY and the default domain name comes from the gethostname().
- *
- * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1
- * rather than INADDR_ANY ("0.0.0.0") as the default name server address
- * since it was noted that INADDR_ANY actually meant ``the first interface
- * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface,
- * it had to be "up" in order for you to reach your own name server. It
- * was later decided that since the recommended practice is to always
- * install local static routes through 127.0.0.1 for all your network
- * interfaces, that we could solve this problem without a code change.
- *
- * The configuration file should always be used, since it is the only way
- * to specify a default domain. If you are running a server on your local
- * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1"
- * in the configuration file.
- *
- * Return 0 if completes successfully, -1 on error
- */
實際上這個所謂的配置文件正逐步被去掉,在__res_vinit後面有一段被#ifndefANDROID_CHANGES包圍的代碼,這段代碼就是解析/etc/resolv.conf文件的。但是4.3後是#define了ANDROID_CHANGES的。所以ANDROID4.3以後再添加
resolv.conf是沒有意義的了。
註釋中說如果沒有配置文件,則server address設爲INADDR_ANY並且通過gethostname來獲得默認domain name。也就是說,如果在wifi等環境下,DNS服務器都是自動獲取的。
5. 對策與思路
Android4.3之前
在Android4.3以前,如果需要修改DNS服務器,有很多種方法,這些方法的實質就是向系統屬性中添加“net.dns1”字段的信息。這些方法的前提條件都是獲得root權限。具體方法有:
1. 在shell下,直接設置“net.dns1”等的系統屬性。
2. 在init.rc腳本中,添加對“net.dns1”等系統屬性的設置。
3. 在root權限下創建resovle.conf文件並添加相關name server信息。
Android4.3以後
在Android4.3以後,通過系統屬性或者解析文件來手動修改DNS服務器已經是不可能了。主要有兩種方法,一個是在NDK下面修改DNS解析邏輯,第二個是通過Android系統源代碼修改相關邏輯,讓Android4.3的新修改無效,然後重構Android。下面是一個老外基於NDK的修改方案,該方案需要以下權限:
1. Root權限
2. 對/system文件夾有寫權限
3. 能修改/etc/init.d
該方案重寫了DnsProxyListener和bionic解析器邏輯,通過將/dev/socket/dnsproxyd改名然後自己替換它來達到目的。
- /* 等待Netd啓動 */
- while (do_wait && stat(SOCKPATH, &statbuf) < 0) {
- sleep(1);
- }
- /* 將其改名 */
- if (stat(SOCKPATH, &statbuf) == 0) {
- unlink(SOCKPATH ".bak");
- rename(SOCKPATH, SOCKPATH ".bak");
- restore_oldsock = 1;
- }
- sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
- …
- /* 移花接木 */
- memset(&sock, 0, sizeof(sock));
- sock.sun_family = AF_UNIX;
- strcpy(sock.sun_path, SOCKPATH);
- if (bind(sockfd, (struct sockaddr *)&sock, sizeof(sock)) < 0)
- …
- if (chmod(SOCKPATH, 0660) < 0)
- …
- /* 使用命令行或者缺省的IP做爲DNS服務器,然後剩下的邏輯就是修改DnsProxyListener了 */
- if (optind < argc)
- setup_resolver(argv[optind]);
- else
- setup_resolver("223.5.5.5");
代碼邏輯比較容易理解,但是如何使用呢?很簡單,使用adb將NDK生成的可執行文件拷貝到system目錄下面,然後./dnstool –v 223.5.5.5&即可。
老外的github:https://github.com/cernekee/dnsproxy2