unbound 源码分析(CDN智能调度场景的二次开发)

Table of Contents

 

一、背景:

二、智能DNS调度流程框图:

三、unbound 二次开发修改源码说明

四、部分源码分享:

一、背景:

最近要完成高铁cdn项目里面的智能DNS调度模块。

要实现在收到用户的DNS查询后,会主动向某个服务(集群状态管理服务)查询该用户所在的单车服务器IP,并将单车服务器IP返回给用户,这里其实跟云端做的根据用户所在的IP来分配靠近用户CDN节点类似。

举例说明一下需求:

1、单车的用户要解析域名:www.doopets.cn,于是向中心服务器发起DNS请求;

2、中心服务器的unbound收到请求后,根据用户的源IP来向“集群状态管理服务”查询可用的单车服务器。

3、根据查询结果,把www.doopets.cn域名解析成192.168.**.1(**这需要根据源IP和集群状态来确定)。

二、智能DNS调度流程框图:

1)运行在中心服务器上的DNS调度服务,在收到用户的DNS查询后,会主动向集群状态管理服务查询该用户所在的单车服务器IP,并且返回给用户。该DNS调度服务由unbound1.9.1二次开发完成。

2)运行在中心服务器上的单车集群状态管理服务,用于管理单车集群的健康状态,保证给用户提供一个可用的单车服务器。如果用户所在的单车服务器可用时,直接返回该用户所在的单车服务器IP,反之,返回该用户相邻的单车服务器IP。这个服务由Python完成。

3)运行在单车服务器上的心跳反馈服务,按照一定的周期收集单车服务器的健康状态,主要指标包括CPU使用率,磁盘占用大小、缓存服务的运行情况。在收集完状态信息后,实时上报给集群状态管理服务器。这个服务由Python完成。

三、unbound 二次开发修改源码说明

这里的思想就是利用local-data 这种映射来进行处理。

判断源IP是我们的用户 -》修改映射对里面的IP为我们想要返回的IP。

1)、首先把需要劫持的域名写到unbound配置文件:

需要劫持的域名,只需要添加如下这种格式:

local-data: www.doopets.cn. IN A 192.168.0.103

后面必须192.168开头。

2)如果src ip是我们中心分配的IP(192.168开头),则把local rrset里面的ip(rdata)换成我们查询出来的单车ip。

代码说明:在localzone.c文件中,local_data_answer函数做处理,新增:

 

is_start_with_special_ip 函数是判断源用户IP用的,这里多亏了EDNS的出现,unbound才能获取到源IP。

danche_num_get(ip_buf);  这个函数是根据源IP来获取一个可用的单车IP

change_rdata(danche_num, (char *)lr->rrset->entry.data, 100);  这个函数则是修改要返回给用的DNS报文。具体代码参考源码。

四、效果展示

这里对www.ccrgt.com这个域名做了劫持,用户访问这个域名的时候,直接返回该用户所在的单车服务器的IP。

五、部分源码分享:

/*
	@profile:判断src ip是否为192.168开头的
    @input: buf 接收到的内容
            buflen 长度
            
	@return: 0代表以192.168开头;
	         1代表不是192开头
	         outBuf 源ip
*/
int is_start_with_special_ip(unsigned char *buf, unsigned int buflen, char *outBuf)
{
    char pbuf[16]={0};
	char strBuf[32] = {0};

    int i;
	int flag = 1;
    for(i = 0; i < buflen; i++)
    {
        sprintf(pbuf, "%02X", buf[i]);
		//printf("char:%s\n", pbuf);
		if (0 == strcmp(pbuf, "C0")) {
			strcat(strBuf, pbuf);
			//16进制ip, 往后添加三个字节
			i++;
			sprintf(pbuf, "%02X", buf[i]);
			if (0 == strcmp(pbuf, "A8")){
				flag = 0;
			}
			strcat(strBuf, pbuf);
			
			i++;
			sprintf(pbuf, "%02X", buf[i]);
			strcat(strBuf, pbuf);

			i++;
			sprintf(pbuf, "%02X", buf[i]);
			strcat(strBuf, pbuf);
			break;
		}
        
    }
	strcpy(outBuf, strBuf);
    return flag;
}	


