处理udp请求
程序员文章站
2022-05-12 09:18:56
...
接下来就分析如何处理udp请求了,它主要用到的函数就是client_request
client_request(isc_task_t *task, isc_event_t *event)
{
ns_client_t *client = event->ev_arg;
//处理的连接数++
ns_client_requests++;
//这里是udp协议过来的
if (event->ev_type == ISC_SOCKEVENT_RECVDONE)
{
//获取那个recvevent事件
sevent = (isc_socketevent_t *)event;
//把buffer转移到新的内容上
isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
isc_buffer_add(&tbuffer, sevent->n);
buffer = &tbuffer;
//获取地址
client->peeraddr = sevent->address;
//
client->nrecvs--
}
//解析dns报文,获取报文的id和flags
result = dns_message_peekheader(buffer, &id, &flags);
{
id = isc_buffer_getuint16(&buffer);
flags = isc_buffer_getuint16(&buffer);
}
//解析报文
result = dns_message_parse(client->message, buffer, 0);
{
//获取四个记录
msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
//获取要查询的名字
ret = getquestions(source, msg, &dctx, options);
{
//每次查询的的域名个数只能是1,name的ndata里面就存储了要查询的域名以及标点的个数
for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++)
{
dns_name_t *name = isc_mempool_get(msg->namepool);
isc_buffer_remainingregion(source, &r);
isc_buffer_setactive(source, r.length);
result = getname(name, source, msg, dctx);
//获取到type和class
dns_rdatatype_t rdtype = isc_buffer_getuint16(source);
dns_rdataclass_t rdclass = isc_buffer_getuint16(source);
//申请dns_rdatalist_t的空间
rdatalist = newrdatalist(msg);
{
dns_msgblock_t *msgblock = msgblock_allocate(msg->mctx,sizeof(dns_rdatalist_t),RDATALIST_COUNT);
ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
dns_rdatalist_t *rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
}
dns_rdataset_t *rdataset = isc_mempool_get(msg->rdspool);
rdatalist->type = rdtype;
rdatalist->covers = 0;
rdatalist->rdclass = rdclass;
rdatalist->ttl = 0;
dns_rdataset_init(rdataset);
//这里把rdatalist转变为rdataset
dns_rdatalist_tordataset(rdatalist, rdataset);
{
rdataset->methods = &methods;
rdataset->rdclass = rdatalist->rdclass;
rdataset->type = rdatalist->type;
rdataset->covers = rdatalist->covers;
rdataset->ttl = rdatalist->ttl;
rdataset->trust = 0;
rdataset->private1 = rdatalist;
rdataset->private2 = NULL;
rdataset->private3 = NULL;
rdataset->privateuint4 = 0;
rdataset->private5 = NULL;
}
//把rdataset添加到name->list中
ISC_LIST_APPEND(name->list, rdataset, link);
}
}
//提取其余三个字段,注意如果是question的话,这三个字段一般就是空
ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
}
//处理edns
//获取目的地址,不同的协议获取的方式是不同的,对于ipv4的通过interface的addr,如果是tcp的query,通过client的tcpsorket
if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
//这是所有ipv4的处理
isc_netaddr_fromsockaddr(&destaddr, &client->interface->addr);
else {
这是ipv6的处理
isc_netaddr_fromsockaddr(&destaddr,&destsockaddr)
}
//通过client的源Ip匹配可以服务他的view,就是遍历server的viewlist,把找到的那个view复制到client->view
for (view = ISC_LIST_HEAD(ns_g_server->viewlist);view != NULL;view = ISC_LIST_NEXT(view, link))
{
dns_view_attach(view, &client->view);
}
//获取跟签名相关的部分
//调整包的大小,这个值是可以设定的
if (client->udpsize > 512)
switch (client->message->opcode)
{
case dns_opcode_query:
ns_query_start(client);
}
}
//开始查询资源了
ns_query_start(ns_client_t *client)
{
//获取到查询信息
dns_message_t *message = client->message;
//用对端的地址初始化client的信息
ns_client_info(client, peerbuf, ISC_SOCKADDR_FORMATSIZE);
//处理好后路
client->next = query_next_callback;
//设置一些查询的属性
//获取查询的域名,存入到msg->cursors[section]中
result = dns_message_firstname(message, DNS_SECTION_QUESTION);
//将上面的名字存储到client->query.qname
dns_message_currentname(message, DNS_SECTION_QUESTION,&client->query.qname);
//域名合法性的检测,这里最终把要查询的域名存储到client->query.origqname->ndata中
//比如a.some.top会存储为a回车换行sometop(假设zone名是some.top)
dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
client->query.origqname = client->query.qname;
//要判断它的question的个数要大于1
if (message->counts[DNS_SECTION_QUESTION] > 1)
{}
//取出第一个rdataset,检验它的查询type,就是A/AAAA/TXT那些,只有合法才继续往下走
rdataset = ISC_LIST_HEAD(client->query.qname->list);
qtype = rdataset->type;
switch(qtype){}
//一路设置message的属性
将client复制到qclient
ns_client_attach(client, &qclient);
query_find(qclient, NULL, qtype, &client->q_log);
}
//在查找合适的db的时候,最先找的是标准库即zone文件,然后找dlz数据,如果找不到再找cache,当然cache就不是权威的答复了
query_find((ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype, query_log_t* q_log)
{
if (client->view->checknames &&!dns_rdata_checkowner(client->query.qname,)
{
//view需要检测合法性的
}
//先找到合适的db
dns_db_t *db
dns_zone_t *zone
result = query_getdb(client, client->query.qname, qtype, options,&zone, &db, &version, &is_zone)
{
dns_db_t * tdbp;
dns_zone_t *zone = NULL;
//他对应的接口dns_dlzimplementation_t *dlz_imp;每个dlz对应一个dlzdb_t的db,该db可以连接到dns_dlzimplementation_t 的接口,然后他又会有公有的methods
dns_dlzdb_t *dlzdatabase;
//这个就是域名的标号
unsigned int namelabels = dns_name_countlabels(name);
//先看是否有一个合适的zonedb
result = query_getzonedb(client, name, qtype, options, &zone,dbp, versionp);
{
//先去红黑树中查找zone对应的信息存储到zone中
result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,&zone);
{
dns_zone_t *dummy = NULL;
//去红黑树中寻找该zone
result = dns_rbt_findname(zt->table, name, rbtoptions, foundname, (void **) (void*)&dummy);
{
dns_rbtnode_t *node = NULL;
result = dns_rbt_findnode(rbt, name, foundname, &node, NULL,options, NULL, NULL);
{
}
}
}
}
//如果没有找到,或者找到的zone的名字比待查找的名字短(比如找到的zone是aa,而我们要查找aa.bb),就去dlz中寻找,注意最终的tdbp就等于封装好的dns_sdlz_db_t 类型的db,里面封装了dlz自己的imp以及自己的数据集合(flexi_dns_instance)
if (zonelabels < namelabels && client->view->dlzdatabase != NULL)
{
tresult = dns_dlzfindzone(client->view, name,zonelabels, &tdbp);
{
dns_name_t *zonename;
for (i = namelabels; i > minlabels && i > 1; i--)
{
if (i == namelabels)
{
//把原始的名字存储到zonename上
result = dns_name_copy(name, zonename, NULL)
}
else
{
dns_name_split(name, i, NULL, zonename);
{
一次次缩减,比如第一个是akindlychinacachecom,缩减第一次为kindlychinacachecom,如果在dlz中找到kindlychinacachecom,那就意味着找到了
}
//去dlz中进行zone的寻找,这里调用的是findzone方法
dns_dlzdb_t *dlzdatabase = view->dlzdatabase;
findzone = dlzdatabase->implementation->methods->findzone;
result = (*findzone)(dlzdatabase->implementation->driverarg,dlzdatabase->dbdata, dlzdatabase->mctx,view->rdclass, zonename, dbp);
{
//这个的方法就是公有方法static dns_dlzmethods_t sdlzmethods
dns_sdlzfindzone
{
//通过它找到对应的dlz模块,就是每个dlz特有的方法
dns_sdlzimplementation_t *imp= (dns_sdlzimplementation_t *) driverarg;
//确保小写
dns_sdlz_tolower(namestr)
imp->methods->findzone(imp->driverarg, dbdata, namestr)
{
//这个才是真正的findzone方法
flexi_dns_findzone(void *driverarg, void *dbdata, const char *name)
{
zone_lookup(db->lookup_data->zone_table,name)
{
//将name进行hash然后查看table中是否有
}
if (result == ISC_R_SUCCESS)
{
result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name,rdclass, dbp);
{
dns_sdlz_db_t *sdlzdb;
dns_sdlzimplementation_t *imp
imp = (dns_sdlzimplementation_t *) driverarg;
//申请dns_sdlz_db_t 空间,注意这里区别dns_dlz_db_t
sdlzdb = isc_mem_get(mctx, sizeof(dns_sdlz_db_t));
//设置它的一些属性
sdlzdb->dlzimp = imp;
sdlzdb->common.methods = &sdlzdb_methods;
//它的dbdata就是每个dlz管理自己的方法的db,比如F_instance
sdlzdb->dbdata = dbdata
//这样就找到了db,封装了公用的方法,但是它的dlz_imp又指向每个私有的dlz
*dbp = (dns_db_t *) sdlzdb;
}
}
}
}
}
}
//看nameid里面是否有更合适的
就是如果找到nameid中更匹配的,那就直接用nameid的db,而不用zonedb的
for (i = namelabels; i > minlabels && i >= 1; i--)
{
//这里用findnameid的方法看有没有更合适的db,如果有就更新
findzone = dlzdatabase->implementation->methods->findnameid;
result = (*findzone)(dlzdatabase->implementation-。。。)
{
//调用公用的方法:dns_sdlzfindnameid
dns_sdlzimplementation_t *imp = (dns_sdlzimplementation_t *) driverarg
//调用dlz真正的属于自己的方法flexi_dns_findnameid
result = imp->methods->findnameid(imp->driverarg, dbdata, namestr, is_full_domain);
{
return flexi_dns_lookup_nameid_level(name, db, is_full_domain);
{
//直接查找
}
}
if (result == ISC_R_SUCCESS)
result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name,rdclass, dbp);
{
//做法同上面的zone是一样的,申请sdlzdb,同时赋值
dns_sdlz_db_t *sdlzdb;
*dbp = (dns_db_t *) sdlzdb;
}
}
}
}
}
}//end dns_dlzfindzone
//分离上面找到的那个db
if (*dbp != NULL)
dns_db_detach(dbp);
*dbp = tdbp
}
if (result == ISC_R_SUCCESS)
{
*zonep = zone;
//代表是权威查找出来的
*is_zonep = ISC_TRUE;
}
else
{
result = query_getcachedb(client, name, qtype, dbp, options);
//是缓存,因此不是权威
*is_zonep = ISC_FALSE;
}
}//end query_db
//确定它的权威性
if (is_zone)
authoritative = ISC_TRUE;
if (is_zone)
{
if (zone != NULL)
{
dns_zone_attach(zone, &client->query.authzone);
}
dns_db_attach(db, &client->query.authdb);
client->query.authdbset = ISC_TRUE;
}
db_find:
//下面开始查找数据
}
上一篇: scrapy框架中redis的使用