1.域名系统
域名系统(Domain Name System,DNS)主要用于主机名与IP地址间的映射。主机名可以是简单名字,如solaris或bsdi,也可以是全限定域名FQDN(Fully Qualified Domain Name),如solaris.kohala.com
1).资源记录
DNS中的条目称为资源记录RR(resource record),一般感兴趣的有如下几个:
- A A记录将主机名映射为32位的IPv4地址。
- AAAA AAAA记录将主机名映射为128位的IPv6地址。
- PTR PTR记录(称为"指针记录")将IP地址映射为主机名。对于IPv4地址,32位地址的四个字节顺序反转,每个字节都转换成他的十进制ASCII值(0~255),然后附上in-addr.arpa,结果串用于PTR查询。对于IPv6地址,128位地址中的32个4位组顺序发转,每组被转换成相应的十六进制ASCII值(0~9, a~f),并附上ip6.int。
- MX MX记录指定一主机作为某主机的“邮件交换器”。
- CNAME CNAME代表“canonical name(规范名字)”,其常见的用法是为常用服务如ftp和www指派一个CNAME记录。
2).解析器和名字服务器
组织运行一个或多个名字服务器(name server), 它们通常就是所谓的BIND(Berkeley Internet Name Domain)程序。各种应用程序,如本书中我们编写的客户和服务器程序,通过调用称为解析器(resolver)的库中的函数来与DNS服务器联系。最常见的解析器函数是gethostbyname和gethostbyaddr。
![](https://images2015.cnblogs.com/blog/886382/201703/886382-20170323233250643-1321648037.png)
2.gethostbyname函数
查找主机名最基本的函数是gethostbyname,如果调用成功,它就返回一个指向hostent的结构指针,该结构中含有所查找主机的所有IPv4地址,这个函数的局限是只能返回IPv4地址。
#include <netdb.h> struct hostent *gethostbyname (const char *hostname); //返回:若成功为非空指针,出错为NULL其设置h_error 本函数返回的空指针指向如下的hostent结构 struct hostent { char *h_name; /* official (canonical) name of host */ char **h_aliases; /* pointer to array of pointers to alias names */ int h_addrtype; /* host address type: AF_INET */ int h_length; /* length of address: 4 */ char **h_addr_list; /* ptr to array of ptrs with IPv4 addrs */ };
按照DNS的说法,gethostbyname执行的是对A记录查询。它只能返回IPv4地址
hostent结构如下所示
![](https://images2015.cnblogs.com/blog/886382/201703/886382-20170323233349658-1979058400.png)
gethostbyname与我们所介绍的其他套接口函数不同之处在于:当发生错误时,他不设置errno,而是将全局整数h_errno设置为定义在头文件<netdb.h>中的下列常值中的一个:
- HOST_NOT_FOUND
- TRY_AGAIN
- NO_RECOVERTY
- NO_DATA(等同于NO_ADDRESS)
#include <netdb.h> #include <string.h> #include <stdio.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <sys/socket.h> int main(int argc,char* argv[]){ if(argc<2){ printf("Usage:./client hostname "); return -1; } struct hostent *ht=NULL; ht=gethostbyname(argv[1]); if(ht){ int i=0; printf("get the host:%s addr ",argv[1]); printf("name:%s ",ht->h_name); printf("type:%s ",ht->h_addrtype==AF_INET?"AF_INET":"AF_INET6"); printf("length:%d ",ht->h_length); for(i=0;;i++){ if(ht->h_addr_list[i]!=NULL){ printf("IP:%s ",inet_ntoa((unsigned int *)ht->h_addr_list[i])); }else{ break; } } for(i=0;;i++){ if(ht->h_aliases[i]!=NULL){ printf("alisa %d:%s ",i,ht->h_aliases[i]); }else{ break; } } } return 0; }
3.gethostbyaddr函数
gethostbyaddr函数试图有一个二进制的IP地址找到相应的主机名,与gethostbyname函数行为刚好相反
#include <netdb.h> struct hostent *gethostbyaddr (const char *addr, socklen_t len, int family); //返回:成功为空指针,出错为NULL并设置h_errno
本函数返回一个指向与之前所叙述一样的hostent结构的指针。
参数addr实际上不是char *类型,而是一个指向存放IPv4地址的某个in_addr结构的指针;len参数是这个结构的大小:地域IPv4地址为4,family参数为AF_INET
4.getservbyname和getservbyport函数
1).像主机一样,服务也通常靠名字来认知,getservbyname函数用于根据给定名字查找相应服务,即:返回对应于给定服务名和协议名的相关服务信息(例如:端口号)
#include <netdb.h> struct servent * getservbyname(const char * servname, const char * protoname); //返回: 非空指针-成功,空指针-出错 本函数返回的空指针指向如下的servent结构 struct servent { char *s_name; /* official service name */ char **s_aliases; /* alias list */ int s-port; /* port number, network-byte order */ char *s_proto; /* protocol to use */ };
服务名servname必须指定,如果还指定了一个协议(即protoname为非空指针),则结果表项也必须有匹配的协议。
servent结构中我们关心的主要字段是端口号。既然端口号是以网络字节序返回的,把它存储于套接口地址结构时绝对不能调用htons,对此函数的典型调用是:
struct servent *sptr; sptr = getservbyname("domain", "udp"); /* DNS using UDP */ sptr = getservbyname("ftp", "tcp"); /* FTP using TCP */ sptr = getservbyname("ftp", NULL); /* FTP using TCP */ sptr = getservbyname("ftp", "udp"); /* this call will fail */
2).函数getservbyport用于给定端口号和可选协议查找相应服务
struct servent *getservbyport (int port, const char *protoname); //返回:成功非空指针,出错为NULL port参数的值必须为网络字节序,本函数的典型调用如下: struct servent *sptr; sptr = getservbyport (htons (53), "udp"); /* DNS using UDP */ sptr = getservbyport (htons (21), "tcp"); /* FTP using TCP */ sptr = getservbyport (htons (21), NULL); /* FTP using TCP */ sptr = getservbyport (htons (21), "udp"); /* this call will fail */ 对于UDP,由于没有服务使用端口21,所以最后一个调用将失败。