获取主机名、IP——gethostent(),gethostbyname(),gethostbyaddr()

时间:2021-04-14 13:34:23

1、原理:查询 /etc/hosts 等文件及 DNS or NIS 服务器
The  domain  name  queries carried out by gethostbyname() and gethostbyaddr() use a combination of any or all of the name server named(8), a broken out line from /etc/hosts, and the Network Information Service (NIS or YP), depending upon the contents of the order line in /etc/host.conf.  The default  action  is  to  query named(8), followed by/etc/hosts.(ubuntu man page)

The network configuration information returned by these functions can be kept in a number of places. They can be kept in static files (/etc/hosts, /etc/services, etc.), or they can be managed by a name service, such as DNS (Domain Name System) or NIS (Network Information Service). 

2、说明:
1) gethostbyname*() 和 gethostbyaddr*() 已经过时,用 getaddrinfo() 和 getnameinfo() 替代
The  gethostbyname*()  and  gethostbyaddr*()  functions  are  obsolete. Applications should use getaddrinfo(3) and getnameinfo(3) instead.
POSIX.1-2001 specifies gethostbyname(), gethostbyaddr(), sethostent(), endhostent(), gethostent(), and h_errno;
gethostbyname(), gethostbyaddr(), and h_errno are marked  obsolescent  in  that standard.
POSIX.1-2008 removes the specifications of gethostbyname(), gethostbyaddr(), and h_errno, recommending the use of getaddrinfo(3) and getnameinfo(3) instead.
(ubuntu man page)

The gethostbyname and gethostbyaddr functions only support IPv4. The API for resolving IPv6 addresses went through several iterations, as will be described in Section 11.20; the final result is the getaddrinfo function.(UNIX Network Programming Volume 1, Third Edition )

2) 返回只想静态数据区的指针
The value that gethostbyname returns points to a static structure within the library. You must copy the information from this structure before you make further gethostbyname , gethostbyaddr , or gethostent calls.
(ubuntu man page)

3) http://beej.us/guide/bgnet/output/html/multipage/gethostbynameman.html


4) gethostent(): gets the next entry in the host file, 即每次获取下一个entry, 所以编写程序是通常都用一个 while 循环来获取所有 entry

----------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 from name server. */
#if defined __USE_MISC || defined __USE_GNU
# defineh_addrh_addr_list[0] /* Address, for backward compatibility.*/
#endif
};

----------gethostent()

/**
* gethostent()
* OS: Ubuntu 11.04 Server
*/
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>// inet_ntop()

static void printhost(struct hostent *host);

int
main(int argc, char *argv[])
{
struct hostent *host = NULL;
sethostent(1);
while( (host = gethostent()) != 0 )
{
printhost(host);
printf("\n");
}
endhostent();
return EXIT_SUCCESS;
}
static void printhost(struct hostent *host)
{
char **aliases = NULL;
char **addr_list = NULL;
char addr_p[INET_ADDRSTRLEN];// IPv4

/* print host name and aliases */
printf("hostname: %s\n", host->h_name);
for(aliases = host->h_aliases; *aliases; aliases++)
{
printf("alternate name: %s\n", *aliases);
}

/* print address type and length */
if(host->h_addrtype == AF_INET)
{
printf("address type: AF_INET\n");
}
else
{
printf("Not an IPv4 address.\n");
}
printf("address length: %d\n", host->h_length);

/* print address list */
//printf("%d\n", sizeof(*(host->h_addr_list)));
printf("%x\n", *(int *)(*(host->h_addr_list)));
for(addr_list = host->h_addr_list; *addr_list; addr_list++)
{
printf("address: %s\n", inet_ntop(host->h_addrtype, *addr_list, addr_p, INET_ADDRSTRLEN));
}
}
/*
hostname: localhost
address type: AF_INET
address length: 4
100007f
address: 127.0.0.1

hostname: Ubuntu-Server.localdomain
alternate name: Ubuntu-Server
address type: AF_INET
address length: 4
101007f
address: 127.0.1.1

hostname: ip6-localhost
alternate name: ip6-loopback
address type: AF_INET
address length: 4
100007f
address: 127.0.0.1
*/
Q: 为什么只能得到 127.0.0.1 这个IP?

A: /etc/hosts 文件内容如下:

127.0.0.1       localhost
127.0.1.1 Ubuntu-Server.localdomain Ubuntu-Server
...
这跟这组函数的工作原理有关


----------gethostbyname()

/**
* gethostbyname()
* OS: Ubuntu 11.04 Server
*/
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>/* inet_ntop() */

int main(int argc, char *argv[])
{
struct hostent *h_ent = NULL;
char addr_p[INET_ADDRSTRLEN];
char **addr_list = NULL;

/* Verify a "hostname" parameter was supplied */
if(argc != 2)
{
printf("please input the hostname.\n");
exit(EXIT_FAILURE);
}

/* call gethostbyname() with a host name. gethostbyname() returns a pointer to a hostent struct or NULL. */
h_ent = gethostbyname(argv[1]);
if(h_ent == NULL)
{
printf("%s was not resolved.\n", argv[1]);
exit(EXIT_FAILURE);
}

for(addr_list = h_ent->h_addr_list; *addr_list; addr_list++)
{
inet_ntop(AF_INET, *(addr_list), addr_p, INET_ADDRSTRLEN);
printf("hostname: %s, was resolved to: %s\n", argv[1], addr_p);
}

return 0;
}
/*
$ ./a.out baidu.com
hostname: baidu.com, was resolved to: 220.181.111.85
hostname: baidu.com, was resolved to: 220.181.111.86
hostname: baidu.com, was resolved to: 123.125.114.144

add a line in /etc/hosts:
111.111.111.111 baidu.com
$ ./a.out baidu.com
hostname: baidu.com, was resolved to: 111.111.111.111
*/
Q: 为什么修改 /etc/hosts 之后出现上述结果?

A: The  domain  name  queries carried out by gethostbyname() and gethostbyaddr() use a combination of any or all of the name server named(8), a broken out line from /etc/hosts, and the Network Information Service (NIS or YP), depending upon the contents of the order line in /etc/host.conf. (ubuntu man page)

/etc/host.conf 文件内容如下:

# The "order" line is only used by old versions of the C library.
order hosts,bind
multi on
hosts 文件优先


----------gethostbyaddr()

/**
* gethostbyaddr()
* OS: Ubuntu 11.04 Server
*/
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>/* inet_pton() */

int main(int argc, char *argv[])
{
struct hostent *h_ent = NULL;
char addr_n[sizeof(struct in_addr)];// or struct in_addr addr_n;
int status;

/* Verify a "ip address" parameter was supplied */
if(argc != 2)
{
printf("please input the ip address.\n");
exit(EXIT_FAILURE);
}

status = inet_pton(AF_INET, argv[1], addr_n);
if(status == 0)// invalid format
{
printf("The format of the ip address is invalid.\n");
exit(EXIT_FAILURE);
}
else if(status == -1)
{
printf("error: inet_pton\n");
exit(EXIT_FAILURE);
}

/* call gethostbyaddr() */
h_ent = gethostbyaddr(addr_n, sizeof(struct in_addr), AF_INET);
if(h_ent == NULL)
{
printf("error: gethostbyaddr\n");
exit(EXIT_FAILURE);
}

printf("%s was resolved to hostname: %s\n", argv[1], h_ent->h_name);

return 0;
}
/*
$ ./a.out 220.181.111.85# baidu.com
error: gethostbyaddr

add a line in /etc/hosts:
111.111.111.111 baidu.com
$ ./a.out 111.111.111.111
111.111.111.111 was resolved to hostname: baidu.com
*/