我是靠谱客的博主 听话西装,最近开发中收集的这篇文章主要介绍处理udp请求,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

接下来就分析如何处理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:
        //下面开始查找数据
   
}

 

最后

以上就是听话西装为你收集整理的处理udp请求的全部内容,希望文章能够帮你解决处理udp请求所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部