在用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