Add Book to My BookshelfPurchase This Book Online

Chapter 14 - Networking with Sockets

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

Other Functions
Several other functions can be used with sockets, although their use is less common than the routines described so far.
Socket "Names''
Two functions let you obtain the name bound to a socket:
    #include <sys/types.h>
    #include <sys/socket.h>
    int getsockname(int s, struct sockaddr *name, int *namelen);
    int getpeername(int s, struct sockaddr *name, int *namelen);
The getsockname function obtains the name bound to the socket s, and stores it in the area pointed to by name. Since name is of different sizes depending on the networking domain (i.e., it may point to a struct sockaddr_un or a struct sockaddr_in), the length of the name is stored in namelen. Note that namelen should be initialized to the size of the area pointed to by name; on return it will be set to the actual length of the name.
The getpeername function obtains the name of the peer connected to the socket s. In other words, it obtains the address and port number of the remote host. A server can use this information to find out who has connected to it. The name and namelen parameters are as described above.
Both getsockname and getpeername return 0 on success; on failure they return -1 and store an error code in errno.
Socket Options
A number of options may be set on a socket to control its behavior; there are two functions for manipulating these options:
    #include <sys/types.h>
    #include <sys/socket.h>
    int getsockopt(int s, int level, int optname, char *optval, int *optlen);
    int setsockopt(int s, int level, int optname, char *optval, int optlen);
The getsockopt function returns information about the state of options currently set on the socket s; setsockopt changes the state of those options.
Options may exist at multiple protocol levels. Therefore, it is necessary to specify the level at which the option in question resides. All of the options described in this section exist at the socket level; the level parameter should always be set to SOL_SOCKET.
The optval parameter specifies a pointer to a buffer that either contains the value to be set for the option, or is used to store the value of the option. The optlen parameter specifies the size of the area pointed to by optval; on return from getsockopt, optlen will be modified to indicate the actual size of the value.
The optname parameter specifies the option of interest:
SO_DEBUG
Enables or disables debugging in the underlying protocol module.
SO_REUSEADDR
Indicates that the rules used in validating addresses provided with calls to bind should be modified to allow re-use of local addresses.
SO_KEEPALIVE
Enables the periodic transmission of “are you there” messages on a connected socket. If the connected party fails to respond to these messages, the connection is considered broken and processes using the socket will receive a SIGPIPE signal the next time they try to use it.
SO_DONTROUTE
Indicates that outgoing messages should bypass the network routing facilities. This is used only for debugging and diagnostic purposes.
SO_LINGER
If SO_LINGER is set on a socket that guarantees reliable data delivery, and a close is performed on the socket, the system will block the process on the close until any unsent data has been transmitted, or until the transmission times out. The timeout in seconds is specified in the optval parameter to setsockopt. If SO_LINGER is disabled and a close is issued, the system will process it in a manner that allows the calling process to continue as quickly as possible.
SO_BROADCAST
Requests permission to send broadcast datagrams (datagrams to be received by all hosts) on the socket.
SO_OOBINLINE
On sockets that support out-of-band data, requests that when the out-of-band data arrives, it be placed in the normal input queue; this allows the data to be processed by read or recv calls without the MSG_OOB flag.
SO_SNDBUF,
SO_RCVBUF
Adjust the size of the normal send and receive buffers, respectively. Generally speaking, for large data transfers, these buffers should be made as large as possible to make the transfer as efficient as possible. The maximum limit on the buffer size in SVR4 is 64 Kbytes.
SO_TYPE
Used with getsockopt only; returns the type of the socket (e.g., SOCK_STREAM).
SO_ERROR
Used with getsockopt only; returns any pending error on the socket and clears the error status.
Address Conversion
Routines are provided to convert between the internal (binary) and external (character string) representations of Internet addresses:
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    unsigned long inet_addr(const char *cp);
    char *inet_ntoa(const struct in_addr addr);
The inet_addr function takes a character string containing an Internet address in “dotted-quad” notation (e.g., 192.10.42.34) and returns the integer representation of that address. The inet_ntoa function takes an integer representation of an Internet address, and returns a character string representation of the address in dotted-quad notation.
The Berkeley "r'' Commands
The functionality of the Berkeley rsh command, which contacts a remote host and passes a command to the shell, is accessible through the rcmd function:
    int rcmd(char **ahost, unsigned short inport, char *luser,
            char *ruser, char *cmd, int *fd2p);
    int rresvport(int *port);
