Network Names, Addresses, and Services

Declared in: be/kit/net/netdb.h

Library: libnet.so

The functions described below let you look up the names, addresses, and other information about the computers and services that the local computer knows about, and let you retrieve information about the current user's account. Also defined here are functions that perform Internet Protocol (IP) address format conversion.

You use the functions defined here to find the information you need so you can form a connection to some other machine.


Terms and Tools

Throughout the following function descriptions, an IP address is the familiar four-byte, dot-separated numeric identifier. For example,

192.0.0.1

The bytes in a multi-byte address are always given in network byte order (big-endian). You should always run your addresses through the appropriate host/network conversion. See the group of functions with the obsessively shortened names (ntohs(), htohl(), etc.) for more information on such transformations.

An IP name is the two-or-three-element "[machine.]domain.extension" case-insensitive text name. For example:

The two most important functions described below, gethostbyname() and gethostbyaddr(), retrieve information about computers ("hosts") that can be reached through the network. Host information is typically (and primarily) gotten from the Domain Name Server (DNS), a service that's usually provided by a server computer that's responsible for tasks such as mail distribution and direct communication with the Internet Service Provider (ISP).

You can also provide host information by adding to your computer's /boot/beos/etc/hosts file. This is a text file that contains the IP addresses and names of the hosts that you want your computer to know about. Each entry in the file lists, in order on a single line, a host's IP address, IP name, and other names (aliases) by which it's also known. For example:

   # Example /boot/beos/etc/hosts entries
   192.0.0.1 phaedo.racine.com fido phydough
   204.123.5.12 playdo.mess.com plywood funfactory

The amount of whitespace separating the elements is arbitrary. The only killing point is that there mustn't be any leading whitespace before the IP address.

