Android 4.1 Netd詳細分析(六)DnsProxyListener
個人用メールアドレス:[email protected]
前のいくつかの編ではmain関数から着手し,主にCommandListener+Netlinkmanagerの2つの部分が共に構成されたKernel層,Framework層との通信を実現し,完全な機能システムを完成した.さらに、DnsProxyListenerおよびMDnsSdListenerの他の2つの部分について説明する.その名の通り両者はDNSに関連している.以下はmain関数で両者に言及した部分です.実はandroidのdnsクエリはすべてnetdにproxyされます.これはandroid-dnsの動作原理を理解する必要があり、bionicのlibcからnetbsd部分を書き換えることであり、androidというdns要求の多様なシステムを実現するために、例えば、米国verzion/ATTのカラーメッセージはdata接続を通じてdnsを検索し、カラーメッセージデータ通信を確立してカラーメッセージを送受信する必要があり、このときwifiが接続されている場合、2つのdns要求通路が必要になる.したがってandroidはpidまたはifaceを区別とし(JB 43以前にpidを使用した後にifaceを使用)、wifiまたはdataをquery-dnsとして使用する必要があると判断した.これは後でandroid dnsの動作原理を分析します.
まずDnsProxyListenerから解析を開始し,この部分は主にDNSエージェント解析,名前からアドレス,名前からサーバポート番号までの機能を実現し,この部分はCommandListenerと類似度が高い.
コンストラクション関数、コマンドの登録、およびdnsproxydというdnsproxydの接続されたsocketというsocketの基本プロパティ名が設定されています.これらは、後の作成socketで使用され、次にdpl->startListener()メソッドが呼び出され、関連するクラス継承関係DNsproxlistener→FrameworkListener→SocketListener、したがって、SocketListenerクラスのstartListener()を呼び出すことに相当し、このメソッドは前のプロパティに従ってsocketを作成し、リスニングを開始します.これらの部分はCommandListenerでの使用と似ています.すなわち,他の部分はsocket接続により「dnsproxyd」にコマンドを書き込むことで機能を要求する.
startListener()を呼び出し、スレッドを作成してthreadStart関数を呼び出します.
その後、スレッド呼び出しrunlistener()を開いて、本当の検出socket状態になり、fd_に使用されます.set,selelct関数,最終的にデータ受信がありonDataAvailable関数をトリガした.(前章とほぼ似ていて全てのコードを貼らない)
その後、FrameworklistenerのdispathCommand関数は、frameworkレイヤのNetworkManagerServiceからNativeDaemonConnectorのdoCommand関数を呼び出すことで文字列コマンドが発行されるため、文字列形式で直接解析されます.(前章とほぼ似ていて全てのコードを貼らない)
解析整合選択処理後にrunCommand関数が呼び出され、この関数はFrameworkCommandに定義された純虚関数であり、サブクラスにインタフェースが提供される.ここでの具体的な実装はDnsProxyListenerの2つのメンバークラスGetAddrInfoCmd,GetAddrInfoHandlerにある.
その後、Frameworkコマンドで得られたデータを解析し、パラメータとしてGetAddrInfoHandlerのコンストラクション関数にaddrinfo構造体として割り当て、start()メソッドを削除する.
最終的に新しいスレッド呼び出し、run()関数を起動し、gethonstbyaddr()関数を呼び出して解析し、frameworkレイヤに解析結果を送信します.これには、addrinfo構造体とgetaddrinfoライブラリ関数が含まれます.
これによりframework層の下発命令が実現し,netdは命令を受け入れて解析し,処理関数を呼び出した解析結果をframework層のメッセージ通信回路に返し,通信間でsocketを用いて通信した.
他のクラスのGetHostByAddrHandlerの基本フレームワークは、上述した部分と完全に類似している.以上の内部クラスの2つの連携により,Framework層に登録された2つのコマンドの実現が共に実現された.DNS解析を実現し、名前からアドレス、名前からサーバポートの2つの機能を実現します.
前のいくつかの編ではmain関数から着手し,主にCommandListener+Netlinkmanagerの2つの部分が共に構成されたKernel層,Framework層との通信を実現し,完全な機能システムを完成した.さらに、DnsProxyListenerおよびMDnsSdListenerの他の2つの部分について説明する.その名の通り両者はDNSに関連している.以下はmain関数で両者に言及した部分です.実はandroidのdnsクエリはすべてnetdにproxyされます.これはandroid-dnsの動作原理を理解する必要があり、bionicのlibcからnetbsd部分を書き換えることであり、androidというdns要求の多様なシステムを実現するために、例えば、米国verzion/ATTのカラーメッセージはdata接続を通じてdnsを検索し、カラーメッセージデータ通信を確立してカラーメッセージを送受信する必要があり、このときwifiが接続されている場合、2つのdns要求通路が必要になる.したがってandroidはpidまたはifaceを区別とし(JB 43以前にpidを使用した後にifaceを使用)、wifiまたはdataをquery-dnsとして使用する必要があると判断した.これは後でandroid dnsの動作原理を分析します.
//**** mian.cpp ****
dpl = new DnsProxyListener();
if (dpl->startListener()) {
ALOGE("Unable to start DnsProxyListener (%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());
}
コンストラクション関数、コマンドの登録、およびdnsproxydというdnsproxydの接続されたsocketという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(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の2つのメンバークラス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コマンドで得られたデータを解析し、パラメータとしてGetAddrInfoHandlerのコンストラクション関数にaddrinfo構造体として割り当て、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(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の基本フレームワークは、上述した部分と完全に類似している.以上の内部クラスの2つの連携により,Framework層に登録された2つのコマンドの実現が共に実現された.DNS解析を実現し、名前からアドレス、名前からサーバポートの2つの機能を実現します.