< IPv4 & IPv6 Addressing | Internet Protocol Main | Program Examples >


 

Winsock2 and Internet Protocol 3 Part 2

 

What do we have in this chapter 3 part 2?

 

  1. Name Resolution Routines

  2. The Unicode Version, GetAddrInfoW()

 

 

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:

 

  1. ai_flags indicates one of three values: AI_PASSIVE, AI_CANONNAME, or AI_NUMERICHOST. AI_CANONNAME indicates that nodename is a computer name like www.microsoft.com and AI_NUMERICHOST indicates that it is a literal string address such as “10.10.10.1”. AI_PASSIVE will be discussed later.
  2. ai_family can indicate AF_INET, AF_INET6, or AF_UNSPEC. If you wish to resolve to a specific address, type the supply AF_INET or AF_INET6. Otherwise, if AF_UNSPEC is given, then the addresses returned could be either IPv4 or IPv6 or both.
  3. ai_socktype specifies the desired socket type, such as SOCK_DGRAM, SOCK_STREAM. This field is used when servname contains the name of a service. That is, some services have different port numbers depending on whether UDP or TCP is used.
  4. ai_protocol specifies the desired protocol, such as IPPROTO_TCP. Again, this field is useful when servname indicates a service.

 

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.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: creating new Win32 empty console application for getaddressinfo() function

 

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 Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: getaddrinfo() 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.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: creating another new getaddrinfo() example

 

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 Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the getaddrinfo() sample output

 

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the getaddrinfo() function sample output for IPv4

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: getaddrinfo() sample output for IPv6

 

The Unicode Version, GetAddrInfoW()

 

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.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the getaddrinfoW() new win32 console application project

 

 

 

 

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.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the getaddrinfoW() sample output

 

 

 


< IPv4 & IPv6 Addressing | Internet Protocol Main | Program Examples >