The authentication scheme is based on reserved port numbers, defined to be port numbers less than 1024. On BSD UNIX systems (and other systems, such as SVR4, that support the concept), only a superuser can obtain a reserved port. On the server side, when a client connects, the server checks to see that the client is using a reserved port between 513 and 1024; port numbers less than or equal to 512 are not permitted. If the port number used by the client is greater than 1024, it is not a reserved port, and the server will not allow it. Note that the whole concept of reserved ports is specific to UNIX; it is not an Internet standard. This means that the authentication provided by this mechanism is dubious at best (for example, a personal computer running MS-DOS can create any port it wants, since there is no concept of a superuser).
A reserved port number is obtained using the rresvport function; it returns either a reserved port suitable for use as the inport parameter to rcmd, or -1 on error.
The rcmd function connects to the host named in *aname, which is modified to contain the official host name, using the reserved port given by inport. It returns a stream socket on success, or -1 on failure. The luser parameter should contain the name of the local user; the ruser parameter should contain the name of the user on the remote host whose account is to be used to execute the command. On the remote host, the rshd daemon will search ruser's .rhosts file for a line specifying the connecting host and luser. If such a line is found, access is granted; otherwise, access is denied.
If access is granted, the shell command in cmd is executed. The standard input and output of the command is connected to the socket returned by rcmd. If fd2p is non-null, an auxilliary channel to a control process will be set up, and a descriptor for it placed in *fd2p. The control process returns the command's standard error output on this channel; it also accepts bytes on this channel as signal numbers to be delivered to the process group of the command. If fd2p is null, the standard error output of the command becomes the same as its standard output, and no provision for delivering signals to the process is made.
As mentioned above, rcmd may only be used by the superuser, since it requires a reserved port. Generally, this means that the program using it must either be executed by “root,” or made set-user-id to “root.” Obviously, for the average user, this presents a problem. The rexec function avoids this problem, to some extent:
    int rexec(char **ahost, unsigned short inport, char *user,
            char *password, char *cmd, int *fd2p);
The usage and parameters of rexec are basically the same as those of rcmd. However, the inport parameter does not have to specify a reserved port, and instead of using .rhosts-based authentication, a login name and password for the remote host must be specified. The advantage of rexec is that is does not require a privileged port. However, this advantage is lost because a rexec now requires a password; it means that programs using rexec cannot safely be used in a non-interactive environment since compiling the password into the program would be unsafe.
A server can implement .rhosts-based authentication by calling the ruserok function:
    int ruserok(char *rhost, int suser, char *ruser, char *luser);
The rhost parameter should be the name of the remote host, as returned by gethostbyaddr. The ruser parameter is the name of the calling user on the remote host, and the luser parameter is the name of the user on the local host (the user whose .rhosts file should be checked). The suser flag should be 1 if the luser name is that of the superuser and 0 otherwise; this bypasses the check of the /etc/hosts.equiv file (which is not used if the local user is the superuser).
The inetd Super-Server
When Berkeley originally developed networking support, each service was served by a separate daemon server process. As the number of services increased, so did the number of daemons. Unfortunately, many of these daemons executed only rarely, since their services were relatively unused. So, the daemon processes sat around all the time consuming system resources and cluttering up the process table, and only rarely did anything useful.
The inetd program was created to solve this problem. inetd is a super-server. It reads a configuration file (/etc/inetd.conf, usually) and then opens a socket for each service listed in the file, and binds it to the appropriate port. When a connection or datagram comes in on one of these ports, inetd spawns a child process and executes the daemon responsible for handling that service. In this way, most of the time the only daemon running is inetd. All the other daemons run only when they have something to do, thus freeing up system resources.
When a daemon server is invoked via inetd, its standard input and output are connected to the socket. When the server reads from standard input, it is actually reading from the network, and when it writes to standard output, it is actually writing to the network. All of the calls to socket, bind, accept, and listen described above are unnecessary. The daemon can use the getpeername function if it needs to know who (what host) is connecting to it.
Generally speaking, servers should be written to operate out of inetd. This is usually more efficient, and it is always much simpler. The only exception to this rule is a server that receives a high volume of connections; the performance cost of having inetd fork and spawn a new copy of the server for each connection may outweigh the performance gained by not having another server out there all the time.

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback