< IPv4 & IPv6 Addressing | Internet Protocol Main | Program Examples >
What do we have in this chapter 3 part 2?
|
Name Resolution Routines
Along with IPv6, several new name resolution functions were introduced that could handle both IPv4 and IPv6 addresses. The legacy functions like gethostbyname() 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 getaddrinfo() function 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 );
The nodename parameter specifies the NULL-terminated host name or literal address. The servname is a NULL-terminated string containing the port number or a service name such as “ftp” or “telnet.” The third parameter, hints, is a structure that can pass one or more options that affect how the name resolution is performed. Finally, the res parameter returns a linked list of addrinfo structure containing the addresses the string name was resolved to. If the operation succeeds, zero is returned; otherwise the Winsock error code is returned. The addrinfo structure is defined as:
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; }; |
When passing hints into the API, the structure should be zeroed out beforehand, and the first four fields are relevant:
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 snippet example shows the use of the addrinfo structure.
// Declare and initialize variables.
char* ip = "127.0.0.1";
char* port = "7777";
struct addrinfo aiHints;
struct addrinfo *aiList = NULL;
int retVal;
// Setup the hints address info structure
// which is passed to the getaddrinfo() function
memset(&aiHints, 0, sizeof(aiHints));
aiHints.ai_family = AF_INET;
aiHints.ai_socktype = SOCK_STREAM;
aiHints.ai_protocol = IPPROTO_TCP;
// Call getaddrinfo(). If the call succeeds, the aiList variable
// will hold a linked list f addrinfo structures containing
// response information about the host
if ((retVal = getaddrinfo(ip, port, &aiHints, &aiList)) != 0)
{
printf("getaddrinfo() failed with error code %d.\n", retVal);
}
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("bodopiang", "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);
In this example, the application is resolving the hostname “bodopiang” and wants to establish a TCP connection to a service on port 5001. You'll also notice that this code doesn't care if the name resolved to an IPv4 or an IPv6 address. It is possible that “bodopiang” has both IPv4 and IPv6 addresses registered, in which case result will contain additional addrinfo structures linked by the ai_next field. If an application wanted only IPv4 addresses registered to “bodopiang,” the hints.ai_family should be set to AF_INET. Finally, note that the information returned via res is dynamically allocated and needs to be freed by calling the freeaddrinfo() API once the application is finished using the information.
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);
The following program example tries to demonstrate the working version. Create a new empty Win32 console mode application and add the project/solution name.
Add the code given below.
#include <winsock2.h>
// For addrinfo structure
#include <ws2tcpip.h>
#include <stdio.h>
int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET s;
int rc, iResult, RetCode;
struct addrinfo *result = NULL;
struct addrinfo hints;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("Client: WSAStartup() failed with error code %d\n", iResult);
return 1;
}
else
printf("Client: WSAStartup() is OK...\n");
// Setup the hints address info structure
// which is passed to the getaddrinfo() function
ZeroMemory( &hints, sizeof(hints) );
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// rc = getaddrinfo("206.190.60.37", "7171", &hints, &result);
rc = getaddrinfo("www.ipv6tf.org", "80", &hints, &result);
if (rc != 0)
{
printf("Client: getaddrinfo() failed with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
printf("Client: getaddrinfo() looks OK!\n");
// Create a new socket to make a client connection.
s = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if(s == INVALID_SOCKET)
{
printf("Client: socket() failed! Error code: %ld\n", WSAGetLastError());
// Do the clean up
WSACleanup();
// Exit with error
return 1;
}
else
printf("Client: socket() is OK!\n");
// Make a connection to the server with socket SendingSocket.
RetCode = connect(s, result->ai_addr, result->ai_addrlen);
if(RetCode != 0)
{
printf("Client: connect() failed! Error code: %ld\n", WSAGetLastError());
// Close the socket
closesocket(s);
// Do the clean up
WSACleanup();
// Exit with error
return 1;
}
else
{
printf("Client: connect() is OK, got connected...\n");
printf("Client: Ready for sending and/or receiving data...\n");
}
// Return the allocated resource to the heap
freeaddrinfo(result);
// When your application is finished handling the connection, call WSACleanup.
if(WSACleanup() != 0)
printf("Client: WSACleanup() failed!...\n");
else
printf("Client: WSACleanup() is OK...\n");
return 0;
}
Build and run the project. The following screenshot shows a sample output.
The literal address “172.17.7.1” will be converted to the necessary socket address structure and returned via result. Because AF_UNSPEC is passed, the API will determine the correct socket address structure (SOCKADDR_IN or SOCKADDR_IN6) required and convert the address accordingly. As before, the port field of the resulting socket address structure will be initialized to 5001.
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 contain AI_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. Take note that another name that you will find for getaddrinfo() is getaddrinfoA().
The following is the program example. Create a new empty Win32 console mode application and add the project/solution name.
Add the following code.
// link with Ws2_32.lib
#include <winsock2.h>
// For addrinfo structure
#include <ws2tcpip.h>
#include <stdio.h>
int main(int argc, char **argv)
{
// Declare and initialize variables
WSADATA wsaData;
int iResult;
DWORD dwRetval;
int i = 1;
char *port = "7171";
struct addrinfo *result = NULL;
struct addrinfo *ptr = NULL;
struct addrinfo hints;
// Validate the parameters
if (argc != 2)
{
printf("Usage: %s <hostname>\n", argv[0]);
printf(" getaddrinfo() provides protocol-independent translation\n");
printf(" from an ANSI host name to an IP address\n");
printf("Example: %s www.contoso.com\n", argv[0]);
return 1;
}
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
printf("WSAStartup() failed with error code %d\n", iResult);
return 1;
}
else
printf("WSAStartup() is OK...\n");
// Setup the hints address info structure
// which is passed to the getaddrinfo() function
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Call getaddrinfo(). If the call succeeds,
// the result variable will hold a linked list
// of addrinfo structures containing response information
dwRetval = getaddrinfo(argv[1], port, &hints, &result);
if ( dwRetval != 0 )
{
printf("getaddrinfo() failed with error: %d\n", dwRetval);
WSACleanup();
return 1;
}
printf("getaddrinfo() returned success!\n");
// Retrieve each address and print out the hex bytes
for(ptr=result; ptr!=NULL; ptr=ptr->ai_next)
{
printf("\ngetaddrinfo() response %d\n", i++);
printf("\tFlags: 0x%x\n", ptr->ai_flags);
printf("\tFamily: ");
switch (ptr->ai_family)
{
case AF_UNSPEC:
printf("Unspecified\n");
break;
case AF_INET:
printf("AF_INET (IPv4)\n");
break;
case AF_INET6:
printf("AF_INET6 (IPv6)\n");
break;
case AF_NETBIOS:
printf("AF_NETBIOS (NetBIOS)\n");
break;
default:
printf("Other %ld\n", ptr->ai_family);
break;
}
printf("\tSocket type: ");
switch (ptr->ai_socktype)
{
case 0:
printf("Unspecified\n");
break;
case SOCK_STREAM:
printf("SOCK_STREAM (stream)\n");
break;
case SOCK_DGRAM:
printf("SOCK_DGRAM (datagram) \n");
break;
case SOCK_RAW:
printf("SOCK_RAW (raw) \n");
break;
case SOCK_RDM:
printf("SOCK_RDM (reliable message datagram)\n");
break;
case SOCK_SEQPACKET:
printf("SOCK_SEQPACKET (pseudo-stream packet)\n");
break;
default:
printf("Other %ld\n", ptr->ai_socktype);
break;
}
printf("\tProtocol: ");
switch (ptr->ai_protocol) {
case 0:
printf("Unspecified\n");
break;
case IPPROTO_TCP:
printf("IPPROTO_TCP (TCP)\n");
break;
case IPPROTO_UDP:
printf("IPPROTO_UDP (UDP) \n");
break;
default:
printf("Other %ld\n", ptr->ai_protocol);
break;
}
printf("\tLength of this sockaddr: %d\n", ptr->ai_addrlen);
printf("\tCanonical name: %s\n", ptr->ai_canonname);
}
// Release the allocated resource
freeaddrinfo(result);
// WSA clean up
WSACleanup();
return 0;
}
The following screenshots show sample outputs.
----------------------------------------------------
The getaddrinfow() function provides protocol-independent translation from a Unicode host name to an address. Create a new empty Win32 console mode application and add the project/solution name.
Add the code given below.
// link with Ws2_32.lib
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
int wmain(int argc, wchar_t **argv)
{
// Declare and initialize variables
WSADATA wsaData;
int iResult;
DWORD dwRetval;
int i = 1;
// The L modifier can be discarded
// for VS 2005/2008. Unicode supported by default
// which can be verified through project property page
WCHAR *port = L"7172";
ADDRINFOW *result = NULL;
ADDRINFOW *ptr = NULL;
ADDRINFOW hints;
// Validate the parameters
if (argc != 2)
{
wprintf(L"Usage: %ws <hostname>\n", argv[0]);
wprintf(L" getaddrinfow provides protocol-independent translation\n");
wprintf(L" from a Unicode host name to an IP address\n");
wprintf(L"Example: %ws www.contoso.com\n", argv[0]);
return 1;
}
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
wprintf(L"WSAStartup() failed with error code %d\n", iResult);
return 1;
}
wprintf(L"WSAStartup() looks OK...\n");
// Setup the hints address info structure
// which is passed to the getaddrinfo() function
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Call GetAddrinfoW(). If the call succeeds,
// the result variable will hold a linked list
// of addrinfow structures containing response information
dwRetval = GetAddrInfoW(argv[1], port, &hints, &result);
if ( dwRetval != 0 )
{
wprintf(L"GetAddrInfoW() failed with error code %d\n", dwRetval);
WSACleanup();
return 1;
}
wprintf(L"GetAddrInfoW() returned success!\n");
// Retrieve each address and print out the hex bytes
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
{
wprintf(L"GetAddrInfoW() response %d\n", i++);
wprintf(L"\tFlags: 0x%x\n", ptr->ai_flags);
wprintf(L"\tFamily: ");
switch (ptr->ai_family)
{
case AF_UNSPEC:
wprintf(L"Unspecified\n");
break;
case AF_INET:
wprintf(L"AF_INET (IPv4)\n");
break;
case AF_INET6:
wprintf(L"AF_INET6 (IPv6)\n");
break;
default:
wprintf(L"Other %ld\n", ptr->ai_family);
break;
}
wprintf(L"\tSocket type: ");
switch (ptr->ai_socktype)
{
case 0:
wprintf(L"Unspecified\n");
break;
case SOCK_STREAM:
wprintf(L"SOCK_STREAM (stream)\n");
break;
case SOCK_DGRAM:
wprintf(L"SOCK_DGRAM (datagram) \n");
break;
case SOCK_RAW:
wprintf(L"SOCK_RAW (raw) \n");
break;
case SOCK_RDM:
wprintf(L"SOCK_RDM (reliable message datagram)\n");
break;
case SOCK_SEQPACKET:
wprintf(L"SOCK_SEQPACKET (pseudo-stream packet)\n");
break;
default:
wprintf(L"Other %ld\n", ptr->ai_socktype);
break;
}
wprintf(L"\tProtocol: ");
switch (ptr->ai_protocol)
{
case 0:
wprintf(L"Unspecified\n");
break;
case IPPROTO_TCP:
wprintf(L"IPPROTO_TCP (TCP)\n");
break;
case IPPROTO_UDP:
wprintf(L"IPPROTO_UDP (UDP) \n");
break;
default:
wprintf(L"Other %ld\n", ptr->ai_protocol);
break;
}
wprintf(L"\tLength of this sockaddr: %d\n", ptr->ai_addrlen);
wprintf(L"\tCanonical name: %s\n", ptr->ai_canonname);
}
// Return the allocated resource back to the heap
FreeAddrInfoW(result);
// WSA clean up
WSACleanup();
return 0;
}
Build and run the project. The following screenshot shows a sample output.
< IPv4 & IPv6 Addressing | Internet Protocol Main | Program Examples >