一、引入
  • 认知计算机主机通常采用直观可读的名字。本书到目前为止的所有例子都有意使用IP地址而不是名字,这样我们能够确切地知道:对于诸如connect和sendto这样的函数,进入套接字地址结构的是什么内容;对于诸如accept和recvfrom这样的函数,返回的是什么内容。然而大多数应用程序应该处理名字而不是地址。当我们往IPv6转移时,这一点变得尤为正确,因为IPv6 地址(十六进制数串)比IPv4点分十进制数串要长得多。(上一节中的AAAA记录例子和 ip6.arpa域PTR记录例子足以说明问题了)
二、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 */
}

  •  查找主机名最基本的函数是gethostbyname。如果调用成功,它就返回一个指向hostent 结构的指针,该结构中含有所查找主机的所有IPv4地址。这个函数的局限是只能返回IPv4地址, 而后面讲解的getaddrinfo函数能够同时处理IPv4地址和IPv6地址。POSIX规范预警可能会在将来的某个版本中撤销gethostbyname函数。

成员

  • h_name:主机的规范名称。返回的h_name称为所查询主机的规范(canonical)名字。以上一节的CNAME记录例子为例,主机ftp.unpbook.com的规范名字是linux.unpbook.com。另外,如果我们在主机aix上 以一个非限定主机名(例如solaris)调用gethostbyname,那么作为规范名字返回的是它的 FQDN(即solaris.unpbook.com)
  • h_aliases:主机的别名列表(指针数组,每个元素以'\0'结尾)
  • h_addrtype:主机的地址类型(例如:AF_INET等)
  • h_length:地址长度
  • h_addr_list:IP值,一个主机名可能对应于多个IP地址,所以这是个IP列表
UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_地址解析函数三、gethostbyname函数

#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *hostname);

  • 功能:通过给定的主机名返回主机名对应的IP地址
  • 按照DNS的说法,此函数执行的是对A记录的查询,只能返回IPV4地址

返回值

  • 成功:成功返回struct  hostent结构体指针
  • 失败:当发生错误时,它不设置errno变量,,返回NULL且将全局整数变量h_errno设置为在头文件中定义的下列常值之一:
  • HOST_NOT_FOUND
  • TRY_AGAIN
  • NO_RECOVERY
  • NO_DATA(等同于NO_ADDRESS):NO_DATA错误表示指定的名字有效,但是它没有A记录。只有MX记录的主机名就是这样的 一个例子

局限性

  • 这个函数的局限是只能返回IPv4地址(POSIX规范预警可能会在将来的某个版本中撤销gethostbyname函数)
  • getaddrinfo函数能够同时处理IPv4和IPv6地址
  • 备注:gethostbyname函数不大可能真正消失,除非整个因特网改为使用IPv6,那可能是在遥 遥无期的将来。从POSIX规范中撤销该函数意在声明新的程序不该再使用它。我们鼓励在新 的程序中改用getaddrinfo函数

参数的注意事项:

  • 有些版本的gethostbyname函数实现允许hostname参数是一个点分十进制数串,也就是如下格式的调用是可行的:
hptr = gethostbyname("192.168.42.2"); 
  • 添加如此处理hostname参数的代码是因为Rlogin客户只接受主机名,并以它为参数调用 gethostbyname,而不接受点分十进制数串。POSIX规范允许但不强求如此处 理hostname参数,因此考虑可移植性的应用程序不能依赖这个特性

演示案例

#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<arpa/inet.h>


