Linux Socket Programming by Example - Warren Gay

< BACKCONTINUE >
159231153036212041242100244042145096184016146223183074028121223009001092207221040054162059033

Internet Services

Before you have fun working with TCP/IP in this chapter, you need to learn about some additional facilities as TCP/IP pertains to Internet services.

Examining the /etc/services File

Your Linux system has a text file, usually named /etc/services. This file is described in the man page services(5). This file maps the user-friendly names of certain Internet services to a port number and protocol. The precise pathname for this file is given by the C language macro _PATH_SERVICES. A simple example of its use follows:

						
#include <netdb.h>

printf("File is path '%s'\n", _PATH_SERVICES);

					

The preceding code shows the necessary include file netdb.h and a printf(3) statement, which prints out the pathname for the services file.

Each text line in the /etc/services file describes one Internet service. It has the following general format:

						
service-name    port/protocol    [alias …]

					

The square brackets shown indicate that the one or more alias entries are optional. The /etc/services text line is described in detail in Table 7.1.

Table 7.1. The /etc/services Fields
Field Description
service-name The case-sensitive user-friendly name of the service is described by this table entry.
port The port number precedes the slash, and is the decimal port number for the service.
/ This separates the port number from the protocol field.
protocol This specifies the type of the protocol to be used. This should be a protocol that can be found in the protocols(5) file. Common examples are udp or tcp.
alias Other names for the "service-name." Additional aliases can be separated by tabs or spaces. There is a maximum of 35 aliases permitted, due to restrictions in getservent(3).

Following is a pair of well-known service entries:

						
ftp        21/tcp
telnet     23/tcp

					

The first entry shown lists the ftp service as being available on TCP/IP port 21. The second entry shows the telnet service being available on TCP/IP port 23.

Working with the /etc/services file directly is neither convenient nor wise for your program. Consequently, Linux provides you with some routines to make things easier.

Using Function getservent(3)

If you have used some of the password database functions like getpwent(3) before, the functions about to be described here will seem similar. The synopsis of the getservent(3) function is as follows:

						
#include <netdb.h>

struct servent *getservent(void);

					

For each call to getservent(3), you are returned a pointer to a structure that represents one entry from the /etc/services file. When the end-of-file is reached, a NULL pointer is returned (but see the caution that follows). If an error occurs, a NULL pointer is also returned, with the reason for the error posted to variable errno.

CAUTION

Even when the value of errno is zeroed prior to calling getservent(3), when end-of-file is reached and indicated by a NULL return pointer, the errno value for Red Hat Linux 6.0 is code ENOENT.

Under other UNIX operating systems, such as HP-UX 10.2 and Sun Solaris 5.5.1, the errno value is left at zero when end-of-file is returned. This leads the author to speculate that this behavior is a bug, which might be corrected in a later release of Linux.


When the pointer returned is not NULL, it points to the structure servent, as illustrated in Listing 7.1.

Example 7.1. The struct servent Structure
struct servent {
    char   *s_name;  /* official service name */
    char   **s_aliases;          /* alias list */
    int    s_port;               /* port number */
    char   *s_proto;        /* protocol to use */
}

CAUTION

Be careful to note that the value in s_port is already in network byte order. To print this value in printf(3), for example, make sure you convert this value back to host order by using ntohs(sp->s_port), for example.

When setting the port number in a socket address, you merely assign this value as-is, since the port number is expected to be in network byte order. Listing 7.7 later in this chapter shows an example of this use.


The structure member s_aliases is actually an array of character pointers. If sp points to the structure, and x is an int subscript, then you can iterate through each alias sp->s_alias[x], until you reach a NULL pointer. A NULL pointer marks the end of this alias list. Listing 7.2 shows a simple program that lists all /etc/services entries and their aliases, if any.

Example 7.2. servent.c—A Program to List All Services
 <$nopage>
