|
|
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.