18.8 Host Names and IP Addresses

Team-FLY

18.8 Host Names and IP Addresses

Throughout this book we refer to hosts by name (e.g., usp.cs.utsa.edu ) rather than by a numeric identifier. Host names must be mapped into numeric network addresses for most of the network library calls. As part of system setup, system administrators define the mechanism by which names are translated into network addresses. The mechanism might include local table lookup, followed by inquiry to domain name servers if necessary. The Domain Name Service (DNS) is the glue that integrates naming on the Internet [81, 82].

In general, a host machine can be specified either by its name or by its address. Host names in programs are usually represented by ASCII strings. IPv4 addresses are specified either in binary (in network byte order as in the s_addr field of struct in_addr ) or in a human readable form, called the dotted -decimal notation or Internet address dot notation . The dotted form of an address is a string with the values of the four bytes in decimal, separated by decimal points. For example, 129.115.30.129 might be the address of the host with name usp.cs.utsa.edu . The binary form of an IPv4 address is 4 bytes long. Since 4-byte addresses do not provide enough room for future Internet expansion, a newer version of the protocol, IPv6, uses 16-byte addresses.

The inet_addr and inet_ntoa functions convert between dotted-decimal notation and the binary network byte order form used in the struct in_addr field of a struct sockaddr_in .

The inet_addr function converts a dotted-decimal notation address to binary in network byte order. The value can be stored directly in the sin_addr.s_addr field of a struct sockaddr_in .

  SYNOPSIS  #include <arpa/inet.h>   in_addr_t inet_addr(const char *cp);  POSIX  

If successful, inet_addr returns the Internet address. If unsuccessful , inet_addr returns ( in_addr_t ) “1. No errors are defined for inet_addr .

The inet_ntoa function takes a struct in_addr structure containing a binary address in network byte order and returns the corresponding string in dotted-decimal notation. The binary address can come from the sin_addr field of a struct sockaddr_in structure. The returned string is statically allocated, so inet_ntoa may not be safe to use in threaded applications. Copy the returned string to a different location before calling inet_ntoa again. Check the man page for inet_ntoa on your system to see if it is thread-safe.

  SYNOPSIS  #include <arpa/inet.h>   char *inet_ntoa(const struct in_addr in);  POSIX  

The inet_ntoa function returns a pointer to the network address in Internet standard dot notation. No errors are defined for inet_ntoa .

The different data types used for the binary form of an address often cause confusion. The inet_ntoa function, takes a struct in_addr structure as a parameter; the inet_addr returns data of type in_addr_t , a field of a struct in_addr structure. POSIX states that a struct in_addr structure must contain a field called s_addr of type in_addr_t . It is implied that the binary address is stored in s_addr and that a struct in_addr structure may contain other fields, although none are specified. It seems that in most current implementations , the struct in_addr structure contains only the s_addr field, so pointers to sin_addr and sin_addr.s_addr are identical. To maintain future code portability, however, be sure to preserve the distinction between these two structures.

At least three collections of library functions convert between ASCII host names and binary addresses. None of these collections report errors in the way UNIX functions do by returning “1 and setting errno . Each collection has advantages and disadvantages, and at the current time none of them stands out as the best method.

UICI introduces the addr2name and name2addr functions to abstract the conversion between strings and binary addresses and allow for easy porting between implementations. The uiciname.h header file shown in Program C.3 contains the following prototypes for addr2name and name2addr .

 int name2addr(const char *name, in_addr_t *addrp); void addr2name(struct in_addr addr, char *name, int namelen); 

Link uiciname.c with any program that uses UICI.

The name2addr function behaves like inet_addr except that its parameter can be either a host name or an address in dotted-decimal format. Instead of returning the address, name2addr stores the address in the location pointed to by addrp to allow the return value to report an error. If successful, name2addr returns 0. If unsuccessful, name2addr returns “1. An error occurs if the system cannot determine the address corresponding to the given name. The name2addr function does not set errno . We suggest that when name2addr is called by a function that must return with errno set, the value EINVAL be used to indicate failure.

The addr2name function takes a struct in_addr structure as its first parameter and writes the corresponding name to the supplied buffer, name . The namelen value specifies the size of the name buffer. If the host name does not fit in name , addr2name copies the first namelen - 1 characters of the host name followed by a string terminator. This function never produces an error. If the host name cannot be found, addr2name converts the host address to dotted-decimal notation.

We next discuss two possible strategies for implementing name2addr and addr2name . Section 18.9 discusses two additional implementations. Appendix C presents complete implementations using all four approaches. Setting the constant REENTRANCY in uiciname.c picks out a particular implementation. We first describe the default implementation that uses gethostbyname and gethostbyaddr .

A traditional way of converting a host name to a binary address is with the gethostbyname function. The gethostbyname function takes a host name string as a parameter and returns a pointer to a struct hostent structure containing information about the names and addresses of the corresponding host.

  SYNOPSIS  #include <netdb.h>   struct hostent {      char    *h_name;         /* canonical name of host */      char    **h_aliases;     /* alias list */      int     h_addrtype;      /* host address type */      int     h_length;        /* length of address */      char    **h_addr_list;   /* list of addresses */   };   struct hostent *gethostbyname(const char *name);  POSIX:OB  

If successful, gethostbyname returns a pointer to a struct hostent . If unsuccessful, gethostbyname returns a NULL pointer and sets h_errno . Macros are available to produce an error message from an h_errno value. The following table lists the mandatory errors for gethostbyname .

h_errno

cause

HOST_NOT_FOUND

no such host

NO_DATA

server recognized request and name but has no address

NO_RECOVERY

unexpected server failure that cannot be recovered

TRY_AGAIN

temporary or transient error

The struct hostent structure includes two members of interest that are filled in by gethostbyname . The h_addr_list field is an array of pointers to network addresses used by this host. These addresses are in network byte order, so they can be used directly in the address structures required by the socket calls. Usually, we use only the first entry, h_addr_list[0] . The integer member h_length is filled with the number of bytes in the address. For IPv4, h_length should always be 4.

Example 18.28

The following code segment translates a host name into an IP address for the s_addr member of a struct sockaddr_in .

 char *hostn = "usp.cs.utsa.edu"; struct hostent *hp; struct sockaddr_in server; if ((hp = gethostbyname(hostn)) == NULL)    fprintf(stderr, "Failed to resolve host name\n"); else    memcpy((char *)&server.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); 

Often, a host has multiple names associated with it. For example, because usp.cs.utsa.edu is a web server for this book, the system also responds to the alias www.usp.cs.utsa.edu .

Exercise 18.29

Use the struct hostent structure returned in Example 18.28 to output a list of aliases for usp.cs.utsa.edu .

Answer:

 char **q; struct hostent *hp; for (q = hp->h_aliases; *q != NULL; q++)    (void) printf("%s\n", *q); 
Exercise 18.30

Use the struct hostent structure returned in Example 18.28 to find out how many IP addresses are associated with usp.cs.utsa.edu .

Answer:

 int addresscount = 0; struct hostent *hp; char **q; for (q = hp->h_addr_list; *q != NULL; q++)    addresscount++; printf("Host %s has %d IP addresses\n", hp->h_name, addresscount); 

Program 18.9 is one implementation of name2addr . The name2addr function first checks to see if name begins with a digit. If so, name2addr assumes that name is a dotted-decimal address and uses inet_addr to convert it to in_addr_t . Otherwise, name2addr uses gethostbyname .

Program 18.9 name2addr_gethostbyname.c

An implementation of name2addr using gethostbyname .

 #include <ctype.h> #include <netdb.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> int name2addr(char *name, in_addr_t *addrp) {     struct hostent *hp;     if (isdigit((int)(*name)))         *addrp = inet_addr(name);     else {         hp = gethostbyname(name);         if (hp == NULL)             return -1;         memcpy((char *)addrp, hp->h_addr_list[0], hp->h_length);     }     return 0; } 

The conversion from address to name can be done with gethostbyaddr . For IPv4, the type should be AF_INET and the len value should be 4 bytes. The addr parameter should point to a struct in_addr structure.

  SYNOPSIS  #include <netdb.h>   struct hostent *gethostbyaddr(const void *addr,                                 socklen_t len, int type);  POSIX:OB  

If successful, gethostbyaddr returns a pointer to a struct hostent structure. If unsuccessful, gethostbyaddr returns a NULL pointer and sets h_error . The mandatory errors for gethostbyaddr are the same as those for gethostbyname .

Example 18.31