001 1:   /* servent.c:
002 2:    * 
003 3:    * Example getservent(3) program:
004 4:    */
005 5:   #include <stdio.h>
006 6:   #include <unistd.h>
007 7:   #include <stdlib.h>
008 8:   #include <errno.h>
009 9:   #include <string.h>
010 10:  #include <netdb.h>
011 11:  #include <netinet/in.h>
012 12:  
013 13:  int
014 14:  main(int argc,char **argv) {
015 15:      int x;
016 16:      struct servent *sp;
017 17:  
018 18:      for (;;) {
019 19:          errno = 0;
020 20:          if ( !(sp = getservent()) )
021 21:              break;
022 22:  
023 23:          printf("%s:\n"
024 24:              "\tPort:     %d\n"
025 25:              "\tProtocol: %s\n"
026 26:              "\tAliases:  ",
027 27:              sp->s_name,
028 28:              ntohs(sp->s_port),
029 29:              sp->s_proto);
030 30:          for ( x=0; sp->s_aliases[x] != NULL; ++x )
031 31:              printf("%s ",sp->s_aliases[x]);
032 32:          putchar('\n');
033 33:      }
034 34:  
035 35:      if ( errno != 0
036 36:      &&   errno != ENOENT ) /* For RH-6.0 */
037 37:          fprintf(stderr,
038 38:              "%s: getservent(3) %d\n",
039 39:              strerror(errno),errno);
040 40:  
041 41:       return 0; 
042 42:  }
043  <$nopage>

The program in Listing 7.2 uses the following basic steps:

  1. Calls getservent(3) to obtain an entry from the /etc/services file.

  2. Prints the service name, port, and protocol.

  3. In an internal loop, prints all alias names, if any.

  4. Repeats step 1, until there are no more entries.

Now looking at the program in more detail:

  1. Line 10 shows that netdb.h was included. This defines the function prototype for getservent(3). Line 11 includes netinet/in.h to define ntohs(), which is used in line 28.

  2. Line 16 declares a pointer to struct servent, which is named as sp.

  3. Line 19 zeros the value of errno. The author suspects that getservent(3) should leave errno as zero when end-of-file is reached. However, Red Hat Linux 6.0 returns with ENOENT in errno at present, when end-of-file is reached. Just be aware that this might be fixed in the future.

  4. The pointer is returned from getservent(3) and assigned to variable sp (line 20). If the pointer is NULL, line 21 breaks out of the loop.

  5. Lines 23 to 29 display the service name, port, and protocol.

  6. Lines 30 and 31 report all aliases, if any.

  7. The program repeats step 3 until no more entries remain in the /etc/services file.

  8. Lines 35 and 36 try to distinguish between end-of-file and an error. Red Hat Linux 6.0 indicates ENOENT, but zero may be indicated in the future (if this behavior is indeed a bug).

  9. Lines 37 to 39 report the error, if step 8 identifies that an error has occurred.

CAUTION

The pointer returned by getservent(3) is only valid until the next call to the same function.


Listing 7.3 shows how to compile and run the program in Listing 7.2. In this example, the output was piped to the head command to show only the first few lines of output.

Example 7.3. Compiling and Running servent.c from Listing 7.2
$ make servent
gcc -c  -D_GNU_SOURCE -Wall servent.c
gcc servent.o -o servent
$ ./servent | head
tcpmux:
        Port:     1
        Protocol: tcp
        Aliases:  
echo:
        Port:     7
        Protocol: tcp
        Aliases:  
echo:
        Port:     7
Broken pipe
$

The error message "Broken pipe" in Listing 7.3 is simply due to the head command being used (it closed the pipe early). There are some companions to the getservent(3) function, and these will be covered next.

Using the setservent(3) Function

The setservent(3) function allows you to rewind the file that is opened behind the scenes in the function getservent(3). For example, if you were to try to process entries a second time in Listing 7.2, you would need setservent(3) to rewind to the start of the /etc/services file. Otherwise, you will just continue to receive end-of-file indications. The function synopsis is as follows:

						
#include <netdb.h>

