Database Routines

The TCP/IP protocol relies on the binary representations for addresses and various other identifiers. However, end users and programmers prefer to use easy-to-remember names (such as "ftp rhino.microsoft.com"). It is therefore necessary to provide a common method to resolve both services and hostnames into their respective binary equivalents. To solve this, the Windows Sockets specification offers a set of APIs known as the database routines.

The database routines fall into three categories:

host resolution

Learning the IP address for a host based on system, or host name

protocol resolution

Learning the protocol ID of a specific member of a protocol family (TCP, for example)

service resolution

Learning the port ID of a service based on a service name/protocol pair


All the database routines return information in structures defined in the previous section.

Applications use the gethostbyname() and gethostbyaddr() functions to learn about the names and IP address(es) of a particular system, knowing only the name or the address of the system. Both calls return a pointer to a hostent structure as defined in the previous section. The gethostbyname() call simply accepts a pointer to a null-terminated string representing the name of the system to resolve. The gethostbyaddr() instead accepts three parameters: a pointer to the address (in network byte order), the length of the address, and the type of address.

Generally the hostname or IP address is offered to the application by the user to specify a remote system to connect to, and the IP address is resolved by Windows Sockets by either parsing a local hosts file, or querying a DNS (domain name system) server. The details of the resolution however, are specific to the implementation, abstracted from the application by these APIs.

The getservbyname() and getservbyport() functions return information about well-known Windows Sockets services, or applications. Each of these system calls return a pointer to a servent structure, as defined in the previous section. Typically an application will use these calls to determine the port ID for a well-known service (such as FTP) to create an endpoint address.

The following code fragment demonstrates the use of the getservbyname() function to fill in the sockaddr_in structure which will be used to connect a socket to a well-known port (the FTP protocol port over TCP):


char            buf [MAX_BUF_LEN];
struct sockaddr_in        srv_addr;
LPSERVENT            srv_info;
LPHOSTENT            host_info;
SOCKET            s;
.
.
.
/* Get FTP service port information */

srv_info=getservbyname("ftp","tcp");

if (srv_info== NULL) {
    /* Couldn't find an entry for "ftp" over "tcp" */

    sprintf(buf,"Windows Sockets error %d: Couldn't resolve FTP service port.",
         WSAGetLastError());
    MessageBox (hWnd,buf,"Windows Sockets Error",MB_OK);
    shutdown_app();
}

/* Set up socket */

srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = INADDR_ANY;
srv_addr.sin_port=srv_info->s_port;

The example uses getservbyname() to resolve the port number of the FTP service over TCP. The port ID is used to construct the sockaddr_in structure (endpoint address) for future use by the application. As we mentioned before, the address family for TCP/IP is always assigned as AF_INET. We use the INADDR_ANY macro to specify any local IP interface to accept incoming connections (more on this later).

To round out the database routines, getprotobyname() and getprotobynumber() fill in a protoent structure, sometimes used by applications to create a socket over a particular protocol (e.g., UDP or TCP). More often, however, an application will use the SOCK_DGRAM and SOCK_STREAM macros to create either datagram or stream sockets.