|
|
As was mentioned in ``Basics'', binding addresses to sockets in the Internet domain can be complex. As a brief reminder, these associations are composed of local and foreign addresses, and local and foreign ports. Port numbers are allocated out of separate spaces, one for each system and one for each domain on that system. Through the bind system call, a process may specify half of an association, the <local address, local port> part, while the connect and accept primitives are used to complete a socket's association by specifying the <foreign address, foreign port> part. Since the association is created in two steps the association uniqueness requirement mentioned previously could be violated unless care is taken. Further, it is unrealistic to expect user programs always to know proper values to use for the local address and local port since a host may reside on multiple networks and the set of allocated port numbers is not directly accessible to a user.
To simplify local address binding in the Internet domain the notion of a wildcard address has been provided. When an address is specified as INADDR_ANY (a manifest constant defined in netinet/in.h), the system interprets the address as any valid address. For example, to bind a specific port number to a socket, but leave the local address unspecified, the following code might be used:
#include <sys/types.h> #include <netinet/in.h> ... struct sockaddr_in sin; ... s = socket(AF_INET, SOCK_STREAM, 0); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(MYPORT); bind(s, (struct sockaddr *) &sin, sizeof(sin));Sockets with wildcarded local addresses may receive messages directed to the specified port number, and sent to any of the possible addresses assigned to a host. For example, if a host has addresses 128.32.0.4 and 10.0.0.78, and a socket is bound as above, the process will be able to accept connection requests that are addressed to 128.32.0.4 or 10.0.0.78. If a server process wished to only allow hosts on a given network connect to it, it would bind the address of the host on the appropriate network.
In a similar fashion, a local port may be left unspecified (specified as zero), in which case the system will select an appropriate port number for it. For example, to bind a specific local address to a socket, but to leave the local port number unspecified:
hp = gethostbyname(hostname); if (hp == NULL) { ... } memcpy((char *) sin.sin_addr, hp->h_addr, hp->h_length); sin.sin_port = htons(0); bind(s, (struct sockaddr *) &sin, sizeof(sin));The system selects the local port number based on two criteria. The first is that Internet ports below IPPORT_RESERVED (1024) are reserved for privileged users (that is, the super user). The second is that the port number is not currently bound to some other socket. To find a free Internet port number in the privileged range the rresvport library routine may be used as follows to return a stream socket with a privileged port number:
int lport = IPPORT_RESERVED - 1; int s; ... s = rresvport(&lport); if (s < 0) { if (errno == EAGAIN) fprintf(stderr, "socket: all ports in use\n"); else perror("rresvport: socket"); ... }This restriction was placed on port allocation to allow processes executing in a ``secure'' environment to do authentication based on the originating address and port number. For example, the rlogin(C) command allows users to log in across a network without being asked for a password, provided that two conditions are met: First, the name of the system the user is logging in from must be in the file /etc/hosts.equiv on the system being logged in to (or the system name and the user name must be in the user's .rhosts file in the user's home directory). Second, the user's rlogin process must come from a privileged port on the machine from which the user is logging in. The port number and network address of the machine from which the user is logging in can be determined either from the accept call (the from result), or the getpeername call.
In certain cases the algorithm used by the system in selecting port numbers is unsuitable for an application. This is because associations are created in a two step process. For example, the Internet file transfer protocol, FTP, specifies that data connections must always originate from the same local port. However, duplicate associations are avoided by connecting to different foreign ports. In this situation the system would disallow binding the same local address and port number to a socket if a previous data connection's socket still existed. To override the default port selection algorithm, an option call must be performed before address binding:
... int on = 1; ... setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); bind(s, (struct sockaddr *) &sin, sizeof(sin));With the above call, local addresses may be bound that are already in use. This does not violate the uniqueness requirement as the system still checks at connect time to be sure any other sockets with the same local address and port do not have the same foreign address and port. If the association already exists, the error EADDRINUSE is returned.