void setservent(int stayopen);

					

This function takes one argument, which is a Boolean value:

  • When non-zero (TRUE), the stayopen argument indicates that the file should be rewound instead of re-opened when rereading the /etc/ services file is performed. This is preferred for performance reasons.

  • When zero (FALSE), the file is closed if it has been previously opened (by getservent(3), for example). Then the function re-opens the file to make ready for the next getservent(3) call.

There is no return value for this function.

Using the endservent(3) Function

The function getservent(3) opens the /etc/services file behind the scenes, before returning a pointer to an entry. If your application has determined that it no longer needs to read more entries, then the endservent(3) function can be used to cause the file to be closed. This is especially important in server programs where the number of open file descriptors may be at a premium. The function synopsis is as follows:

						
#include <netdb.h>

void endservent(void);

					

There is no argument, no return value, and no errors to test.

Looking Up a Service by Name and Protocol

The previously introduced functions enable you to scan the /etc/services file one entry at a time. Often, however, this still proves to be inconvenient because of the amount of code involved. Instead, it would be more convenient to supply the service name and protocol, and have a function return the required entry. The getservbyname(3) function does just that. The function synopsis is as follows:

						
#include <netdb.h>

struct servent *getservbyname(const char *name, const char *proto);

					

The arguments to the function are as follows:

  1. The service name to look up. For example, "telnet" could be used.

  2. The protocol to be used (proto). Often a service will be available using UDP or TCP/IP. Consequently, you must specify the protocol that you are willing to use in order to contact that service. An example would be "tcp."

The value returned is NULL if the service cannot be found. Otherwise, a pointer to a structure servent is returned. An example of its use is shown as follows:

						
struct servent *sp;

sp = getservbyname("telnet","tcp");
if ( !sp )
    abort(); /* No such service! */

					

If the function call is successful, the structure pointer sp will point to all of the pertinent details, including the port number.

CAUTION

The pointer returned by getservbyname(3) is only valid until the next call to the same function.


Looking Up a Service by Port and Protocol

You saw in the last section that it was possible to look up a service by name and protocol. The function getservbyport(3) allows you to also perform a lookup by port and protocol. The function synopsis is as follows:

						
#include <netdb.h>

struct servent *getservbyport(int port, const char *proto);

					

The function arguments are as follows:

  1. The port number for this Internet protocol.

  2. The protocol proto to be looked up for port.

The function returns a NULL pointer if no service entry can be found to match your input parameters. Otherwise, a pointer is returned to the structure containing information, such as the service name, for example.

CAUTION

The pointer returned by getservbyport(3) is only valid until the next call to the same function.


< BACKCONTINUE >

Index terms contained in this section

arguments
      getservbyname(3) 2nd
      getservbyport(3) function
      setservent(3) function
compiling
      servent. c program 2nd
endservent(3) function
etc/services file
      examining
      fields 2nd
examining
      etc/services file 2nd 3rd
fields
      etc/services file 2nd
files
      etc/services 2nd 3rd
functions
      endservent(3)
      getservbyname(3) 2nd
      getservbyport(3)
      getservent(3) 2nd 3rd 4th 5th 6th
      setservent(3)
getservbyname(3) function 2nd
getservbyport(3) function
      arguments
getservent(3) function 2nd 3rd 4th 5th 6th
Internet
      TCP/IP
            etc/services file 2nd 3rd
listing
      services
            getservent(3) function 2nd 3rd
looking up
      services
programs
      servent. c 2nd 3rd 4th
protocols
      services
running
      servent. c program 2nd
servent. c program 2nd 3rd 4th
      compiling 2nd
      running 2nd
services
      listing all
            getservent(3) function 2nd 3rd
      looking up 2nd
            with port and protocol
      naming
      protocol
setservent(3) function
setservent(3) funtion
      arguments
struct servant function
      structure
structures
      struct servant
TCP/IP
      Internet
            etc/services file 2nd 3rd