查询A记录 (根据域名获取ip)实现
getipbyhost -> gethostbyname //通过域名获取IP地址 – getnameinfo / getaddrinfo
gethostbyname函数实现
域名仅仅是 IP 地址的一个助记符,目的是方便记忆,通过域名并不能找到目标计算机,通信之前必须要将域名转换成 IP 地址。gethostbyname() 函数可以完成这种转换.
需要socket通过域名方式完成连接 , 通过API gethostbyname 函数完成。gethostbyname 函数是 glibc 库中提供一个域名解析的函数。
存在问题:范围结果是 static 类型变量,存在数据被覆盖的情况。
它的原型为:
struct hostent *gethostbyname(const char *name);
入参:字符串name可取的值分为三种类型:
- 十进制数字格式的IPv4地址
- 十六进制数字格式的IPv6地址
- 域名
返回值
返回值为指向struct hostent类型的指针,调用者显然没有提前分配它,那么该结构一定是有内部实现分配的,所以该函数是不可重入的。struct hostent结构定义如下:
struct hostent {
char h_name; // official name of host
char *h_aliases; // alias list
int h_addrtype; // host address type
int h_length; // length of address
char *h_addr_list; // list of addresses
}
#define h_addr h_addr_list[0] // for backward compatibility
- h_name:代表正式名字
- h_aliases:指针数组,每个成员指向的字符串代表域名的别名,该数组以NULL结尾
- h_addrtype:表示解析后的地址类型,可取的值为AF_INET和AF_INET6
- h_length:地址长度,对于IPv4地址为4,对于IPv6为16字节
- h_addr_list:指针数组,每个成员指向一个解析出来的IP地址,该数组以NULL结尾
- h_addr:为了向后兼容,也可以用h_addr引用第一个解析结果
工作原理
gethostbyname(域名->IP)、gethostbyaddr(IP->域名)在解析的时候,系统默认会先到 /etc/hosts 文件中去寻找匹配信息,然后再到DNS服务器去解析,这个顺序是可以交换的,只要修改 /etc/host.conf 里的 host(本机) 和 bind(DNS服务器) 的顺序即可,例:
在DNS服务器配置正常的情况下,gethostbyname的参数为“www.baidu.com”能正常解析,禁掉DNS就不能正常工作了。但是如果把它和 IP 的映射对添加到 /etc/hosts 文件中,则函数又能正常工作了。例如我们在 /etc/hosts 文件里添加这样一组映射:
12.34.56.78 www.baidu.com
则在执行这两个函数的时候,就会返回映射对对应的信息。
注:iHost进行环境配置便是利用的 /etc/hosts里进行绑定
MX记录(邮件交换记录)实现
res_init //读取配置文件/etc/resolv.conf获取默认域名、搜索顺序和名称服务器 地址
res_mkquery //制作标准查询消息 (DNS 数据包)用于名称服务器
res_send //发送查询或更新消息 到名称服务器并检索响应
skipName //points to the answer section
->dn_expand
skipToData
->skipName
->GETSHORT/GETLONG
dn_expand //将压缩域名扩展为完整域名
实质:
调用DNS解析器函数实现获取MX记录
DNS解析器
定义
解析器是C库中的一系例程,用于访问DNS系统
解析器主要函数
具体信息请参考手册,man resolver
int res_query(const char *dname, int class, int type, unsigned char *answer, int anslen); int res_search(const char *dname, int class, int type, unsigned char *answer, int anslen);
int res_querydomain(const char *name, const char *domain, int class, int type, unsigned char *answer, int anslen);
int res_mkquery(int op, const char *dname, int class, int type, char *data, int datalen, struct rrec *newrr, char *buf, int buflen);
int res_send(const char *msg, int msglen, char *answer, int anslen);
int dn_comp(unsigned char *exp_dn, unsigned char *comp_dn, int length, unsigned char **dnptrs, unsigned char **lastdnptr);
int dn_expand(unsigned char *msg, unsigned char *eomorig, unsigned char *comp_dn, char *exp_dn, int length);
1.res_query 发送域查询
参数
domain_name
(输入)指向域名的指针。
.class
(输入)要查找的数据的类。请参阅 res_mkquery() 或 值。
类型 type
(输入)发出的请求的类型。请参阅 res_mkquery() 或 值。
answer_buffer
(输出)指向存储响应的地址的指针。
answer_buffer_length
(输入)答案区域的大小。
2.res_search 搜索域名
参数
domain_name
(输入)指向域名的指针。
.class
(输入)要查找的数据的类。请参阅 res_mkquery() 或 值。
类型 type
(输入)发出的请求的类型。请参阅 res_mkquery() 或 值。
answer_buffer
(输出)指向存储响应的地址的指针。
answer_buffer_length
(输入)答案区域的大小。
3.res_mkquery 将域查询放在缓冲区中
参数
操作 op
(输入)所需的查询操作。这被放入操作码中的 数据包的标头。下面列出了常用值(请参阅 表示所有可能的值):ns_o_query或查询标准查询请求。(此值几乎始终使用)
domain_name
(输入)指向域名称的指针。
.class
(输入)要查找的数据的类。下面列出了常见值 :ns_c_in或C_IN指定 ARPA 互联网。ns_c_any或C_ANY这是通配符匹配。
类型 type
(输入)发出的请求的类型。下面列出了常用值(请参阅 表示所有可能的值):ns_t_a或T_A主机地址。ns_t_aaaa IPv6 地址。ns_t_ns或T_NS权威服务器。ns_t_cname或T_CNAME规范名称。ns_t_soa或T_SOA权限区域的开始。ns_t_wks或T_WKS知名服务。ns_t_ptr或T_PTR域名指针。ns_t_hinfo或T_HINFO主机信息。ns_t_mx或T_MX邮件路由信息。ns_t_txt或T_TXT文本字符串。ns_t_any或T_ANY通配符匹配。
search_data
(输入)包含反向查询数据的缓冲区。它是空的 IQUERY 以外的类型。
search_data_length
(输入)search_data的长度。对于以下类型为空 查询。
保留 reserved
(输入)保留且当前未使用的参数。它始终为空 指针(为兼容性而定义)。
query_buffer
(输出)指向包含查询的用户提供的位置的指针 消息。
query_buffer_length
(输入)query_buffer的长度。
4.res_send 发送缓冲域查询或更新
参数
query_buffer
(输入)指向查询或更新消息的指针。
query_buffer_length
(输入)query_buffer的长度。
answer_buffer
(输出)指向存储响应位置的指针。
answer_buffer_length
(输入)answer_buffer的大小。
5.dn_comp 压缩扩展域名字
参数
expanded_domain_name
(输入)指向展开的域名的指针。
compressed_domain_name
(输出)指向压缩域名所在位置的指针 数据处理。
answer_buffer_length
(输入)compressed_domain_name缓冲区的大小。
domain_name_pointers
(输入)指向指向以前压缩域的指针数组的指针 当前邮件中的名称。
last_domain_name
(输入)指向domain_name_pointers 指定的数组末尾的指针。
6.dn_expand 展开域名
参数
message_pointer
(输入)指向 DNS 数据包开头的指针。
end_of_message
(输入)指向 DNS 数据包末尾的指针。
compressed_domain_name
(输入)指向 DNS 中压缩域名的指针 包。
expanded_domain_name
(输出)指向展开的域名的指针。
answer_buffer_length
(输入)expanded_domain_name缓冲区的大小。
解析器配置文件
一般为/etc/resolv.conf,关于resolv.conf的说明请参考手册,man resolv.conf
总结:
解析器按照search搜索列表进行循环的搜索解析,直到解析成功为止;
形成search搜索列表的方式有很多,如果显式设置search,则用search;如果没有显式设置search,如果设置了domain,则用domain作为search搜索列表;如果domain和search都没有设置,则使用gethostname返回的主机名的域名部分作为search搜索列表
/etc/resolv.conf参数:
1. nameserver
解析器使用的名称服务器的ip地址,如下配置:
nameserver 192.168.198.2
可以配置多个,解析器使用该选项的规则是按顺序查询,查询超时,则查询下一个,直到查询最后一个nameserver
如果没有nameserver配置项,解析器使用本地主机上的名称服务器进行解析
2. domain
本地域名设置
(1) 当查询名称时,没有使用FQDN全名称时(后缀点),查询名称时先在名称后面加上domain后查询,如下示例:
设置
domain james.cn
ping apache时,实际上会查询apache.james.cn
ping apache.时,只会查询apache.(因为后缀点)
ping apache.xyz时,先查询apache.xyz,然后查询apache.xyz.james.cn
(2) 当没有设置domain时,会以gethostname函数返回的名称的域名后缀为domain,如下示例:
未设置domain
hostname为:bindforward.james.cn
ping apache时,会查询apache.james.cn
3. search
主机名搜索列表
(1) 当没有定义search时,缺省的search为本地域名domain
(2) 当定义了search时,以search为顺序进行搜索,domain一般将不起作用,示例如下:
domain james.cn search james.com.cn james.cn
ping xyz时,会查询xyz.james.com.cn,然后查询xyz.james.cn(domain项没起做作用)
ping xyz.yy时,会查询xyz.yy,再查询xyz.yy.james.com.cn,然后查询xyz.yy. james.cn
(domain项目仍然没有起作用)
search最多可以定义6个域名搜索项
4. options
选项很多,常用的有timeout、attempts
- timeout设置超时
- attempts设置重试次数
如下设置:
options timeout:2 attempts:2
5. nsswitch.conf配置文件
/etc/nsswitch.conf配置文件中的hosts配置项目,如下
hosts: files dns
定义了主机名解析的顺序,先使用hosts文件,再使用dns解析
同样host.conf(/etc/host.conf)也有此功能,如下:
order hosts,bind
定义了解析器解析方法的顺序,先使用hosts文件,再使用bind