From Fedora Project Wiki

Domain Name System

Resolving using getaddrinfo() in applications

The getaddrinfo() function is a dualstack-friendly API to name resolution. It is used by applications to translate host and service names to a linked list of struct addrinfo objects.

Running getaddrinfo()

And example of getaddrinfo() call:

const char *node = "www.fedoraproject.org";
const char *service = "http";
struct addrinfo hints = {
    .ai_family = AF_UNSPEC,
    .ai_socktype = SOCK_DGRAM,
    .ai_flags = 0,
    .ai_protocol = 0,
    .ai_canonname = NULL,
    .ai_addr = NULL,
    .ai_next = NULL
};
struct addrinfo *result;
int error;

error = getaddrinfo(node, service, &hints, &result);

The input of getaddrinfo() consists of node specification, service specification and further hints.

  • node: literal IPv4 or IPv6 address, or a hostname to be resolved
  • service: numeric port number or a symbolic service name
  • hints.ai_family: enable dualprotocol, IPv4-only or IPv6-only queries
  • hints.ai_socktype: select socket type (and thus protocol family)

getaddrinfo() can be futher tweaked with the hints.ai_flags. Other attributes are either not needed (ai_protocol) or not supposed to be set in hints (ai_canonname, ai_addr and ai_next).

On success, the error variable is assigned to 0 and result is pointed to a linked list of one or more struct addrinfo objects.

Never assume that getaddrinfo() returns only one result or that the first result actually works!

Using getaddrinfo() results

It is necesary to try all results until one successfully connects. This works perfectly for TCP connections as they can fail gracefully at this stage.

struct addrinfo *item;
int sock;

for (item = result; item; item = item->ai_next) {
    sock = socket(item->ai_family, item->ai_socktype, item->ai_protocol);

    if (sock == -1)
        continue;

    if (connect(sock, item->ai_addr, item->ai_addrlen) != -1) {
        fprintf(stderr, "Connected successfully.");
        break;
    }

    close(sock);
}

For UDP, connect() succeeds without contacting the other side (if you are using connect() with udp at all). Therefore you might want to perform additional actions (such as sending a message and recieving a reply) before crying out „success!“.

Freeing getaddrinfo() results

When we're done with the results, we'll free the linked list.

freeaddrinfo(result);

==== Using getaddrinfo() in Python

Python's socket.getaddrinfo() API tries to be a little bit more sane than the C API.

#!/usr/bin/python3

import sys, socket

host = "www.fedoraproject.org"
port = "http"
family = socket.AF_UNSPEC
socktype = socket.SOCK_DGRAM
protocol = 0
flags = 0

result = socket.getaddrinfo(host, port, family, socktype, protocol, flags)

sock = None
for family, socktype, protocol, canonname, sockaddr in result:
    try:
        sock = socket.socket(family, socktype, protocol)
    except socket.error:
        continue
    try:
        sock.connect(sockaddr)
        print("Successfully connected to: {}".format(sockaddr))
    except socket.error:
        sock.close()
        sock = None
        continue
    break

if sock is None:
    print("Failed to connect.", file=sys.stderr)
    sys.exit(1)