getaddrinfo() returns 几个相同的结果
getaddrinfo() returns several identical results
在 /etc/hosts
我有:
127.0.0.1 localhost.localdomain localhost
::1 localhost.localdomain localhost
一个测试程序:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
const char* family_to_string(int family)
{
return family == AF_INET ? "AF_INET"
: family == AF_INET6 ? "AF_INET6"
: "<unknown>";
}
const char* socktype_to_string(int socktype)
{
return socktype == SOCK_STREAM ? "SOCK_STREAM"
: socktype == SOCK_DGRAM ? "SOCK_DGRAM"
: "<unknown>";
}
const char* protocol_to_string(int protocol)
{
return protocol == IPPROTO_TCP ? "IPPROTO_TCP"
: protocol == IPPROTO_UDP ? "IPPROTO_UDP"
: "<unknown>";
}
void print_addrinfo(struct addrinfo *ai)
{
int r;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
r = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
if (r)
{
fputs("getnameinfo\n", stderr);
exit(EXIT_FAILURE);
}
const char *family = family_to_string(ai->ai_family);
const char *socktype = socktype_to_string(ai->ai_socktype);
const char *protocol = protocol_to_string(ai->ai_protocol);
const char *family2 = family_to_string(ai->ai_addr->sa_family);
printf("flags=%i, family=%s, socktype=%s, protocol=%s, family=%s, host=%s, serv=%s\n",
ai->ai_flags, family, socktype, protocol, family2, hbuf, sbuf);
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
int r;
struct addrinfo hints;
struct addrinfo *result, *ai;
memset(&hints, 0, sizeof(struct addrinfo));
// hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
r = getaddrinfo("localhost", "8000", &hints, &result);
if (r)
{
fputs("getaddrinfo\n", stderr);
exit(EXIT_FAILURE);
}
ai = result;
while (ai)
{
print_addrinfo(ai);
ai = ai->ai_next;
}
exit(EXIT_SUCCESS);
}
当我不设置hints.ai_family
时,如预期的那样:
$ gcc -Wall -Wextra -Werror -pedantic -pedantic-errors -g -gdwarf-2 -g3 % && ./a.out
flags=0, family=AF_INET6, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET6, host=::1, serv=8000
flags=0, family=AF_INET, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET, host=127.0.0.1, serv=8000
与hints.ai_family
它returns两个相同的addrinfo
结构:
flags=0, family=AF_INET, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET, host=127.0.0.1, serv=8000
flags=0, family=AF_INET, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET, host=127.0.0.1, serv=8000
你能解释一下这是怎么回事吗?我认为 hints
充当过滤器。即本例只返回一个结果。
UPD /etc/nsswitch.conf
# Begin /etc/nsswitch.conf
passwd: compat mymachines systemd
group: compat mymachines systemd
shadow: compat
publickey: files
hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
networks: files
protocols: files
services: files
ethers: files
rpc: files
netgroup: files
# End /etc/nsswitch.conf
/etc/resolv.conf
:
# Generated by resolvconf
search Dlink
nameserver 192.168.0.1
192.168.0.1
是我的路由器。我的 IP 地址是 192.168.0.39
.
据我所知:
sysdeps/posix/getaddrinfo.c:2206
int
getaddrinfo (const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
sysdeps/posix/getaddrinfo.c:2304
last_i = gaih_inet (name, pservice, hints, end, &naddrs, &tmpbuf);
sysdeps/posix/getaddrinfo.c:342
static int
gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai,
unsigned int *naddrs, struct scratch_buffer *tmpbuf)
sysdeps/posix/getaddrinfo.c:595
rc = __gethostbyname2_r (name, AF_INET, &th,
tmpbuf->data, tmpbuf->length,
&h, &h_errno);
int
INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM
EXTRA_PARAMS)
status = DL_CALL_FCT (fct.l, (ADD_VARIABLES, resbuf, buffer, buflen,
&errno H_ERRNO_VAR EXTRA_VARIABLES));
nss/nss_files/files-hosts.c:385
enum nss_status
_nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop)
nss/nss_files/files-hosts.c:389
return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
errnop, herrnop, NULL, NULL);
nss/nss_files/files-hosts.c:334
enum nss_status
_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp, char **canonp)
这里我们得到第一个匹配项,如果 multi
在 /etc/host.conf
中是 on
- 其余的:
nss/nss_files/files-hosts.c:352
while ((status = internal_getent (stream, result, buffer, buflen, errnop,
herrnop, af, flags))
== NSS_STATUS_SUCCESS)
{
LOOKUP_NAME_CASE (h_name, h_aliases)
}
if (status == NSS_STATUS_SUCCESS
&& _res_hconf.flags & HCONF_FLAG_MULTI)
status = gethostbyname3_multi
(stream, name, af, result, buffer, buflen, errnop, herrnop, flags);
nss/nss_files/files-hosts.c:127
static enum nss_status
gethostbyname3_multi (FILE * stream, const char *name, int af,
struct hostent *result, char *buffer, size_t buflen,
int *errnop, int *herrnop, int flags)
nss/nss_files/files-hosts.c:162
status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,
tmp_buffer.length, errnop, herrnop, af,
flags);
static enum nss_status
internal_getent (FILE *stream, struct STRUCTURE *result,
char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
EXTRA_ARGS_DECL)
|| ! (parse_result = parse_line (p, result, data, buflen, errnop
EXTRA_ARGS)));
这里我们解析“::1 localhost.localdomain localhost”这一行。我们尝试
运行 inet_pton
超过 ::1
。那失败了,因为 af == AF_INET
。然后我们
注意 ::1
是 IPv6 环回地址,所以我们 return IPv4 环回
地址:
nss/nss_files/files-hosts.c:51
LINE_PARSER
nss/nss_files/files-hosts.c:59
if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
> 0)
af = af == AF_UNSPEC ? AF_INET : af;
else
{
if (...)
...
else if (af == AF_INET
&& inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
{
if (...)
...
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
也许 mailing list 上有人会回复,我们会学到更多。
在 /etc/hosts
我有:
127.0.0.1 localhost.localdomain localhost
::1 localhost.localdomain localhost
一个测试程序:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
const char* family_to_string(int family)
{
return family == AF_INET ? "AF_INET"
: family == AF_INET6 ? "AF_INET6"
: "<unknown>";
}
const char* socktype_to_string(int socktype)
{
return socktype == SOCK_STREAM ? "SOCK_STREAM"
: socktype == SOCK_DGRAM ? "SOCK_DGRAM"
: "<unknown>";
}
const char* protocol_to_string(int protocol)
{
return protocol == IPPROTO_TCP ? "IPPROTO_TCP"
: protocol == IPPROTO_UDP ? "IPPROTO_UDP"
: "<unknown>";
}
void print_addrinfo(struct addrinfo *ai)
{
int r;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
r = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
if (r)
{
fputs("getnameinfo\n", stderr);
exit(EXIT_FAILURE);
}
const char *family = family_to_string(ai->ai_family);
const char *socktype = socktype_to_string(ai->ai_socktype);
const char *protocol = protocol_to_string(ai->ai_protocol);
const char *family2 = family_to_string(ai->ai_addr->sa_family);
printf("flags=%i, family=%s, socktype=%s, protocol=%s, family=%s, host=%s, serv=%s\n",
ai->ai_flags, family, socktype, protocol, family2, hbuf, sbuf);
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
int r;
struct addrinfo hints;
struct addrinfo *result, *ai;
memset(&hints, 0, sizeof(struct addrinfo));
// hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
r = getaddrinfo("localhost", "8000", &hints, &result);
if (r)
{
fputs("getaddrinfo\n", stderr);
exit(EXIT_FAILURE);
}
ai = result;
while (ai)
{
print_addrinfo(ai);
ai = ai->ai_next;
}
exit(EXIT_SUCCESS);
}
当我不设置hints.ai_family
时,如预期的那样:
$ gcc -Wall -Wextra -Werror -pedantic -pedantic-errors -g -gdwarf-2 -g3 % && ./a.out
flags=0, family=AF_INET6, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET6, host=::1, serv=8000
flags=0, family=AF_INET, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET, host=127.0.0.1, serv=8000
与hints.ai_family
它returns两个相同的addrinfo
结构:
flags=0, family=AF_INET, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET, host=127.0.0.1, serv=8000
flags=0, family=AF_INET, socktype=SOCK_STREAM, protocol=IPPROTO_TCP, family=AF_INET, host=127.0.0.1, serv=8000
你能解释一下这是怎么回事吗?我认为 hints
充当过滤器。即本例只返回一个结果。
UPD /etc/nsswitch.conf
# Begin /etc/nsswitch.conf
passwd: compat mymachines systemd
group: compat mymachines systemd
shadow: compat
publickey: files
hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
networks: files
protocols: files
services: files
ethers: files
rpc: files
netgroup: files
# End /etc/nsswitch.conf
/etc/resolv.conf
:
# Generated by resolvconf
search Dlink
nameserver 192.168.0.1
192.168.0.1
是我的路由器。我的 IP 地址是 192.168.0.39
.
据我所知:
sysdeps/posix/getaddrinfo.c:2206
int
getaddrinfo (const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
sysdeps/posix/getaddrinfo.c:2304
last_i = gaih_inet (name, pservice, hints, end, &naddrs, &tmpbuf);
sysdeps/posix/getaddrinfo.c:342
static int
gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai,
unsigned int *naddrs, struct scratch_buffer *tmpbuf)
sysdeps/posix/getaddrinfo.c:595
rc = __gethostbyname2_r (name, AF_INET, &th,
tmpbuf->data, tmpbuf->length,
&h, &h_errno);
int
INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM
EXTRA_PARAMS)
status = DL_CALL_FCT (fct.l, (ADD_VARIABLES, resbuf, buffer, buflen,
&errno H_ERRNO_VAR EXTRA_VARIABLES));
nss/nss_files/files-hosts.c:385
enum nss_status
_nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop)
nss/nss_files/files-hosts.c:389
return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
errnop, herrnop, NULL, NULL);
nss/nss_files/files-hosts.c:334
enum nss_status
_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp, char **canonp)
这里我们得到第一个匹配项,如果 multi
在 /etc/host.conf
中是 on
- 其余的:
nss/nss_files/files-hosts.c:352
while ((status = internal_getent (stream, result, buffer, buflen, errnop,
herrnop, af, flags))
== NSS_STATUS_SUCCESS)
{
LOOKUP_NAME_CASE (h_name, h_aliases)
}
if (status == NSS_STATUS_SUCCESS
&& _res_hconf.flags & HCONF_FLAG_MULTI)
status = gethostbyname3_multi
(stream, name, af, result, buffer, buflen, errnop, herrnop, flags);
nss/nss_files/files-hosts.c:127
static enum nss_status
gethostbyname3_multi (FILE * stream, const char *name, int af,
struct hostent *result, char *buffer, size_t buflen,
int *errnop, int *herrnop, int flags)
nss/nss_files/files-hosts.c:162
status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,
tmp_buffer.length, errnop, herrnop, af,
flags);
static enum nss_status
internal_getent (FILE *stream, struct STRUCTURE *result,
char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
EXTRA_ARGS_DECL)
|| ! (parse_result = parse_line (p, result, data, buflen, errnop
EXTRA_ARGS)));
这里我们解析“::1 localhost.localdomain localhost”这一行。我们尝试
运行 inet_pton
超过 ::1
。那失败了,因为 af == AF_INET
。然后我们
注意 ::1
是 IPv6 环回地址,所以我们 return IPv4 环回
地址:
nss/nss_files/files-hosts.c:51
LINE_PARSER
nss/nss_files/files-hosts.c:59
if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
> 0)
af = af == AF_UNSPEC ? AF_INET : af;
else
{
if (...)
...
else if (af == AF_INET
&& inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
{
if (...)
...
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
也许 mailing list 上有人会回复,我们会学到更多。