Network Interface Guide

Servers

Most servers are accessed at well-known Internet port numbers or UNIX family names. Example 2-6 illustrates the main loop of a remote-login server.


Example 2-6 Remote Login Server

main(argc, argv)
   int argc;
   char *argv[];
{
   int f;
   struct sockaddr_in6 from;
   struct sockaddr_in6 sin;
   struct servent *sp;
 
   sp = getservbyname("login", "tcp");
 
   if (sp == (struct servent *) NULL) {
      fprintf(stderr, "rlogind: tcp/login: unknown service");
      exit(1);
   }
   ...
   #ifndef DEBUG
   /* Disassociate server from controlling terminal. */
   ...
   #endif
   	sin.sin6_port = sp->s_port;	/* Restricted port */
      sin.sin6_addr.s6_addr = in6addr_any;
      ...
      f = socket(AF_INET6, SOCK_STREAM, 0);
      ...
      if (bind( f, (struct sockaddr *) &sin, sizeof sin ) == -1) {
      ...
      }
      ...
      listen(f, 5);
      while (TRUE) {
         int g, len = sizeof from;
         g = accept(f, (struct sockaddr *) &from, &len);
         if (g == -1) {
            if (errno != EINTR)
               syslog(LOG_ERR, "rlogind: accept: %m");
            continue;
         }
         if (fork() == 0) {
            close(f);
            doit(g, &from);
         }
         close(g);
      }
      exit(0);
}

Example 2-7 shows how the server gets its service definition.


Example 2-7 Remote Login Server: Step 1

sp = getservbyname("login", "tcp");
if (sp == (struct servent *) NULL) {
		fprintf(stderr, "rlogind: tcp/login: unknown service\n");
		exit(1);
}

The result from getservbyname(3SOCKET) is used later to define the Internet port at which the program listens for service requests. Some standard port numbers are in /usr/include/netinet/in.h.

Example 2-8 shows how the server dissociates from the controlling terminal of its invoker in the non-DEBUG mode of operation.


Example 2-8 Dissociating From the Controlling Terminal

   (void) close(0);
   (void) close(1);
   (void) close(2);
   (void) open("/", O_RDONLY);
   (void) dup2(0, 1);
   (void) dup2(0, 2);
   setsid();

This prevents the server from receiving signals from the process group of the controlling terminal. After a server has dissociated itself, it cannot send reports of errors to a terminal and must log errors with syslog(3C).

A server next creates a socket and listens for service requests. bind(3SOCKET) ensures that the server listens at the expected location. (The remote login server listens at a restricted port number, so it runs as superuser.)

Example 2-9 illustrates the main body of the loop.


Example 2-9 Remote Login Server: Main Body

while(TRUE) {
		int g, len = sizeof(from);
		if (g = accept(f, (struct sockaddr *) &from, &len) == -1) {
			if (errno != EINTR)
				syslog(LOG_ERR, "rlogind: accept: %m");
			continue;
		}
		if (fork() == 0) {		/* Child */
			close(f);
			doit(g, &from);
		}
		close(g);					/* Parent */
}

accept(3SOCKET) blocks messages until a client requests service. accept(3SOCKET) returns a failure indication if it is interrupted by a signal, such as SIGCHLD. The return value from accept(3SOCKET) is checked and an error is logged with syslog(3C) if an error has occurred.

The server then fork(2)s a child process and invokes the main body of the remote login protocol processing. The socket used by the parent to queue connection requests is closed in the child. The socket created by accept(3SOCKET) is closed in the parent. The address of the client is passed to the server application's doit() routine, which performs the actual application protocol with the client, for authenticating clients.