Chapter 4 ■ SoCket NameS aNd dNS
61
The variable named info here contains everything you need to create a socket and use it to make a connection.
It provides a family, a type, a protocol, a canonical name, and finally an address. What are the arguments provided to
getaddrinfo()? I have asked about the possible methods for connecting to the HTTP service of the host gatech.edu,
and the two-element list that has been returned tells you that there are two ways to do it: either by creating a
SOCK_STREAM socket (socket type 1 ) that uses IPPROTO_TCP (protocol number 6 ) or by using a SOCK_DGRAM (socket type 2 )
socket with IPPROTO_UDP (which is the protocol represented by the integer 17 ).
And yes, the foregoing answer is indicative of the fact that HTTP officially supports both TCP and UDP, at least
according to the official organization that doles out port numbers. When you call getaddrinfo() later from scripts,
you will generally specify which kind of socket you want instead of leaving the answer to chance.
If you use getaddrinfo() in your code, then unlike the listings in Chapter 2 and Chapter 3, which used real
symbols like AF_INET just to make it clearer how the low-level socket mechanisms were working, your production
Python code will not reference any symbols at all from the socket module except for those that explain to
getaddrinfo() which kind of address you want. Instead, you will use the first three items in the getaddrinfo()
return value as the arguments to the socket() constructor and then use the fifth item as the address to any of the
address-aware calls like connect() that were listed in the first section of this chapter.
As you can see from the previous code snippet, getaddrinfo() generally allows not only the hostname but also
the port name to be a symbol like 'www' rather than an integer, eliminating the need for older Python code to make
extra calls if the user wants to provide a symbolic port number like www or smtp instead of 80 or 25.
Before tackling all of the options that getaddrinfo() supports, it will be more useful to see how it is used to
support three basic network operations. I will tackle them in the order that you might perform operations on a socket:
binding, connecting, and then identifying a remote host who has sent you information.
Using getaddrinfo() to Bind Your Server to a Port
If you want an address to provide to bind(), either because you are creating a server socket or because for some
reason you want your client to be connecting to someone else but from a predictable address, then you will call
getaddrinfo() with None as the hostname but with the port number and socket type filled in. Note that here, as in the
following getaddrinfo() calls, zeros serve as wildcards in fields that are supposed to contain numbers:
from socket import getaddrinfo
getaddrinfo(None, 'smtp', 0, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
[(2, 1, 6, '', ('0.0.0.0', 25)), (10, 1, 6, '', ('::', 25, 0, 0))]
getaddrinfo(None, 53, 0, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)
[(2, 2, 17, '', ('0.0.0.0', 53)), (10, 2, 17, '', ('::', 53, 0, 0))]
Here I asked two different questions using a string port identifier for the first but a raw numeric port number for
the second. First, I asked to which address I should bind() a socket if I want to serve SMTP traffic using TCP. Second,
I asked about serving port 53 (DNS) traffic using UDP. The answers I got back are the appropriate wildcard addresses
that will let you bind to every IPv4 and every IPv6 interface on the local machine with all of the right values for the
socket family, socket type, and protocol in each case.
If you instead want to bind() to a particular IP address that you know is configured as a local address for the
machine on which you are running, then omit the AI_PASSIVE flag and just specify the hostname. For example, here
are two ways that you might try binding to localhost:
getaddrinfo('127.0.0.1', 'smtp', 0, socket.SOCK_STREAM, 0)
[(2, 1, 6, '', ('127.0.0.1', 25))]
getaddrinfo('localhost', 'smtp', 0, socket.SOCK_STREAM, 0)
[(10, 1, 6, '', ('::1', 25, 0, 0)), (2, 1, 6, '', ('127.0.0.1', 25))]