Although we've been using getaddrinfo() in previous chapters, we'll discuss it in more detail here.
The declaration for getaddrinfo() is shown in the following code:
int getaddrinfo(const char *node,
const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
The preceding code snippet is explained as follows:
- node specifies a hostname or address as a string. Valid examples could be example.com, 192.168.1.1, or ::1.
- service specifies a service or port number as a string. Valid examples could be http or 80. Alternately, the null pointer can be passed in for service, in which case, the resulting address is set to port 0.
- hints is a pointer to a struct addrinfo, which specifies options for selecting the address. The addrinfo structure has the following fields:
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;
};
You should not assume that the fields are stored in the order listed in the previous code, or that additional fields aren't present. There is some variation between operating systems.
The call to getaddrinfo() looks at only four fields in *hints. The rest of the structure should be zeroed-out. The relevant fields are ai_family, ai_socktype, ai_protocol, and ai_flags:
- ai_family specifies the desired address family. It can be AF_INET for IPv4, AF_INET6 for IPv6, or AF_UNSPEC for any address family. AF_UNSPEC is defined as 0.
- ai_socktype could be SOCK_STREAM for TCP (see Chapter 3, An In-Depth Overview of TCP Connections), or SOCK_DGRAM for UDP (see Chapter 4, Establishing UDP Connections). Setting ai_socktype to 0 indicates that the address could be used for either.
- ai_protocol should be left to 0. Strictly speaking, TCP isn't the only streaming protocol supported by the socket interface, and UDP isn't the only datagram protocol supported. ai_protocol is used to disambiguate, but it's not needed for our purposes.
- ai_flags specifies additional options about how getaddrinfo() should work. Multiple flags can be used by bitwise OR-ing them together. In C, the bitwise OR operator uses the pipe symbol, |. So, bitwise OR-ing two flags together would use the (flag_one | flag_two) code.
Common flags you may use for the ai_flags field are:
- AI_NUMERICHOST can be used to prevent name lookups. In this case, getaddrinfo() would expect node to be an address such as 127.0.0.1 and not a hostname such as example.com. AI_NUMERICHOST can be useful because it prevents getaddrinfo() from doing a DNS record lookup, which can be slow.
- AI_NUMERICSERV can be used to only accept port numbers for the service argument. If used, this flag causes getaddrinof() to reject service names.
- AI_ALL can be used to request both an IPv4 and IPv6 address. The declaration for AI_ALL seems to be missing on some Windows setups. It can be defined as 0x0100 on those platforms.
- AI_ADDRCONFIG forces getaddrinfo() to only return addresses that match the family type of a configured interface on the local machine. For example, if your machine is IPv4 only, then using AI_ADDRCONFIG | AI_ALL prevents IPv6 addresses from being returned. It is usually a good idea to use this flag if you plan to connect a socket to the address returned by getaddrinfo().
- AI_PASSIVE can be used with node = 0 to request the wildcard address. This is the local address that accepts connections on any of the host's network addresses. It is used on servers with bind(). If node is not 0, then AI_PASSIVE has no effect. See Chapter 3, An In-Depth Overview of TCP Connections for example usage.
All other fields in hints should be set to 0. You can also pass in 0 for the hints argument, but different operating systems implement different defaults in that case.
The final parameter to getaddrinfo(), res, is a pointer to a pointer to struct addrinfo and returns the address(es) found by getaddrinfo().
If the call to getaddrinfo() succeeds, then its return value is 0. In this case, you should call freeaddrinfo() on *res when you've finished using the address. Here is an example of using getaddrinfo() to find the address(es) for example.com:
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ALL;
struct addrinfo *peer_address;
if (getaddrinfo("example.com", 0, &hints, &peer_address)) {
fprintf(stderr, "getaddrinfo() failed. (%d)\n", GETSOCKETERRNO());
return 1;
}
Note that we first zero out hints using a call to memset(). We then set the AI_ALL flag, which specifies that we want both IPv4 and IPv6 addresses returned. This even returns addresses that we don't have a network adapter for. If you only want addresses that your machine can practically connect to, then use AI_ALL | AI_ADDRCONFIG for the ai_flags field. We can leave the other fields of hints as their defaults.
We then declare a pointer to hold the return address list: struct addrinfo *peer_address.
If the call to getaddrinfo() succeeds, then peer_address holds the first address result. The next result, if any, is in peer_address->ai_next.
We can loop through all the returned addresses with the following code:
struct addrinfo *address = peer_address;
do {
/* Work with address... */
} while ((address = address->ai_next));
When we've finished using peer_address, we should free it with the following code:
freeaddrinfo(peer_address);
Now that we can convert a text address or name into an addrinfo structure, it is useful to see how to convert the addrinfo structure back into a text format. Let's look at that now.