Section 17.8. Legacy Networking Functions

   


17.8. Legacy Networking Functions

There are a number of library functions related to TCP/IP networking that should not be used in new applications. They are widely used in existing IPv4-only programs, however, so they are covered here to aid in understanding and updating this older code.

17.8.1. IPv4 Address Manipulation

The inet_ntop() and inet_pton() functions are relatively new and were introduced to allow a single set of functions to handle both IPv4 and IPv6 addresses. Before they were around, programs used inet_addr(), inet_aton(), and inet_ntoa(), all of which are IPv4 specific.

Recall that struct sockaddr_in is defined as

 struct sockaddr_in {     short int           sin_family; /* AF_INET */     unsigned short int  sin_port;   /* port number */     struct in_addr      sin_addr;   /* IP address */ } 


The sin_addr member is a struct in_addr, which these legacy functions use as a parameter.[33] This structure is meant to be opaque; application programs should never manipulate a struct in_addr except through library functions.The old function for converting an IPv4 address to its dotted-decimal form is inet_ntoa().

[33] Using this structure directly made it impossible to extend these functions for IPv6 without changing their interface.

 #include <netinet/in.h> #include <arpa/inet.h> char * inet_ntoa(struct in_addr address); 


The address passed is converted to a string in dotted-decimal form, and a pointer to that string is returned. The string is stored in a static buffer in the C library and will be destroyed on the next call to inet_ntoa().[34]

[34] Functions using static memory to store results make it difficult to build threaded applications, as locks need to be added by the application code to protect those static buffers.

There are two functions that provide the inverse, converting a dotted-decimal string to a binary IP address. The older of the two, inet_addr() function has two problems, both caused by its returning a long. Not returning a struct in_addr, which is what the rest of the standard functions expect, forced programmers into ugly casts. In addition, if a long was 32 bits, programs could not differentiate between a return of -1 (indicating an error, such as a malformed address) and the binary representation of 255.255.255.255.

 #include <netinet/in.h> #include <arpa/inet.h> unsigned long int inet_addr(const char * ddaddress); 


The function takes the passed string, which should contain a dotted-decimal IP address, and converts it to a binary IP address.

To remedy the shortcomings of inet_addr(), inet_aton() was introduced.

 #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char * ddaddress, struct in_addr * address); 


This function expects a string containing a dotted-decimal IP address and fills in the struct in_addr pointed to by address with the binary representation of the address. Unlike most library functions, inet_aton() returns zero if an error occurred and non-zero if the conversion was successful.

17.8.2. Hostname Resolution

The getaddrinfo() getnameinfo() functions, which make it easy to write a program that supports both IPv4 and IPv6, were added to do just that. The original hostname functions could not be easily extended for IPv6, and their interfaces require applications to know quite a bit about the version-specific structures that store IP addresses; the new interfaces are abstract and so support IPv4 and IPv6 equally.

Rather than returning a linked list like getaddrinfo() does, the older host-name functions use struct hostent, which can contain all the hostnames and addresses for a single host.

 #include <netdb.h> struct hostent {     char * h_name;         /* canonical hostname            */     char ** h_aliases;     /* aliases (NULL terminated)     */     int h_addrtype;        /* host address type             */     int h_length;          /* length of address             */     char ** h_addr_list;   /* list of addresses (NULL term) */ }; 


The h_name is the canonical name for the host. The h_aliases array contains any aliases for the host. The final entry in h_aliases is a NULL pointer, signaling the end of the array.

h_addrtype tells the type of address the host has. For the purposes of this chapter, it is always AF_INET. Applications that were written to support IPv6 will see other address types.[35] The next member, h_length, specifies the length of the binary addresses for this host. For AF_INET addresses, it is equal to sizeof(struct in_addr). The final member, h_addr_list, is an array of pointers to the addresses for this host, with the final pointer being NULL to signify the end of the list. When h_addrtype is AF_INET, each pointer in this list points to a struct in_addr.

[35] There probably are not any IPv6 programs that use struct hostaddr, but it is possible for them to do so. The functions we discuss here return only IPv4 information by default and we do not talk about using these functions with IPv6.

Two library functions convert between IP numbers and hostnames. The first, gethostbyname(), returns a struct hostent for a hostname; the other, gethostbyaddr(), returns information on a machine with a particular IP address.

 #include <netdb.h> struct hostent * gethostbyname(const char * name); struct hostent * gethostbyaddr(const char * addr, int len, int type); 


Both functions return a pointer to a struct hostent. The structure may be overwritten by the next call to either function, so the program should save any values it will need later. gethostbyname() takes a single parameter, a string containing a hostname. gethostbyaddr() takes three parameters, which together specify an address. The first, addr, should point to a struct in_addr. The next, len, specifies how long the information pointed to by addr is. The final parameter, type, specifies the type of address that should be converted to a hostname AF_INET for IPv4 addresses.

When errors occur during hostname lookup, an error code is set in h_errno. The herror() function call prints a description of the error, behaving almost identically to the standard perror() function.

The only error code that most programs test for is NEtdB_INTERNAL, which indicates a failed system call. When this error is returned, errno contains the error that led to the failure.

17.8.3. Legacy Host Information Lookup Example

