GETADDRINFO - Linux手册页
Linux程序员手册 第3部分
更新日期: 2020-04-11
名称
getaddrinfo,freeaddrinfo,gai_strerror-网络地址和服务翻译
语法
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo(struct addrinfo *res); const char *gai_strerror(int errcode);
glibc的功能测试宏要求(请参阅feature_test_macros(7)):
getaddrinfo(),freeaddrinfo(),gai_strerror():
从glibc 2.22开始:_POSIX_C_SOURCE> = 200112L
Glibc 2.21及更早版本:_POSIX_C_SOURCE
说明
给定标识Internet主机和服务的节点和服务,getaddrinfo()返回一个或多个addrinfo结构,每个结构都包含可以在对bind(2)或connect(2)的调用中指定的Internet地址。 getaddrinfo()函数将gethostbyname(3)和getservbyname(3)函数提供的功能组合到一个接口中,但是与后面的函数不同,getaddrinfo()是可重入的,并允许程序消除IPv4对IPv6的依赖性。
getaddrinfo()使用的addrinfo结构包含以下字段:
struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; };
hints参数指向一个addrinfo结构,该结构指定用于选择由res指向的列表中返回的套接字地址结构的条件。如果提示不是NULL,则指向一个addrinfo结构,其ai_family,ai_socktype和ai_protocol指定限制getaddrinfo()返回的套接字地址集的条件,如下所示:
- ai_family
- 该字段为返回的地址指定所需的地址族。该字段的有效值包括AF_INET和AF_INET6。值AF_UNSPEC指示getaddrinfo()应该返回可与节点和服务一起使用的任何地址族(例如,IPv4或IPv6)的套接字地址。
- ai_socktype
- 该字段指定首选的套接字类型,例如SOCK_STREAM或SOCK_DGRAM。在该字段中指定0表示getaddrinfo()可以返回任何类型的套接字地址。
- ai_protocol
- 该字段指定返回的套接字地址的协议。在此字段中指定0表示getaddrinfo()可以返回具有任何协议的套接字地址。
- ai_flags
- 该字段指定其他选项,如下所述。通过对它们进行按位或运算来指定多个标志。
提示所指向的结构中的所有其他字段必须包含0或空指针(视情况而定)。
将提示指定为NULL等同于将ai_socktype和ai_protocol设置为0; ai_family to AF_UNSPEC;和ai_flags到(AI_V4MAPPED || AI_ADDRCONFIG)。 (POSIX为ai_flags指定了不同的默认值;请参见"注释"。)节点指定了数字网络地址(对于IPv4,inet_aton(3)支持数字和点符号;对于IPv6,十六进制字符串格式由inet_pton(3)支持)或网络主机名,可以查找并解析其网络地址。如果hints.ai_flags包含AI_NUMERICHOST标志,则节点必须是数字网络地址。 AI_NUMERICHOST标志禁止任何可能冗长的网络主机地址查找。
如果在hints.ai_flags中指定了AI_PASSIVE标志,并且node为NULL,则返回的套接字地址将适合bind(2)接受将接受(2)连接的套接字。返回的套接字地址将包含"通配符地址"(对于IPv4地址为INADDR_ANY,对于IPv6地址为IN6ADDR_ANY_INIT)。通配符地址由打算接受任何主机网络地址上的连接的应用程序(通常是服务器)使用。如果node不为NULL,则AI_PASSIVE标志将被忽略。
如果未在hints.ai_flags中设置AI_PASSIVE标志,则返回的套接字地址将适用于connect(2),sendto(2)或sendmsg(2)。如果node为NULL,则网络地址将设置为回送接口地址(对于IPv4地址为INADDR_LOOPBACK,对于IPv6地址为IN6ADDR_LOOPBACK_INIT);打算与在同一主机上运行的对等方进行通信的应用程序使用此地址。
服务在每个返回的地址结构中设置端口。如果此参数是服务名称(请参见services(5)),则将其转换为相应的端口号。也可以将此参数指定为十进制数,将其简单地转换为二进制数。如果service为NULL,则返回的套接字地址的端口号将保持未初始化状态。如果在hints.ai_flags中指定了AI_NUMERICSERV,并且service不为NULL,则service必须指向包含数字端口号的字符串。在已知不需要该名称的情况下,此标志用于禁止调用名称解析服务。
节点或服务中的任何一个都可以为NULL,但不能同时为两者。
getaddrinfo()函数分配并初始化一个addrinfo结构的链接列表,该列表针对与节点和服务匹配的每个网络地址(受提示施加的任何限制),并返回一个指向res列表开头的指针。链接列表中的项目通过ai_next字段链接。
链表可能具有多个addrinfo结构的原因有很多,其中包括:网络主机是多宿主的,可以通过多种协议访问(例如,AF_INET和AF_INET6);或者可以从多种套接字类型(例如,一个SOCK_STREAM地址和另一个SOCK_DGRAM地址)获得相同的服务。通常,应用程序应尝试按返回地址的顺序使用地址。 getaddrinfo()中使用的排序功能在RFC 3844中定义;可以通过编辑/etc/gai.conf(自glibc 2.5起可用)来调整特定系统的顺序。
如果hints.ai_flags包含AI_CANONNAME标志,则将返回列表中第一个addrinfo结构的ai_canonname字段设置为指向主机的正式名称。
每个返回的addrinfo结构的其余字段初始化如下:
- *
- ai_family,ai_socktype和ai_protocol字段返回套接字创建参数(即,这些字段与socket(2)的相应参数含义相同)。例如,ai_family可能返回AF_INET或AF_INET6。 ai_socktype可能返回SOCK_DGRAM或SOCK_STREAM; ai_protocol返回套接字的协议。
- *
- 指向套接字地址的指针放在ai_addr字段中,而套接字地址的长度(以字节为单位)放在ai_addrlen字段中。
如果hints.ai_flags包含AI_ADDRCONFIG标志,则仅当本地系统配置了至少一个IPv4地址时,才在res指向的列表中返回IPv4地址;仅当本地系统具有至少一个IPv6地址时才返回IPv6地址。配置。在这种情况下,环回地址不视为已配置地址有效。该标志在例如仅IPv4的系统上很有用,以确保getaddrinfo()不会返回在connect(2)或bind(2)中始终失败的IPv6套接字地址。
如果hints.ai_flags指定AI_V4MAPPED标志,并且hints.ai_family被指定为AF_INET6,并且找不到匹配的IPv6地址,则在res指向的列表中返回IPv4映射的IPv6地址。如果在hints.ai_flags中同时指定了AI_V4MAPPED和AI_ALL,则在res指向的列表中返回IPv6和IPv4映射的IPv6地址。如果未同时指定AI_V4MAPPED,则将忽略AI_ALL。
freeaddrinfo()函数释放为动态分配的链表res分配的内存。
Extensions to getaddrinfo() for Internationalized Domain Names
从glibc 2.3.4开始,对getaddrinfo()进行了扩展,以选择性地允许传入和传出的主机名与国际化域名(IDN)格式进行透明转换(请参阅RFC 3490,应用程序中的国际化域名(IDNA)) 。定义了四个新标志:
- AI_IDN
- 如果指定了此标志,则在必要时将node中给定的节点名转换为IDN格式。源编码是当前语言环境的编码。
- 如果输入名称包含非ASCII字符,则使用IDN编码。包含非ASCII字符的节点名称的那些部分(由点分隔)在传递给名称解析功能之前使用ASCII兼容编码(ACE)进行编码。
- AI_CANONIDN
- 成功查找名称后,如果指定了AI_CANONNAME标志,则getaddrinfo()将返回与传递回的addrinfo结构值相对应的节点的规范名称。返回值是名称解析函数返回的值的精确副本。
- 如果名称是使用ACE编码的,则它将包含名称的一个或多个组成部分的xn--前缀。要将这些组件转换为可读形式,除了AI_CANONNAME之外,还可以传递AI_CANONIDN标志。使用当前语言环境的编码对结果字符串进行编码。
- AI_IDN_ALLOW_UNASSIGNED, AI_IDN_USE_STD3_ASCII_RULES
- 设置这些标志将分别使IDNA_ALLOW_UNASSIGNED(允许未分配的Unicode代码点)和IDNA_USE_STD3_ASCII_RULES(检查输出以确保它符合STD3主机名)标志分别用于IDNA处理。
返回值
如果成功,getaddrinfo()将返回0,或者返回以下非零错误代码之一:
- EAI_ADDRFAMILY
- 指定的网络主机在请求的地址族中没有任何网络地址。
- EAI_AGAIN
- 名称服务器返回了临时故障指示。稍后再试。
- EAI_BADFLAGS
- hints.ai_flags包含无效标志;或者,hints.ai_flags包含AI_CANONNAME,名称为NULL。
- EAI_FAIL
- 名称服务器返回永久故障指示。
- EAI_FAMILY
- 不支持请求的地址族。
- EAI_MEMORY
- 内存不足。
- EAI_NODATA
- 指定的网络主机存在,但没有定义任何网络地址。
- EAI_NONAME
- 节点或服务未知;或节点和服务均为NULL;或或在hints.ai_flags中指定了AI_NUMERICSERV,并且服务不是数字端口号字符串。
- EAI_SERVICE
- 所请求的服务不适用于所请求的套接字类型。它可能通过另一种套接字类型可用。例如,如果服务是" shell"(仅在流套接字上可用的服务),并且hints.ai_protocol是IPPROTO_UDP,或者hints.ai_socktype是SOCK_DGRAM,则可能发生此错误。否则,如果service不为NULL,并且hints.ai_socktype为SOCK_RAW(不支持服务概念的套接字类型),则可能发生错误。
- EAI_SOCKTYPE
- 不支持请求的套接字类型。例如,如果hints.ai_socktype和hints.ai_protocol不一致(例如,分别为SOCK_DGRAM和IPPROTO_TCP),就会发生这种情况。
- EAI_SYSTEM
- 其他系统错误,请检查errno以获取详细信息。
gai_strerror()函数将这些错误代码转换为人类可读的字符串,适用于错误报告。
文件
/etc/gai.conf
属性
有关本节中使用的术语的说明,请参见attribute(7)。
Interface | Attribute | Value |
getaddrinfo() | Thread safety | MT-Safe env locale |
freeaddrinfo(),gai_strerror() | Thread safety | MT-Safe |
遵循规范
POSIX.1-2001,POSIX.1-2008。 getaddrinfo()函数记录在RFC 2553中。
备注
getaddrinfo()支持用于指定IPv6作用域ID的address%scope-id表示法。
自glibc 2.3.3起,AI_ADDRCONFIG,AI_ALL和AI_V4MAPPED可用。自glibc 2.3.4起提供AI_NUMERICSERV。
根据POSIX.1,将提示指定为NULL应该会导致ai_flags假定为0.在这种情况下,GNU C库假定值为(AI_V4MAPPED | AI_ADDRCONFIG),因为该值被认为是对规范的改进。
示例
以下程序演示了getaddrinfo(),gai_strerror(),freeaddrinfo()和getnameinfo(3)的用法。这些程序是UDP数据报的回显服务器和客户端。
Server program
#include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netdb.h> #define BUF_SIZE 500 int main(int argc, char *argv[]) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s; struct sockaddr_storage peer_addr; socklen_t peer_addr_len; ssize_t nread; char buf[BUF_SIZE]; if (argc != 2) { fprintf(stderr, "Usage: %s port\n", argv[0]); exit(EXIT_FAILURE); } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; s = getaddrinfo(NULL, argv[1], &hints, &result); if (s != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); exit(EXIT_FAILURE); } /* getaddrinfo() returns a list of address structures. Try each address until we successfully bind(2). If socket(2) (or bind(2)) fails, we (close the socket and) try the next address. */ for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) break; /* Success */ close(sfd); } if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not bind\n"); exit(EXIT_FAILURE); } freeaddrinfo(result); /* No longer needed */ /* Read datagrams and echo them back to sender */ for (;;) { peer_addr_len = sizeof(struct sockaddr_storage); nread = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &peer_addr, &peer_addr_len); if (nread == -1) continue; /* Ignore failed request */ char host[NI_MAXHOST], service[NI_MAXSERV]; s = getnameinfo((struct sockaddr *) &peer_addr, peer_addr_len, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV); if (s == 0) printf("Received %zd bytes from %s:%s\n", nread, host, service); else fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s)); if (sendto(sfd, buf, nread, 0, (struct sockaddr *) &peer_addr, peer_addr_len) != nread) fprintf(stderr, "Error sending response\n"); } }
Client program
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUF_SIZE 500 int main(int argc, char *argv[]) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s, j; size_t len; ssize_t nread; char buf[BUF_SIZE]; if (argc < 3) { fprintf(stderr, "Usage: %s host port msg...\n", argv[0]); exit(EXIT_FAILURE); } /* Obtain address(es) matching host/port */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ hints.ai_flags = 0; hints.ai_protocol = 0; /* Any protocol */ s = getaddrinfo(argv[1], argv[2], &hints, &result); if (s != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); exit(EXIT_FAILURE); } /* getaddrinfo() returns a list of address structures. Try each address until we successfully connect(2). If socket(2) (or connect(2)) fails, we (close the socket and) try the next address. */ for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; /* Success */ close(sfd); } if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not connect\n"); exit(EXIT_FAILURE); } freeaddrinfo(result); /* No longer needed */ /* Send remaining command-line arguments as separate datagrams, and read responses from server */ for (j = 3; j < argc; j++) { len = strlen(argv[j]) + 1; /* +1 for terminating null byte */ if (len > BUF_SIZE) { fprintf(stderr, "Ignoring long message in argument %d\n", j); continue; } if (write(sfd, argv[j], len) != len) { fprintf(stderr, "partial/failed write\n"); exit(EXIT_FAILURE); } nread = read(sfd, buf, BUF_SIZE); if (nread == -1) { perror("read"); exit(EXIT_FAILURE); } printf("Received %zd bytes: %s\n", nread, buf); } exit(EXIT_SUCCESS); }
另外参见
getaddrinfo_a(3),gethostbyname(3),getnameinfo(3),inet(3),gai.conf(5),主机名(7),ip(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。