If you're connected to DNS, then you shouldn't need the hosts file. If you're not connected to a network at all, the only way to get information about other machines is through the hosts file, but it won't do you much good--you won't be able to use the information to connect to other machines. The archetypal situation in which the hosts file becomes useful is if your computer is connected to some other machine that's supposed to be connected to a DNS machine, but this latter connection is down (or the DNS machine isn't running). If you have an entry in your hosts file that identifies the other machine, you'll still be able to look up the machine's address and connect to it, despite the absence of DNS.


Functions


gethostbyname(), gethostbyaddr(), herror()

      struct hostent *gethostbyname(const char *name)
      struct hostent *gethostbyaddr(const char *address, int length, int type)
      void herror(const char *string)

The gethostbyname() and gethostbyaddr() functions retrieve information about a particular host machine, stuff the information into a global "host entry" structure, and then return a pointer to that structure. To get this information, the functions talks to the Domain Name Server. If DNS doesn't respond or doesn't know the desired host, the functions then look for an entry in the file /boot/beos/system/etc/hosts. See "Terms and Tools" on page 18 for more information on DNS and the hosts file.

herror() generates a human-readable message that describes the most recent gethostby...() error, and prints it to standard error.

Because gethostbyname() and gethostbyaddr() use a global structure to return information, the functions are not thread safe.

The gethostbyname() Function

gethostbyname()'s name argument is a NULL-terminated, case-insensitive host name that must be no longer than MAXHOSTNAMELEN (64) characters (not counting the NULL). The name can be:

The gethostbyaddr() Function

gethostbyaddr()'s address argument is a pointer to a complete IP address given in its natural format (but cast to a char *; note that the argument's type declaration doesn't mean that the function wants the address converted to a string). length is the length of address in bytes; type is a constant that gives the format of the address.

For IP format, the first argument is a four-byte integer, length is always 4, and type is AF_INET ("Address Format: InterNET"). The following gets the hostent for a hard-coded address:

   /* This is the hex equivalent of 192.0.0.1
   ulong addr = 0xc0000001;
   struct hostent *theHost;
   theHost = gethostbyaddr((const char *)&addr, 4, AF_INET);

If you have an address stored as a string, you can use the inet_addr() function to convert it to an integer:

   ulong addr = inet_addr("192.0.0.1");
   struct hostent *theHost;
   theHost = gethostbyaddr((const char *)&addr, 4, AF_INET);

The hostent Structure

If a gethostby...() function fails, it returns NULL; otherwise, it returns a pointer to a global hostent structure. The hostent structure (which isn't typedef'd) looks like this:

   struct hostent 
      char *h_name;
      char **h_aliases;
      int h_addrtype;
      int h_length;
      char **h_addr_list;
   };

The fields are:

As a convenience, the global h_addr constant is a fake field that points to the first item in the h_addr_list field. Keep in mind that h_addr must be treated as a structure field--it must point off a hostent structure. Also, make sure you dereference the h_addr "field" properly. For example:

   ulong ip_address;
   struct hostent *theHost;
   
   theHost = gethostbyname("fido"); 
   ip_address = *(ulong *)theHost->h_addr;

As a demonstration of the h_addr definition, the final line is the same as

   ip_address = *(ulong *)theHost->h_addr_list[0];

Keep in mind that the hostent structure that's pointed to by the gethostby...() functions is global to your application's address space. If you want to cache the structure, you should copy it as soon as it's returned to you.

h_errno and the herror() Function

The host look-up functions use a global error variable (an integer), called h_errno, to register errors. You can look at the h_errno value directly in your code after a host function fails (the potential h_errno values are listed below). Alternatively, you can use the herror() function which prints, to standard error, its argument followed by a system-generated string that describes the current state of h_errno.

The values that h_errno can take, and the corresponding herror() messages, are:

Value Message
HOST_NOT_FOUND "unknown host name"
TRY_AGAIN "host name server busy"
NO_RECOVERY "unrecoverable system error"
NO_DATA "no address data is available for this host name"
anything else "unknown error"

Note that while h_errno is set when something goes wrong, it isn't cleared if all is well. For example, if gethostbyname() can't find the named host, h_errno is set to HOST_NOT_FOUND and the function returns NULL. If, in an immediately subsequent call, the function succeeds, a pointer to a valid hostent is returned, but h_errno will still report HOST_NOT_FOUND.

The moral of this tale is that you should only check h_errno (or call herror()) if the network function call has failed, or clear it yourself before each gethostby...() call. Or both:

   struct hostent *host_ent;
   
   h_errno = 0;
   if ( !(host_ent = gethostbyname("a.b.c")) {
      herror("Error");
   }

Furthermore, h_errno might be legitimately set to a new error code even if the gethostby...() function succeeds. For example, if DNS can't be reached but the desired host is found in the hosts file, h_errno will be set to TRY_AGAIN, yet the returned hostent will be legitimate (it won't be NULL).

Be aware that TRY_AGAIN is used as a blanket "DNS doesn't know" state, regardless of the reason why. In other words, h_errno is set to TRY_AGAIN if DNS is actually down, if your machine isn't connected to the network, or if DNS simply doesn't know the requested host. You can use this fact to tell whether a (successful) look-up was performed through DNS or the hosts file:

   struct hostent *host_ent;
   
   h_errno = 0;
   if ( !(host_ent = gethostbyname("a.b.c")) {
      herror("Error");
   }
   else {
      if (h_errno == TRY_AGAIN)
         /* The hosts file was used. */
      else
         /* DNS was used. */
   }

Keep in mind that h_errno is global; be careful if you're using it in a multi-threaded program.


gethostname(), getusername(), getpassword()

      int gethostname(char *name, uint length)
      int getusername(char *name, uint length)
      int getpassword(char *password, uint length)

These functions retrieve, and copy into their first arguments, the name of the local computer, the name of the current user, and the current user's encoded password, respectively. In all three case, length gives the maximum number of characters that the functions should copy. If the length of the desired element is less than length, the copied string will be NULL-terminated.

