使用ACL庫編寫DNS查詢應用

  在用C/C++寫程序時,若要根據域名查找其對應的IP地址,你們通常會用到 gethostbyname 的標準函數,如若要查詢 www.sina.com.cn 域名調用 gethostbyname 時,該函數首先會查找本機 hosts 文件裏的條目,若該配置文件裏沒有對應域名,則該函數會向本機配置的DNS服務器發送查詢請求,而後將DNS服務器的返回結果反給用戶。該函數的使用較爲簡單,但卻有一個限制,若是針對某個域名,在內外網有不一樣的IP地址時就很差辦了,假設該域名由內網DNS(192.168.1.33)解析時的IP地址爲 192.168.1.22, 由外網DNS(211.239.1.33)解析由於gethostbyname使用系通通一的 resolve.conf裏的DNS服務器配置地址。要解決這個問題,最好的方式就是直接鏈接DNS服務器以遵循DNS協議格式發送查詢指令,ACL庫裏便實現DNS協議的通訊及協議解析功能(在 lib_acl/src/net/dns/ 目錄下),下面介紹如何使用ACL的DNS庫進行域名查詢。服務器

  首先列出本例子用到的函數接口說明:網絡

/**
 * 建立一個DNS查詢對象
 * @param dns_ip {const char*} DNS的IP地址
 * @param dns_port {unsigned short} DNS的Port
 * @return {ACL_RES*} 新建立的查詢對象
 */
ACL_API ACL_RES *acl_res_new(const char *dns_ip, unsigned short dns_port);

/**
 * 釋放一個DNS查詢對象
 * @param res {ACL_RES*} DNS查詢對象
 */
ACL_API void acl_res_free(ACL_RES *res);

/**
 * 查詢某個域名的IP地址
 * @param res {ACL_RES*} DNS查詢對象
 * @param domain {const char*} 要查詢的域名
 * @return {ACL_DNS_DB*} 查詢的結果集
 */
ACL_API ACL_DNS_DB *acl_res_lookup(ACL_RES *res, const char *domain);

/**
 * 根據錯誤號得到查詢失敗的緣由
 * @param errnum {int} 錯誤號
 * @return {const char*} 錯誤信息
 */
ACL_API const char *acl_res_strerror(int errnum);

/**
 * 得到當前查詢的錯誤信息
 * @param res {ACL_RES*} DNS查詢對象
 * @return {const char*} 錯誤信息
 */
ACL_API const char *acl_res_errmsg(const ACL_RES *res);

 

  而後給出具體例子以下:dom

#include "lib_acl.h"
#include <stdio.h>

static int dns_lookup(const char *domain, const char *dns_ip,
	unsigned short dns_port)
{
	ACL_RES *res = NULL;  /* DNS 查詢對象 */
	ACL_DNS_DB *dns_db = NULL;  /* 存儲查詢結果 */
	ACL_ITER iter;  /* 通用迭代對象 */

#define RETURN(_x_) do { \
	if (res) \
		acl_res_free(res); \
	if (dns_db) \
		acl_netdb_free(dns_db); \
	return (_x_); \
} while (0)

	/* 建立一個DNS查詢對象 */
	res = acl_res_new(dns_ip, dns_port);

	/* 向DNS服務器發送查詢指令並接收處理結果 */
	dns_db = acl_res_lookup(res, domain);
	if (dns_db == NULL) {
		printf("failed for domain %s, %s", domain, acl_res_errmsg(res));
		RETURN (-1);
	}

	/* 遍歷查詢結構並輸出至標準輸出 */
	printf("type\tttl\tip\t\tnet\t\tqid\t\n");
	acl_foreach(iter, dns_db) {
		ACL_HOST_INFO *info;
		struct in_addr in;
		char  buf[32];

		info = (ACL_HOST_INFO*) iter.data;
		in.s_addr = info->saddr.sin_addr.s_addr;

		/* 假設網絡掩碼爲24位,得到網絡地址 */
		acl_mask_addr((unsigned char*) &in.s_addr, sizeof(in.s_addr), 24);

		/* 將地址轉換成字符串 */
		acl_inet_ntoa(in, buf, sizeof(buf));

		/* 輸出查詢結果 */
		printf("A\t%d\t%s\t%s\t%d\r\n",
			info->ttl, info->ip, buf, res->cur_qid);
	}

	RETURN (0);
}