Here is a sample program that makes use of inet_aton(), inet_ntoa(), gethostbyname(), and gethostbyaddr(). It takes a single argument that may be either a hostname or an IP address in dotted-decimal notation. It looks up the host and prints all the hostnames and IP addresses associated with it.

Any argument that is a valid dotted-decimal address is assumed to be an IP number, not a hostname.

  1: /* lookup.c */  2:  3: /* Given either a hostname or IP address on the command line, print  4:    the canonical hostname for that host and all of the IP numbers  5:    and hostnames associated with it. */  6:  7: #include <netdb.h>              /* for gethostby* */  8: #include <sys/socket.h>  9: #include <netinet/in.h>         /* for address structures */ 10: #include <arpa/inet.h>          /* for inet_ntoa() */ 11: #include <stdio.h> 12: 13: int main(int argc, const char ** argv) { 14:     struct hostent * answer; 15:     struct in_addr address, ** addrptr; 16:     char ** next; 17: 18:     if (argc != 2) { 19:         fprintf(stderr, "only a single argument is supported\n"); 20:         return 1; 21:     } 22: 23:     /* If the argument looks like an IP, assume it was one and 24:        perform a reverse name lookup */ 25:     if (inet_aton(argv[1], &address)) 26:         answer = gethostbyaddr((char *) &address, sizeof(address), 27:                                AF_INET); 28:     else 29:         answer = gethostbyname(argv[1]); 30: 31:     /* the hostname lookup failed :-( */ 32:     if (!answer) { 33:         herror("error looking up host"); 34:         return 1; 35:     } 36: 37:     printf("Canonical hostname: %s\n", answer->h_name); 38: 39:     /* if there are any aliases, print them all out */ 40:     if (answer->h_aliases[0]) { 41:         printf("Aliases:"); 42:         for (next = answer->h_aliases; *next; next++) 43:             printf(" %s", *next); 44:         printf("\n"); 45:     } 46: 47:     /* display all of the IP addresses for this machine */ 48:     printf("Addresses:"); 49:     for (addrptr = (struct in_addr **) answer->h_addr_list; 50:                 *addrptr; addrptr++) 51:         printf(" %s", inet_ntoa(**addrptr)); 52:     printf("\n"); 53: 54:     return 0; 55: } 


Here is what this program looks like when it is run:

 $. /lookup ftp.netscape.com Canonical hostname: ftp25.netscape.com Aliases: ftp.netscape.com anonftp10.netscape.com Addresses: 207.200.74.21 


17.8.4. Looking Up Port Numbers

While getaddrinfo() and getnameinfo() make it easy for application to convert service names to port numbers at the same time hostname resolution is performed, the older functions for service name lookups are completely independent of hostname lookups. Service names are accessed through the getservbyname() function.

 #include <netdb.h> struct servent * getservbyname(const char * name,                                const char * protocol); 


The first parameter, name, is the service name about which the application needs information. The protocol parameter specifies the protocol to use. The services database contains information on other protocols (especially UDP); specifying the protocol allows the function to ignore information on other protocols. The protocol is usually the string "tcp", although other protocol names, such as "udp", may be used.

getservbyname() returns a pointer to a structure that contains information on the queried service. The information may be overwritten by the next call to getservbyname(), so any important information should be saved by the application. Here is the information returned by getservbyname():

 #include <netdb.h> struct servent {     char * s_name;         /* service name */     char ** s_aliases;     /* service aliases */     int s_port;            /* port number */     char * s_proto;        /* protocol to use */ } 


Each service may have multiple names associated with it but only one port number. s_name lists the canonical name of the service, s_port contains the well-known port number for the service (in network byte order), and s_proto is the protocol for the service (such as "tcp"). The s_aliases member is an array of pointers to aliases for the service, with a NULL pointer marking the end of the list.

If the function fails, it returns NULL and sets h_errno.

Here is an example program that looks up a TCP service specified on the command line and displays the service's canonical name, port number, and all of its aliases:

  1: /* services.c */  2:  3: #include <netdb.h>  4: #include <netinet/in.h>  5: #include <stdio.h>  6:  7: /* display the TCP port number and any aliases for the service  8:    which is named on the command line */  9: 10: /* services.c - finds the port number for a service */ 11: int main(int argc, const char ** argv) { 12:     struct servent * service; 13:     char ** ptr; 14: 15:     if (argc != 2) { 16:         fprintf(stderr, "only a single argument is supported\n"); 17:         return 1; 18:     } 19: 20:     /* look up the service in /etc/services, give an error if 21:        we fail */ 22:     service = getservbyname(argv[1], "tcp"); 23:     if (!service) { 24:         herror("getservbyname failed"); 25:         return 1; 26:     } 27: 28:     printf("service: %s\n", service->s_name); 29:     printf("tcp port: %d\n", ntohs(service->s_port)); 30: 31:     /* display any aliases this service has */ 32:     if (*service->s_aliases) { 33:         printf("aliases:"); 34:         for (ptr = service->s_aliases; *ptr; ptr++) 35:             printf(" %s", *ptr); 36:         printf("\n"); 37:     } 38: 39:     return 0; 40: } 


Here is an example of running the program; notice it can look up services by either their canonical name or an alias:

 $ ./services http service: http tcp port: 80 $ ./services source service: chargen tcp port: 19 aliases: ttytst source 



       
    top
     


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net