In this section, we'll cover how to assign both literal string addresses and resolve names to the address specific structures for both IP protocols. First, we will cover the new name resolution APIs: getaddrinfo and getnameinfo. These APIs have replaced the IPv4 specific routines. Then we'll cover the generic Winsock APIs for converting between string literal addresses and socket address structure. These APIs are WSAAddressToString andWSAStringToAddress. Note that these functions perform only address conversion and assignment, not name resolution.
Next, the IPv4 specific legacy routines will be described. We include the legacy API descriptions in case legacy code needs to be maintained, but any new projects should use the newer API functions. By using the newer functions it will be trivial to write an application that can seamlessly operate over both IPv4 and IPv6, which is the topic of the last section in this chapter.
Finally, note that all the name resolution functions covered in this chapter deal only with resolving names and not registering a name with an address. This is accomplished by the Winsock Registration and Name Resolution (RNR) APIs discussed in Chapter 8.
Name Resolution Routines :
Along with IPv6, several new name resolution functions were introduced that could handle both IPv4 and IPv6 addresses. The legacy functions likegethostbyname and inet_addr work with IPv4 addresses only. The replacement functions are named getnameinfo and getaddrinfo.
These new name resolution routines are defined in WS2TCPIP.H. Also, note that although these functions are new for Windows XP, they can be made available to work on all Winsock 2 enabled platforms. This is done by including the header file WSPIAPI.H before including WS2TCPIP.H. The compiled binary will then run on all Winsock 2–enabled platforms, such as Windows 95, Windows 98, Windows Me, Windows NT 4.0, and Windows 2000. The getaddrinfofunction provides protocol-independent name resolution. The function prototype is :
- int getaddrinfo(
- const char FAR *nodename,
- const char FAR *servname,
- const struct addrinfo FAR *hints,
- struct addrinfo FAR *FAR *res
- );
- struct addrinfo {
- int ai_flags;
- int ai_family;
- int ai_socktype;
- int ai_protocol;
- size_t ai_addrlen;
- char *ai_canonname;
- struct sockaddr *ai_addr;
- struct addrinfo *ai_next;
- };
If no hints are passed into getaddrinfo, the function behaves as if a zeroed hints structure was provided with an ai_family of AF_UNSPEC.
If the function succeeds, then the resolved addresses are returned via res. If the name resolved to more than one address, then the result is a linked list chained by the ai_next field. Every address resolved from the name is indicated in ai_addr with the length of that socket address structure given in ai_addrlen. These two fields may be passed directly into bind, connect, sendto, etc.
The following code sample illustrates how to resolve a hostname along with the port number before making a TCP connection to the server :
- SOCKET s;
- struct addrinfo hints,
- *result;
- int rc;
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_CANONNAME;
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- rc = getaddrinfo("foobar", "5001", &hints, &result);
- if (rc != 0) {
- // unable to resolve the name
- }
- s = socket(result->ai_family, result->ai_socktype,
- result->ai_protocol);
- if (s == INVALID_SOCKET) {
- // socket API failed
- }
- rc = connect(s, result->ai_addr, result->ai_addrlen);
- if (rc == SOCKET_ERROR) {
- // connect API failed
- }
- freeaddrinfo(result);
Another common action that applications perform is assigning a literal string address such as “172.17.7.1” or “fe80::1234” into a socket address structure of the appropriate type. The getaddrinfo function does this by setting the AI_NUMERICHOST flag within the hints. The following code illustrates this :
- struct addrinfo hints,
- *result;
- int rc;
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_NUMERICHOST;
- hints.ai_family = AI_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- rc = getaddrinfo("172.17.7.1", "5001", &hints, &result);
- if (rc != 0) {
- // invalid literal address
- }
- // Use the result
- freeaddrinfo(result);
Note that if no flags are passed as part of the hints and a literal string address is resolved, the returned structure addrinfo containing the converted address will have the AI_NUMERICHOST flags set. Likewise, if a hostname is resolved but no hints are passed, the returned structure addrinfo flag will containAI_CANONNAME.
The last flag that can be used with getaddrinfo is AI_PASSIVE, which is used to obtain an address that can be passed to the bind function. For IPv4, this would be INADDR_ANY (0.0.0.0) and for IPv6 it would be IN6ADDR_ANY (::). To obtain the bind address, the hints should indicate which address family the passive address is to be obtained for (via ai_family), nodename should be NULL, and servname should be non-NULL—indicating the port number the application will bind to (which can be “0”). If AF_UNSPEC is passed in the hints, then two addrinfo structures will be returned, one with the IPv4 bind address and the other with the IPv6 bind address.
The AI_PASSIVE flag is useful after resolving a hostname via getaddrinfo. Once the resolved address is returned, the original result's ai_family can be used in another call to getaddrinfo to obtain the appropriate bind address for that address family. This prevents applications from having to touch the internal socket address structure's fields and also removes the need for two separate code paths for binding the socket depending on which address family the address was resolved to.
The other new name resolution API is getnameinfo, which performs the reverse of the getaddrinfo function. It takes a socket address structure already initialized and returns the host and service name corresponding to the address/port information. The getnameinfo function is prototyped as the following :
- int getnameinfo(
- const struct sockaddr FAR *sa,
- socklen_t salen,
- char FAR *host,
- DWORD hostlen,
- char FAR *serv,
- DWORD servlen,
- int flags
- );
There is a file named RESOLVER.CPP that performs name resolution on the companion CD. A hostname or address is passed to the application along with service or port information and is resolved via getaddrinfo. Then for every resolved address returned, getnameinfo is called to obtain either the machine name back or the string literal address.
Simple Address Conversion :
When an application needs only to convert between string literal addresses and socket address structures, the WSAStringToAddress andWSAAddressToString APIs are available. WSAStringToAddress is not as "smart" as getaddrinfo because you must specify the address family that the string address belongs to. The API is the following :
- INT WSAStringToAddress(
- LPTSTR AddressString,
- INT AddressFamily,
- LPWSAPROTOCOL_INFO lpProtocolInfo,
- LPSOCKADDR lpAddress,
- LPINT lpAddressLength
- );
Note that this API will convert string addresses that contain port numbers. For example, the IPv4 string notation allows a colon followed by the port number at the end of the address. For example, “157.54.126.42:1200” indicates the IPv4 address using port 1200. In IPv6, the IPv6 address string must be enclosed in square brackets after which the colon and port notation may be used. For example, [fe80::250:8bff:fea0:92ed%5]:80 indicates a link-local address with its scope ID followed by port 80. Note that only port numbers will be resolved and not service names (such as “ftp”). For both these examples, if these strings were converted with WSAStringToAddress, then the returned socket address structure will be initialized with the appropriate binary IP address, port number, and address family. For IPv6, the scope ID field will also be initialized if the string address contains “%scope_ID” after the address portion.
The WSAAddressToString provides a mapping from a socket address structure to a string representation of that address. The prototype is :
- INT WSAAddressToString(
- LPSOCKADDR lpsaAddress,
- DWORD dwAddressLength,
- LPWSAPROTOCOL_INFO lpProtocolInfo,
- LPTSTR lpszAddressString,
- LPDWORD lpdwAddressStringLength
- );
Legacy Name Resolution Routines :
This section covers the legacy name resolution and is included only for the sake of code maintenance, because new applications should be using getaddrinfoand getnameinfo. The other feature you will notice is that the two new API calls replace eight legacy functions.
The function inet_addr converts a dotted IPv4 address to a 32-bit unsigned long integer quantity. The inet_addr function is defined as :
- unsigned long inet_addr(
- const char FAR *cp
- );
The reverse of inet_addr is inet_ntoa, which takes an IPv4 network address and converts it to a string. This function is declared as :
- char FAR *inet_ntoa(
- Struct in_addr in
- );
- SOCKADDR_IN InternetAddr;
- INT nPortId = 5150;
- InternetAddr.sin_family = AF_INET;
- // Convert the proposed dotted Internet address 136.149.3.29
- // to a 4-byte integer, and assign it to sin_addr
- InternetAddr.sin_addr.s_addr = inet_addr("136.149.3.29");
- // The nPortId variable is stored in host-byte order. Convert
- // nPortId to network-byte order, and assign it to sin_port.
- InternetAddr.sin_port = htons(nPortId);
- struct hostent
- {
- char FAR * h_name;
- char FAR * FAR * h_aliases;
- short h_addrtype;
- short h_length;
- char FAR * FAR * h_addr_list;
- };
- struct hostent FAR * gethostbyname (
- const char FAR * name
- );
- HANDLE WSAAsyncGetHostByName(
- HWND hWnd,
- unsigned int wMsg,
- const char FAR * name,
- char FAR * buf,
- int buflen
- );
- struct HOSTENT FAR * gethostbyaddr(
- const char FAR * addr,
- int len,
- int type
- );
- HANDLE WSAAsyncGetHostByAddr(
- HWND hWnd,
- unsigned int wMsg,
- const char FAR *addr,
- int len,
- int type,
- char FAR *buf,
- int buflen
- );
Finally, these and the rest of the asynchronous name and service resolution functions return a HANDLE identifying the operation issued. Upon completion, a window message indicated by wMsg is posted to the window given by hWnd. If at some point the application wishes to cancel the asynchronous request, theWSACancelAsyncRequest function is used. This function is declared as :
- int WSACancelAsyncRequest(
- HANDLE hAsyncTaskHandle
- );
The next type of legacy resolution functions provide the capability to retrieve port numbers for well-known services and the reverse. The API functionsgetservbyname and WSAAsyncGetServByName take the name of a well-known service like “FTP” and return the port number that the service uses. The functions getservbyport and WSAAsyncGetServByPort perform the reverse operation by taking the port number and returning the service name that uses that port. These functions simply retrieve static information from a file named services. In Windows 95, Windows 98, and Windows Me, the services file is located under %WINDOWS%; in Windows NT, it is located under %WINDOWS%\System32\Drivers\Etc.
These four functions return the service information in a SERVENT structure that is defined as :
- struct servent {
- char FAR * s_name;
- char FAR * FAR *s_aliases;
- short s_port;
- char FAR * s_proto
- };
- struct servent FAR * getservbyname(
- const char FAR * name,
- const char FAR * proto
- );
- HANDLE WSAAsyncGetServByName(
- HWND hWnd,
- unsigned int wMsg,
- const char FAR *name,
- const char FAR *proto,
- char FAR *buf,
- int buflen
- );
- struct servent FAR *getservbyport(
- int port,
- const char FAR *proto
- );
- HANDLE WSAAsyncGetServByPort(
- HWND hWnd,
- unsigned int wMsg,
- int port,
- const char FAR *proto,
- char FAR *buf,
- int buflen
- );
The last set of legacy name resolution API functions convert between a protocol string name, such as “tcp”, and its protocol number (“tcp” would resolve toIPPROTO_TCP.). These functions are getprotobyname, WSAAsyncGetProtoByName, getprotobynumber, and WSAAsyncGetProtoByNumber. The first two convert from the string protocol to the protocol number and the latter two do the opposite—map the protocol number back to its string name. These functions return a PROTOENT structure defined as :
- struct protoent {
- char FAR * p_name;
- char FAR * FAR *p_aliases;
- short p_proto;
- };
- struct protoent FAR *getprotbyname(
- const char FAR *name
- );
- HANDLE WSAAsyncGetProtoByName(
- HWND hWnd,
- unsigned int wMsg,
- const char FAR *name,
- char FAR *buf,
- int buflen
- );
- struct protoent FAR *getprotobynumber(
- int number
- );
- HANDLE WSAAsyncGetProtoByNumber(
- HWND hWnd,
- unsigned int wMsg,
- int number,
- char FAR *buf,
- int buflen
- );
沒有留言:
張貼留言