int main(int argc,char **argv)
{
char *ptr,**pptr;
char str[INET_ADDRSTRLEN];

struct hostent *hptr;

while(--argc>0)
{
ptr= *++argv;
if((hptr=gethostbyname(ptr))==NULL){
printf("gethostbyname error for host:%s,error code:%s\n",ptr,hstrerror(h_errno));
continue;
}

printf("offical hostname:%s\n",hptr->h_name);

if((*(pptr=hptr->h_aliases))!=NULL){
printf("alias hostname:");
for(;*pptr!=NULL;pptr++)
printf("%s\t",*pptr);
printf("\n");
}

switch(hptr->h_addrtype)
{
case AF_INET:
printf("ip type:AF_INET\n");
printf("ip length:%d\n",hptr->h_length);
if((*(pptr=hptr->h_addr_list))!=NULL){
printf("ip address:");
for(;*pptr!=NULL;pptr++)
printf("%s\t",inet_ntop(hptr->h_addrtype,*pptr,str,sizeof(str)));
printf("\n");
}
break;
default:
perror("unknown address type");
break;
}
}

exit(0);
}

 UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_struct  hostent_02UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_#include_03

UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_gethostbyaddr_04

  • 测试一个不存在的地址

UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_gethostbyname_05

四、gethostbyaddr函数

#include <netdb.h>
#include <sys/socket.h> /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);

//返回值:成功返回struct hostent结构体指针;出错返回NULL且设置h_errno

  • 功能:试图由一个二进制的IP地址找到相应的主机名,恰好与gethosybyname的行为相反
  • 按照DNS的说明,gethostbyaddr在in_addr.arpa域中向一个名字服务区查询PTR记录

参数

  • addr:网络格式的IP地址,需要强转(实际上不是char*类型,而是一个指向存放IPV4地址的某个in_addr结构体的指针)
  • len:参数1结构的大小
  • type:对于IPv4地址,此参数为AF_INET

返回值

  • 成功:成功返回struct  hostent结构体指针
  • 失败:当发生错误时,它不设置errno变量,,返回NULL且将全局整数变量h_errno设置为在头文件中定义的下列常值之一:
  • HOST_NOT_FOUND
  • TRY_AGAIN
  • NO_RECOVERY
  • NO_DATA(等同于NO_ADDRESS):NO_DATA错误表示指定的名字有效,但是它没有A记录。只有MX记录的主机名就是这样的 一个例子

演示案例

#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<arpa/inet.h>

int main(int argc, char **argv)
{
char *ptr, **pptr;
char str[INET_ADDRSTRLEN];
struct hostent *hptr;
u_int addr;

if (argc != 2)
{
printf("usage: %s IP-address\n",argv[0]);
exit(EXIT_FAILURE);
}
if ((int) (addr = inet_addr (argv[1])) == -1)//将字符串形式的点分十进制转换为网络IP格式
{
printf("IP-address must be of the form a.b.c.d\n");
exit (EXIT_FAILURE);
}

while(--argc>0)
{
ptr= *++argv;
if((hptr=gethostbyaddr((char *) &addr, sizeof (addr), AF_INET))==NULL){
printf("gethostbyname error for host:%s,error code:%s\n",ptr,hstrerror(h_errno));
continue;
}

printf("offical hostname:%s\n",hptr->h_name);

if((*(pptr=hptr->h_aliases))!=NULL){
printf("alias hostname:");
for(;*pptr!=NULL;pptr++)
printf("%s\t",*pptr);
printf("\n");
}

switch(hptr->h_addrtype)
{
case AF_INET:
printf("ip type:AF_INET\n");
printf("ip length:%d\n",hptr->h_length);
if((*(pptr=hptr->h_addr_list))!=NULL){
printf("ip address:");
for(;*pptr!=NULL;pptr++)
printf("%s\t",inet_ntop(hptr->h_addrtype,*pptr,str,sizeof(str)));
printf("\n");
}
break;
default:
perror("unknown address type");
break;
}
}
exit(0);
}

UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_#include_06

UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_gethostbyaddr_07

UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_#include_08

UNP编程:30---名字与地址转换之(地址解析函数:gethostbyname()、gethostbyaddr()、hstrerror()、struct  hostent)_#include_09

五、出错返回(hstrerror函数)
  • gethostbyname()函数与gethostbyaddr()函数在出错时会设置h_errno全局变量,如今多数解析器提供名为hstrerror的函数,它以某个h_errno值作为唯一的参数,返回的是一个const char *指针,指向相应错误的说明