在用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);
然后给出具体例子如下:
#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语言中迭代器的设计与使用
QQ 群:242722074
512

被折叠的 条评论
为什么被折叠?



