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)