int main(int argc acl_unused, char *argv[] acl_unused)
{
	const char *dns_in_ip = "192.168.1.33", *dns_out_ip = "211.239.1.33";
        unsigned short dns_in_port = 53, dns_out_port = 53;
	const char *domain = "www.test.com.cn";

	/* 查詢內網DNS */
	(void) dns_lookup(domain, dns_in_ip, dns_in_port);

	/* 查詢外網DNS */
	(void) dns_lookup(domain, dns_out_ip, dns_out_port);

	return (0);
}

 

  OK,這個例子很簡單,徹底知足剛纔所說的需求。此外,該例子用到了幾個結構類型,以下(也能夠直接查詢 lib_acl/include/net/ 目錄下的頭文件說明):函數

/* DNS查詢對象結構定義 */
typedef struct ACL_RES {
	char dns_ip[64];                /**< DNS的IP地址 */
	unsigned short dns_port;        /**< DNS的Port */
	unsigned short cur_qid;         /**< 內部變量,數據包的標識 */
	time_t tm_spent;                /**< 查詢時間耗費(秒) */
	int   errnum;
#define ACL_RES_ERR_SEND	-100    /**< 寫出錯 */
#define ACL_RES_ERR_READ	-101    /**< 讀出錯 */
#define ACL_RES_ERR_RTMO	-102    /**< 讀超時 */
#define ACL_RES_ERR_NULL	-103    /**< 空結果 */
#define ACL_RES_ERR_CONN	-104    /**< TCP方式時鏈接失敗 */

	int transfer;                   /**< TCP/UDP 傳輸模式 */
#define ACL_RES_USE_UDP		0       /**< UDP 傳輸模式 */
#define ACL_RES_USE_TCP		1       /**< TCP 傳輸模式 */

	int   conn_timeout;             /**< TCP 傳輸時的鏈接超時時間, 默認爲10秒 */
	int   rw_timeout;               /**< TCP/UDP 傳輸的IO超時時間, 默認爲10秒 */
} ACL_RES;

/**
 * 主機地址結構
 */
typedef struct ACL_HOSTNAME ACL_HOST_INFO;
typedef struct ACL_HOSTNAME {
	char  ip[64];                   /**< the ip addr of the HOST */
	struct sockaddr_in saddr;	/**< ip addr in sockaddr_in */
	unsigned int ttl;               /**< the HOST's ip timeout(second) */
	int   hport;
	unsigned int nrefer;            /**< refer number to this HOST */
} ACL_HOSTNAME;

/**
 * DNS查詢結果集
 */
typedef struct ACL_DNS_DB {
	ACL_ARRAY *h_db;
	int   size;
	char  name[256];

	/* for acl_iterator */

	/* 取迭代器頭函數 */
	const ACL_HOST_INFO *(*iter_head)(ACL_ITER*, struct ACL_DNS_DB*);
	/* 取迭代器下一個函數 */
	const ACL_HOST_INFO *(*iter_next)(ACL_ITER*, struct ACL_DNS_DB*);
	/* 取迭代器尾函數 */
	const ACL_HOST_INFO *(*iter_tail)(ACL_ITER*, struct ACL_DNS_DB*);
	/* 取迭代器上一個函數 */
	const ACL_HOST_INFO *(*iter_prev)(ACL_ITER*, struct ACL_DNS_DB*);
	/* 取迭代器關聯的當前容器成員結構對象 */
	const ACL_HOST_INFO *(*iter_info)(ACL_ITER*, struct ACL_DNS_DB*);
} ACL_DNS_DB;

 

  至於文所提到的迭代器遍歷過程,請參考文章:C語言中迭代器的設計與使用this

      我的微博:http://weibo.com/zsxxsz設計

      QQ 羣:242722074code

相關文章
相關標籤/搜索