The functions return the number of characters that were actually copied (not counting the NULL terminator). If there's an error--and such should be rare--the gethostname() and getusername() functions return 0 and point their respective name arguments to NULL. getpassword(), sensing an error, copies "*" into the password argument and returns -1 (thus you can tell the difference between a NULL password--which would legitimately return 0--and an error).

All three bits of information (host name, user name, and password) are taken from the settings that are declared through the Network preferences application.

A typical use of gethostname() is to follow the call with gethostbyname() in order to retrieve the address of the local host, as shown below:

   /* To fill a need, we invent the gethostaddr() function. */
   long gethostaddr(void) {
      struct hostent *host_ent;
      char host_name[MAXHOSTNAMELEN];
   
      if (gethostname(host_name, MAXHOSTNAMELEN) == 0) {
         return -1;
      }
   
      if ((host_ent = gethostbyname(host_name)) == NULL) {
         return -1;
      }
   
      return *(long *)host_ent.h_addr;
   }

Keep in mind that since host name information is taken from Network preferences, there's no guarantee that the name that's returned by gethostname() will match an entry that DNS or the hosts file knows about.


getservbyname()

      struct servent *getservbyname(const char *name, const char *protocol)

You pass in the name of a service (such as "ftp") that runs under a particular protocol (such as "tcp"), and getservbyname() returns a pointer to a servent structure that describes the service.

The servent structure is:

   struct servent {
      char *s_name;
      char **s_aliases;
      int s_port;
      char *s_proto;
   };

The function always recognizes the "ftp" and "telnet" services, both of which run under the "tcp" protocol. For all other services, the function looks in the /etc/services file. In the file, services are listed one-per-line, in this format:

   name port/protocol

For example:

   systat         11/tcp
   systat         11/udp
   daytime         12/tcp
   daytime         12/udp

The servent structure that getservbyname() points to is global, persistent, and isn't write protected--don't change the data in the structure that's returned to you. Each service is represented by a separate servent allocation, so the function should be thread-safe.


inet_addr(), inet_ntoa()

      uint inet_addr(const char *addr)
      char *inet_ntoa(struct in_addr addr)

These functions convert addresses from ASCII to IP format and vice versa. Neither of them consults the DNS or the hosts file to perform the conversion--in other words, they perform the conversions without regard for an address' correspondence to an actual machine.

inet_addr() converts from ASCII to IP:

   ulong addr = inet_addr("192.0.0.1");

The result of this call (addr) would be appropriate as the initial argument to gethostbyaddr() (for example). The returned address is in network byte order.

inet_ntoa() converts the other way: It takes an IP address and converts it into an ASCII string. Note that the address that you pass in must first be placed in the s_addr field of the argument in_addr structure (s_addr is the structure's only field). For example:

   in_addr addr;
   char addr_buf[16];
   
   addr.s_addr = 0xc0000001;
   strcpy(addr_buf, inet_ntoa(addr));

Here, addr_buf will contain the (NULL-terminated) string "192.0.0.1". inet_ntoa() isn't thread-safe; if you want to cache the string that it returns you must copy it, as shown in the example. Given the IP format, the string that inet_ntoa() returns is guaranteed to be no more than 16 characters long (four 3-character address components, three dots, and a NULL).


ntohs(), ntohl(), htons(), htonl()

Declared in: be/support/ByteOrder.h

      short ntohs(short val)
      long ntohl(long val)
      short htons(short val)
      long htonl(long val)

These macros convert values between host and network byte order; ntohs() and ntohl() convert 16-bit and 32-bit integers from network byte order to host byte order, and htons() and htonl() convert from host byte order to network byte order.

There are other functions available for converting between big-endian and little-endian form, but these four guarantee that the correct network and host order is used, regardless of the processor for which your application is compiled.




The Be Book, in lovely HTML, for BeOS Release 4.

Copyright © 1998 Be, Inc. All rights reserved.

Last modified September 30, 1998.