getaddrinfo()

Updated: April 19, 2023

Get socket address information

Synopsis:

#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo( const char * nodename,
                 const char * servname,
                 const struct addrinfo * hints,
                 struct addrinfo ** res );

Arguments:

nodename
The node name. A non-NULL nodename may be either a node name or a numeric host address string (i.e., a dotted-decimal IPv4 address or an IPv6 hex address.)
servname
The server name. A non-NULL servname may be either a server name or a decimal port number.
hints
A pointer to an addrinfo structure that provides hints about the type of socket you're supporting. See Using the hints argument for more information.
res
The address of a location where the function can store a pointer to a linked list of one or more addrinfo structures.

Library:

libsocket

Use the -l socket option to qcc to link against this library.

Description:

The getaddrinfo() function performs the functionality of gethostbyname() and getservbyname() but in a more sophisticated manner.

The nodename and servname arguments are either pointers to null-terminated strings or NULL. One or both of these two arguments must be a non-NULL pointer. Normally, a client scenario specifies both nodename and servname.

On success, the getaddrinfo() function stores, in the location pointed to by res, a pointer to a linked list of one or more addrinfo structures. You can process each addrinfo structure in this list by following the ai_next pointer until reaching a NULL pointer. Each addrinfo structure contains the corresponding ai_family, ai_socktype, and ai_protocol arguments for a call to the socket() function. The ai_addr argument of the addrinfo structure points to a filled-in socket address structure with a length specified by the ai_addrlen argument.

Note: If the name server isn't responding, there's a timeout of 1.5 minutes per name server.

Using the hints argument

You can optionally pass an addrinfo structure, pointed to by the hints argument, that provides hints concerning the type of socket that your application supports.

In this structure, all members—except ai_flags, ai_family, ai_socktype, and ai_protocol—must be zero or a NULL pointer. The addrinfo structure of the hints argument can accept various types of sockets:

To accept: Set: To:
Any protocol family ai_family PF_UNSPEC
Any socket type ai_socktype 0
Any protocol ai_protocol 0
All of the above (as well as setting ai_flags to 0) hints NULL

The hints argument defaults to all possibilities, but you can also use it to limit choices:

Using the ai_flags argument in the hints structure

You can set the ai_flags argument to further configure the hints structure. Settings for ai_flags include:

AI_PASSIVE
Set this bit if you plan to use the returned addrinfo structure in a call to bind(). In this call, if the nodename argument is a NULL pointer, then the IP address portion of the socket address structure ai_addr is set to INADDR_ANY for an IPv4 address or IN6ADDR_ANY_INIT for an IPv6 address.

If you don't set the AI_PASSIVE flag, you can use the returned addrinfo structure in a call to:

  • connect() — connectionless or connection-oriented protocol
  • sendto() — connectionless protocol
  • sendmsg() — connectionless protocol

In this case, if the nodename argument is a NULL pointer, then the IP address portion of the socket address structure ai_addr is set to the loopback address.

AI_CANONNAME
Set this bit if you want the ai_canonname argument of the first addrinfo structure to point to a null-terminated string containing the canonical name of the specified nodename.
AI_NUMERICHOST
Set this bit if you want to prevent any type of name resolution service (such as DNS) from being used. A non-NULL nodename string must be a numeric host address string; otherwise, getaddrinfo() returns EAI_NONAME.

Pitfalls

The arguments to getaddrinfo() must be sufficiently consistent and unambiguous or this function will return an error. Here are some problems you may encounter:

Note: The getaddrinfo() function dynamically allocates space for the following:
  • addrinfo structures
  • socket address structures
  • canonical node name strings pointed to by the addrinfo structures

Use freeaddrinfo() to free the addrinfo structures, and gai_strerror() to decipher error codes.

Returns:

Zero for success, or nonzero if an error occurs.

Errors:

To get an explanation of any error code, use gai_strerror().

Examples:

The following code tries to connect to www.kame.net service HTTP using a stream socket. It loops through all the addresses available, regardless of the address family. If the destination resolves to an IPv4 address, it uses a AF_INET socket. Similarly, it uses an AF_INET6 socket if it resolves to IPv6. Note that there aren't any hardcoded references to any particular address family; the code works even if getaddrinfo() returns addresses that aren't IPv4/v6.

struct addrinfo hints, *res, *res0;
int error;
int s;
const char *cause = NULL;

memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo("www.kame.net", "http", &hints, &res0);
if (error) {
    err1(1, "%s", gai_strerror(error));
    /*NOTREACHED*/
}
s = -1;
for (res = res0; res; res = res->ai_next) {
    s = socket(res->ai_family, res->ai_socktype,
        res->ai_protocol);
    if (s < 0) {
        cause = "socket";
        continue;
    }

    if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
        cause = "connect";
        close(s);
        s = -1;
        continue;
    }

    break;  /* okay we got one */
}
if (s < 0) {
    err(1, cause);
    /*NOTREACHED*/
}
freeaddrinfo(res0);

The following example tries to open a wildcard-listening socket onto the HTTP service for all of the available address families:

struct addrinfo hints, *res, *res0;
int error;
int s[MAXSOCK];
int nsock;
const char *cause = NULL;

memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(NULL, "http", &hints, &res0);
if (error) {
    err1(1, "%s", gai_strerror(error));
    /*NOTREACHED*/
}
nsock = 0;
for (res = res0; res && nsock < MAXSOCK; res = res->ai_next) {
    s[nsock] = socket(res->ai_family, res->ai_socktype,
        res->ai_protocol);
    if (s[nsock] < 0) {
        cause = "socket";
        continue;
    }

    if (connect(s[nsock], res->ai_addr, res->ai_addrlen) < 0) {
        cause = "connect";
        close(s[nsock]);
        continue;
    }

    nsock++;
}
if (nsock == 0) {
    err(1, cause);
    /*NOTREACHED*/
}
freeaddrinfo(res0);

Files:

/etc/nsswitch.conf
Name-service switch configuration file.

Classification:

POSIX 1003.1

Safety:  
Cancellation point Yes
Interrupt handler No
Signal handler No
Thread Yes