#define BUFSIZE 512
#define DestIp "127.0.0.1"
#define DestPort 8484
//#define ReqLen sizeof(Req)
/*
@获取单车的第三段ip
@in_buf: 16进制源ip
@danche_num_buf  单车的第三段ip
*/
int danche_num_get(char * ip_buf) {
	ssize_t i;
	int nRequestLen;
	int size_recv, total_size = 0;
	int ReqLen;
	char Req[256] = {0};

	char strResponse[BUFSIZE]={0};
	char strRequest[BUFSIZE]={0};
	int danche_num = 1;

	int sockfd, numbytes;
	struct sockaddr_in dest_addr; /* connector's address information */

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
			perror("socket");
			exit(1);
	}
	sprintf(Req, "GET /%s HTTP/1.1\r\nHost:127.0.0.1\r\nConnection: Close\r\n\r\n", ip_buf);
	ReqLen = sizeof(Req);
	dest_addr.sin_family = AF_INET; /* host byte order */
	dest_addr.sin_port = htons(DestPort); /* short, network byte order */
	dest_addr.sin_addr.s_addr = inet_addr(DestIp);

	/* Create and setup the connection */
	if (connect(sockfd, (struct sockaddr *)&dest_addr,sizeof(struct sockaddr)) == -1){
		perror("connect");
		return 0;
	}

	/* Send the request */
	strncpy(strRequest, Req,ReqLen);
	nRequestLen = ReqLen;
	if (write(sockfd,strRequest,nRequestLen) == -1) {
		perror("write");
		return 0;
	}

	/* Read in the response */
	while(1) {
		if ((size_recv =  recv(sockfd, strResponse, 512, 0) ) == -1) {
			if (errno == EWOULDBLOCK || errno == EAGAIN) {
				printf("recv timeout ...\n");
				break;
			} else if (errno == EINTR) {
				printf("interrupt by signal...\n");
				continue;
			} else if (errno == ENOENT) {
				printf("recv RST segement...\n");
				break;
			} else {
				printf("unknown error: %d\n", errno);
				break;
			}
		} else if (size_recv == 0) {
			//printf("peer closed ...\n");
			break;
		} else {
			total_size += size_recv;
		}

	}

	/* Close the connection */
	close(sockfd);
	//strcpy(danche_num_buf, strResponse);
	danche_num = atoi(strResponse); 
	//printf("strResponse: %s\n" , strResponse);
	//printf("danche_num: %d\n" , danche_num);
	return danche_num;
} 



/** answer local data match */
static int
local_data_answer(struct local_zone* z, struct module_env* env,
	struct query_info* qinfo, struct edns_data* edns,
	struct comm_reply* repinfo, sldns_buffer* buf,
	struct regional* temp, int labs, struct local_data** ldp,
	enum localzone_type lz_type, int tag, struct config_strlist** tag_datas,
	size_t tag_datas_size, char** tagname, int num_tags)
{
	struct local_data key;
	struct local_data* ld;
	struct local_rrset* lr;
	key.node.key = &key;
	key.name = qinfo->qname;
	key.namelen = qinfo->qname_len;
	key.namelabs = labs;
	if(lz_type == local_zone_redirect ||
		lz_type == local_zone_inform_redirect) {
		key.name = z->name;
		key.namelen = z->namelen;
		key.namelabs = z->namelabs;
		if(tag != -1 && (size_t)tag<tag_datas_size && tag_datas[tag]) {
			struct ub_packed_rrset_key r;
			memset(&r, 0, sizeof(r));
			if(find_tag_datas(qinfo, tag_datas[tag], &r, temp)) {
				verbose(VERB_ALGO, "redirect with tag data [%d] %s",
					tag, (tag<num_tags?tagname[tag]:"null"));

				/* If we found a matching alias, we should
				 * use it as part of the answer, but we can't
				 * encode it until we complete the alias
				 * chain. */
				if(qinfo->local_alias)
					return 1;
				return local_encode(qinfo, env, edns, repinfo, buf, temp,
					&r, 1, LDNS_RCODE_NOERROR);
			}
		}
	}
	ld = (struct local_data*)rbtree_search(&z->data, &key.node);
	*ldp = ld;
	if(!ld) {
		return 0;
	}
	//qinfo->qtype 1:LDNS_RR_TYPE_A
	lr = local_data_find_type(ld, qinfo->qtype, 1);
	if(!lr)
		return 0;

	char ip_buf[32] = {0};
	int danche_num = 1;
	/* 这里做判断,如果src ip是 192.168开头的,则把local rrset里面的ip(rdata)换成
	   我们查询出来的单车ip.*/
	if (0 == is_start_with_special_ip(buf->_data, 100, ip_buf)) {
		//获取用户的单车号;
		//替换rrset里面的ip
		//danche_num_get();
		//struct timeval start, end;
		//gettimeofday(&start, NULL);
		//获取单车序号,16进制的单车ip,第三个网段;
		danche_num = danche_num_get(ip_buf);
		//gettimeofday(&end, NULL);
		//printf("time: %lu us\n", (end.tv_sec - start.tv_sec)*1000000+ (end.tv_usec - start.tv_usec));
		change_rdata(danche_num, (char *)lr->rrset->entry.data, 100);
		//arrayToStrs((char *)lr->rrset->entry.data, 100);
	}
		
	/* Special case for alias matching.  See local_data_answer(). */
	if((lz_type == local_zone_redirect ||
		lz_type == local_zone_inform_redirect) &&
		qinfo->qtype != LDNS_RR_TYPE_CNAME &&
		lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
		qinfo->local_alias =
			regional_alloc_zero(temp, sizeof(struct local_rrset));
		if(!qinfo->local_alias)
			return 0; /* out of memory */
		qinfo->local_alias->rrset =
			regional_alloc_init(temp, lr->rrset, sizeof(*lr->rrset));
		if(!qinfo->local_alias->rrset)
			return 0; /* out of memory */
		qinfo->local_alias->rrset->rk.dname = qinfo->qname;
		qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
		return 1;
	}
	if(lz_type == local_zone_redirect ||
		lz_type == local_zone_inform_redirect) {
		/* convert rrset name to query name; like a wildcard */
		struct ub_packed_rrset_key r = *lr->rrset;
		r.rk.dname = qinfo->qname;
		r.rk.dname_len = qinfo->qname_len;
		return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1,
			LDNS_RCODE_NOERROR);
	}
	return local_encode(qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1,
		LDNS_RCODE_NOERROR);
}

 

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值