GETADDRINFO - Linux手册页

时间:2019-08-20 18:00:22  来源:igfitidea点击:

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)。

InterfaceAttributeValue
getaddrinfo()Thread safetyMT-Safe env locale
freeaddrinfo(),gai_strerror()Thread safetyMT-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/