The following code segment prints the host name from a previously set struct sockaddr_in structure.

 struct hostent *hp; struct sockaddr_in net; int sock; if (( hp = gethostbyaddr(&net.sin_addr, 4, AF_INET))    printf("Host name is %s\n", hp->h_name); 

Program 18.10 is an implementation of the addr2name function that uses the gethostbyaddr function. If gethostbyaddr returns an error, then addr2name uses inet_ntoa to convert the address to dotted-decimal notation. The addr2name function copies at most namelen-1 bytes, allowing space for the string terminator.

Program 18.10 addr2name_gethostbyaddr.c

An implementation of addr2name using gethostbyaddr .

 #include <ctype.h> #include <netdb.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> void addr2name(struct in_addr addr, char *name, int namelen) {     struct hostent *hostptr;     hostptr = gethostbyaddr((char *)&addr, 4, AF_INET);     if (hostptr == NULL)         strncpy(name, inet_ntoa(addr), namelen-1);     else         strncpy(name, hostptr->h_name, namelen-1);     name[namelen-1] = 0; } 

When an error occurs, gethostbyname and gethostbyaddr return NULL and set h_errno to indicate an error. Thus, errno and perror cannot be used to display the correct error message. Also, gethostbyname and gethostbyaddr are not thread-safe because they use static data for storing the returned struct hostent . They should not be used in threaded programs without appropriate precautions being taken. (See Section 18.9.) A given implementation might use the same static data for both of these, so be careful to copy the result before it is modified.

A second method for converting between host names and addresses, getnameinfo and getaddrinfo , first entered an approved POSIX standard in 2001. These general functions, which can be used with both IPv4 and IPv6, are preferable to gethostbyname and gethostbyaddr because they do not use static data. Instead, getnameinfo stores the name in a user -supplied buffer, and getaddrinfo dynamically allocates a buffer to return with the address information. The user can free this buffer with freeaddrinfo . These functions are safe to use in a threaded environment. The only drawback in using these functions, other than the complication of the new structures used, is that they are not yet available on many systems.

  SYNOPSIS  #include <sys/socket.h>      #include <netdb.h>      void freeaddrinfo(struct addrinfo *ai);      int getaddrinfo(const char *restrict nodename,                      const char *restrict servname,                      const struct addrinfo *restrict hints,                      struct addrinfo **restrict res);      int getnameinfo(const struct sockaddr *restrict sa,                      socklen_t salen, char *restrict node,                      socklen_t nodelen, char *restrict service,                      socklen_t servicelen, unsigned flags);  POSIX  

If successful, getaddrinfo and getnameinfo return 0. If unsuccessful, these functions return an error code. The following table lists themandatory error codes for getaddrinfo and getnameinfo .

error

cause

EAI_AGAIN

name cannot be resolved at this time

EAI_BADFLAGS

flags had an invalid value

EAI_FAIL

unrecoverable error

EAI_FAMILY

address family was not recognized or address length invalid for specified family

EAI_MEMORY

memory allocation failure

EAI_NONAME

name does not resolve for supplied parameters

EAI_SERVICE

service passed not recognized for socket ( getaddrinfo )

EAI_SOCKTYPE

intended socket type not recognized ( getaddrinfo )

EAI_SYSTEM

a system error occurred and error code can be found in errno

EAI_OVERFLOW

argument buffer overflow ( getaddrinfo )

The struct addrinfo structure contains at least the following members.

 int              ai_flags;       /* input flags */ int              ai_family;      /* address family */ int              ai_socktype;    /* socket type */ int              ai_protocol;    /* protocol of socket */ socklen_t        ai_addrlen;     /* length of socket address */ struct sockaddr  *ai_addr;       /* socket address */ char             *ai_canonname;  /* canonical service name */ struct addrinfo  *ai_next;       /* pointer to next entry */ 

The user passes the name of the host in the nodename parameter of getaddrinfo . The servname parameter can contain a service name (in IPv6) or a port number. For our purposes, the nodename determines the address, and the servname parameter can be a NULL pointer. The hints parameter tells getaddrinfo what type of addresses the caller is interested in. For IPv4, we set ai_flags to 0. In this case, ai_family, ai_socktype and ai_protocol are the same as in socket . The ai_addrlen parameter can be set to 0, and the remaining pointers can be set to NULL . The getaddrinfo function, using the res parameter, returns a linked list of struct addrinfo nodes that it dynamically allocates to contain the address information. When finished using this linked list, call freeaddrinfo to free the nodes.

Program 18.11 shows an implementation of name2addr that uses getaddrinfo . After calling getaddrinfo , the function copies the address and frees the memory that was allocated.

Program 18.11 name2addr_getaddrinfo.c

An implementation of name2addr using getaddrinfo .

 #include <ctype.h> #include <netdb.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> int name2addr(char *name, in_addr_t *addrp) {     struct addrinfo hints;     struct addrinfo *res;     struct sockaddr_in *saddrp;     hints.ai_flags = 0;     hints.ai_family = PF_INET;     hints.ai_socktype = SOCK_STREAM;     hints.ai_protocol = 0;     hints.ai_addrlen = 0;     hints.ai_canonname = NULL;     hints.ai_addr = NULL;     hints.ai_next = NULL;     if (getaddrinfo(name,NULL,&hints,&res) != 0)         return -1;     saddrp = (struct sockaddr_in *)(res->ai_addr);     memcpy(addrp, &saddrp->sin_addr.s_addr, 4);     freeaddrinfo(res);     return 0; } 

To use getnameinfo to convert an address to a name, pass a pointer to a sockaddr_in structure in the first parameter and its length in the second parameter. Supply a buffer to hold the name of the host as the third parameter and the size of that buffer as the fourth parameter. Since we are not interested in the service name, the fifth parameter can be NULL and the sixth parameter can be 0. The last parameter is for flags, and it can be 0, causing the fully qualified domain name to be returned. The sin_family field of the sockaddr_in should be AF_INET , and the sin_addr field contains the addresses. If the name cannot be determined, the numeric form of the host name is returned, that is, the dotted-decimal form of the address.

Program 18.12 shows an implementation of addr2name . The addr2name function never returns an error. Instead, it calls inet_ntoa if getnameinfo produces an error.

Program 18.12 addr2name_getnameinfo.c

An implementation of addr2name using getnameinfo .

 #include <ctype.h> #include <netdb.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> void addr2name(struct in_addr addr, char *name, int namelen) {     struct sockaddr_in saddr;     saddr.sin_family = AF_INET;     saddr.sin_port = 0;     saddr.sin_addr = addr;     if (getnameinfo((struct sockaddr *)&saddr, sizeof(saddr), name, namelen,                     NULL, 0, 0) != 0) {         strncpy(name, inet_ntoa(addr), namelen-1);         name[namelen-1] = 0;     } } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

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