您的位置:首页 > 移动开发 > Android开发

Android 4.1 Netd详细分析(六)DnsProxyListener

2014-06-23 18:39 411 查看
在前面的几篇中我们从 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工作原理。

[cpp]
view plaincopy





//**** 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 相似度较高。

[cpp]
view plaincopy





//**** 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”写入命令来请求功能。

[cpp]
view plaincopy





//**** /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 函数。

[cpp]
view plaincopy





void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);
//获得上层
//无关类型转换,获得完全相同的比特位
me->runListener();
pthread_exit(NULL);
return NULL;
}

而后开启线程调用 runlistener()到了真正的检测 socket 状态,使用到 fd_set,selelct 函数,最终有数据接收,触发了 onDataAvailable 函数。(与前面篇章基本相似不粘贴全部代码)

[cpp]
view plaincopy





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函数下发字符串命令。(与前面篇章基本相似不粘贴全部代码)

[cpp]
view plaincopy





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 中。

[cpp]
view plaincopy





// 哪里来的 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()方法。

[cpp]
view plaincopy





//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 库函数。

[cpp]
view plaincopy





// *************************************************************************
// 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 进行通信。

[cpp]
view plaincopy





//内部类 GetAddrInfoCmd (public NetdCommand )
// GetAddrInfoHandler
//
// GetHostByAddrCmd (public NetdCommand)
// GetHostByAddrHandler

而另外的类的 GetHostByAddrHandler 的基本框架与上文中描述的部分完全相似。以上内部类两两协作,共同实现了向 Framework 层注册的两个命令的实现。实现 DNS 解析,名字到地址到、名字到服务器端口两中功能。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: