個人郵箱:[email protected]
在前面的幾篇中我們從 main 函數入手,主要分析了 CommandListener + Netlinkmanager 兩部分共同組成的可實現與 Kernel 層、Framework 層通信,並完成一套完整的功能系統。並且在文中提及到另外兩個部分,DnsProxyListener 和 MDnsSdListener。顧名思義兩者都是與
DNS 相關。以下是 main函數中提及到兩者的部分。
其實所有android的dns查詢都會proxy到netd中。這就需要了解android-dns的工作原理,是從bionic的libc中改寫netbsd部分,爲了實現android這種dns請求多樣的系統,比如,美國verzion/ATT的彩信就會需要通過data連接查詢dns,然後才能建立彩信數據通信收發彩信,如果此時wifi在連接的時候,就會需要兩條dns請求通路。所以android以pid或者iface作爲區分(JB43之前使用pid之後使用iface),來判斷需要走wifi或data來query-dns。這個我會在後面的文章分析android
dns工作原理。
//**** mian.cpp ****
dpl = new <span style="BACKGROUND-COLOR: #ffd700">DnsProxyListener</span>();
if (dpl->startListener()) {
ALOGE("Unable to start <span style="BACKGROUND-COLOR: #ffd700">DnsProxyListener</span> (%s)", strerror(errno));
exit(1);
}
//multicast_DNS_server_descript_listener 多播DNS守護進程
mdnsl = new MDnsSdListener();
if (mdnsl->startListener()) {
ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
exit(1);
}
首先從 DnsProxyListener 開始分析,該部分主要實現的是,DNS代理解析,從名字到地址,從名字到服務器端口號碼的功能,該部分與 CommandListener 相似度較高。
//**** DnsProxyListener.cpp ****
// 註冊命令:GetAddrInfoCmd(),GetHostByAddrCmd()
DnsProxyListener::DnsProxyListener() :
FrameworkListener("dnsproxyd") {
registerCmd(new GetAddrInfoCmd());
registerCmd(new GetHostByAddrCmd());
}
構造函數,註冊命令,並設置了 socket 的基本屬性名字爲 dnsproxyd 的有連接的 socket,它們將在後面的創建 socket 中使用到,接下來調用了 dpl->startListener()方法,相關的類繼承關係Dnsproxlistener → FrameworkListener → SocketListener,因此相當於調用 SocketListener 類中的startListener(),該方法按照之前的屬性創建 socket,並開始監聽。這些部分均與 CommandListener中的使用相似。也就是其他部分通過socket連接的方法向“dnsproxyd”寫入命令來請求功能。
//**** /system/core/libsysutils/src/SocketListener ****
int SocketListener::startListener() {
if (!mSocketName && mSock == -1) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
return -1;
} else if (mSocketName) {
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",
mSocketName, strerror(errno));
return -1;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
}
if (mListen && listen(mSock, 4) < 0) { //有鏈接(tcp)
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen) //無鏈接(udp)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
return 0;
}
調用 startListener()創建線程調用 threadStart 函數。
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);
//獲得上層
//無關類型轉換,獲得完全相同的比特位
me->runListener();
pthread_exit(NULL);
return NULL;
}
而後開啓線程調用 runlistener()到了真正的檢測 socket 狀態,使用到 fd_set,selelct 函數,最終有數據接收,觸發了 onDataAvailable 函數。(與前面篇章基本相似不粘貼全部代碼)
void SocketListener::runListener() {
SocketClientCollection *pendingList = new SocketClientCollection();
while(1) {
SocketClientCollection::iterator it;
fd_set read_fds; //使用了fd_set
int rc = 0;
int max = -1;
FD_ZERO(&read_fds);
//mListener用於判斷有鏈接(TCP)or無鏈接(UDP)
if (mListen) {
max = mSock;
FD_SET(mSock, &read_fds);
}
……
……
隨後觸發 Frameworklistener 中的 dispathCommand 函數直接按照字符串格式解析,因爲命令源爲framework 層的 NetworkManagerService 通過調用 NativeDaemonConnector 裏面的的 doCommand函數下發字符串命令。(與前面篇章基本相似不粘貼全部代碼)
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
FrameworkCommandCollection::iterator i;
int argc = 0;
char *argv[FrameworkListener::CMD_ARGS_MAX];
char tmp[255];
char *p = data;
char *q = tmp;
char *qlimit = tmp + sizeof(tmp) - 1;
bool esc = false;
bool quote = false;
int k;
bool haveCmdNum = !mWithSeq;
memset(argv, 0, sizeof(argv));
memset(tmp, 0, sizeof(tmp));
while(*p) {
if (*p == '\\') { //if (*p == '\')
if (esc) {
if (q >= qlimit)
goto overflow;
*q++ = '\\'; //*q = *p++
……
……
經過解析匹配選擇處理後調用 runCommand 函數進行處理,該函數爲定義在FrameworkCommand 中的純虛函數,爲子類提供接口,這裏的具體的實現在 DnsProxyListener 的兩個成員類 GetAddrInfoCmd,GetAddrInfoHandler 中。
// 哪裏來的 command? 答:frameworkCommand中的onDataAvailable中
// 的runcommand,相當於netlinkListener 中的 onEvent。是對接收自framework層
// 的調用命令(一般是該類自己註冊的函數)作反應。
//
// arg[]: 1 2 3 4 5 6
// name service flags family socktype protocol
//
int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (DBG) {
for (int i = 0; i < argc; i++) {
ALOGD("argv[%i]=%s", i, argv[i]);
}
}
if (argc != 7) {
char* msg = NULL;
asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);
ALOGW("%s", msg);
cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
free(msg);
return -1;
}
char* name = argv[1];
if (strcmp("^", name) == 0) { //arg[1] != "^"
name = NULL;
} else { //name = arg[1]
name = strdup(name);
}
char* service = argv[2]; //argv[2] != "^"
if (strcmp("^", service) == 0) {
service = NULL;
} else {
service = strdup(service); //service = arg[2]
}
struct addrinfo* hints = NULL;
int ai_flags = atoi(argv[3]); //ai_flags = argv[3]
int ai_family = atoi(argv[4]); //ai_family= argv[4]
int ai_socktype = atoi(argv[5]); //ai_socktype= argv[5]
int ai_protocol = atoi(argv[6]); //ai_protocol = argv[6]
if (ai_flags != -1 || ai_family != -1 ||
ai_socktype != -1 || ai_protocol != -1) {
hints = (struct addrinfo*) calloc(1, sizeof(struct addrinfo));
hints->ai_flags = ai_flags;
hints->ai_family = ai_family;
hints->ai_socktype = ai_socktype;
hints->ai_protocol = ai_protocol;
}
if (DBG) {
ALOGD("GetAddrInfoHandler for %s / %s",
name ? name : "[nullhost]",
service ? service : "[nullservice]");
}
cli->incRef();
DnsProxyListener::GetAddrInfoHandler* handler =
new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints);
//相當於調用GetAddrInfoHandler -> run
handler->start(); //////////////////////
//////////////////////////////////////////////////
return 0;
}
而後將解析 Framework 命令中獲得的數據,作爲參數以 addrinfo 結構體的形式賦值給GetAddrInfoHandler 的構造函數,而後掉用其 start()方法。
//start ()與 threadStart()函數
void DnsProxyListener::GetAddrInfoHandler::start() {
pthread_create(&mThread, NULL,
DnsProxyListener::GetAddrInfoHandler::threadStart, this);
}
void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) {
GetAddrInfoHandler* handler = reinterpret_cast<GetAddrInfoHandler*>(obj);
handler->run();
delete handler;
pthread_exit(NULL);
return NULL;
}
最終啓動新的線程調用,run()函數,通過調用 gethonstbyaddr()函數進行解析,並將解析結果發送至framework 層。其中涉及到 addrinfo 結構體,和 getaddrinfo 庫函數。
// *************************************************************************
// run()函數
// 通過getaddrinfo函數,實現地址解析,解析內容爲來自framework層下發的命令
// 並將返回數值提交回framework層
// *************************************************************************
// struct addrinfo{
// int ai_flags;
// int ai_family;
// int ai_socktype;
// int ai_protocol;
// socklen_t ai_addrlen;
// char *ai_canonname;
// struct sockaddr *ai_addr;
// struct addrinfo *ai_next;
// };
//
void DnsProxyListener::GetAddrInfoHandler::run() {
if (DBG) {
ALOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService);
}
struct addrinfo* result = NULL;
uint32_t rv = getaddrinfo(mHost, mService, mHints, &result);
//重要的函數,通過mHost:主機名或者地址串
// mService:服務器名或者10禁止端口號
// mhints:期望返回的信息類型的暗示~
//
//本條函數的參數全部來自於,framework層下發命令格式如後文
//參考:http://blog.csdn.net/xjtuse_mal/article/details/1967471
if (rv) {
// getaddrinfo failed
mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
} else {
bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
struct addrinfo* ai = result;
while (ai && success) { //將解析結果返回給framework層
success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)
&& sendLenAndData(mClient, ai->ai_addrlen, ai->ai_addr)
&& sendLenAndData(mClient,
ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0,
ai->ai_canonname);
ai = ai->ai_next; //一個結構體鏈~ 其中有ai_next元素因爲以下兩種情形:
//
//(1) 以爲 mHost 可能關聯多個地址,則將適用於所有請
// 求地址簇的每個地址都返回一個對應結構。
//
//(2) 如果service參數指定的服務支持多個socket類型
// 則對每個socket類型都返回一個對應的結構
}
success = success && sendLenAndData(mClient, 0, "");//補加結尾‘\0’
if (!success) {
ALOGW("Error writing DNS result to client");
}
}
if (result) {
freeaddrinfo(result);
}
mClient->decRef();
}
至此實現了 framework 層下發命令,netd 接受並解析命令,而後並將調用處理函數後的解析結果返回給framework 層的消息通信迴路,通信之間使用了 socket 進行通信。
//內部類 GetAddrInfoCmd (public NetdCommand )
// GetAddrInfoHandler
//
// GetHostByAddrCmd (public NetdCommand)
// GetHostByAddrHandler
而另外的類的 GetHostByAddrHandler 的基本框架與上文中描述的部分完全相似。以上內部類兩兩協作,共同實現了向 Framework 層註冊的兩個命令的實現。實現 DNS 解析,名字到地址到、名字到服務器端口兩中功能。