< IPv4 & IPv6 Server Examples | Internet Protocol Main | Chap 4: Other Supported Protocols >


 

Winsock2 and Internet Protocol 3  Part 7

 

What do we have in this chapter 3 part 7?

 

  1. The IPv6 Client Program Example

  2. Testing IPv6 Client and Server Programs

 

The IPv6 Client Program Example

 

The following program example demonstrates the IPv6 client program with host name as the argument. Take note that we include the inet_pton() source code as a function in this program just to demo its functionalities on Windows system prior to Vista or Server 2008. In actual implementation the function should be called directly or included through the header file.

 

#include <winsock2.h>

#include <Ws2tcpip.h>

#include <stdio.h>

// for inet_pton()

#include <ctype.h>

#include <string.h>

#include <errno.h>

 

// For Windows 2008 Server or Vista please

// use the Microsoft 'standard' versions available at:

// http://msdn.microsoft.com/en-us/library/cc805844(VS.85).aspx

// http://msdn.microsoft.com/en-us/library/cc805843(VS.85).aspx

//

// UNIX/BSD/LINUX:

// http://www.opengroup.org/onlinepubs/000095399/functions/inet_ntop.html

//

//========================================================================

// Just for demo, by changing the function names, we use the source code

// directly to provide inet_pton() functionalities taken from:

// http://www.koders.com/c/fid74A647B73741DDD374D5478366A00584E8740D43.aspx?s=md5

#ifndef EAFNOSUPPORT

# define EAFNOSUPPORT EINVAL

#endif

 

#define NS_INADDRSZ 4

#define NS_IN6ADDRSZ 16

#define NS_INT16SZ 2

 

static int inet_pton4_gedik (const char *src, unsigned char *dst);

static int inet_pton6_gedik (const char *src, unsigned char *dst);

 

/* int inet_pton(af, src, dst)

 *          convert from presentation format (which usually means ASCII printable)

 *          to network format (which is usually some kind of binary format).

 * return:

 *          1 if the address was valid for the specified address family

 *          0 if the address wasn't valid (`dst' is untouched in this case)

 *          -1 if some other error occurred (`dst' is untouched in this case, too)

 * original author: Paul Vixie, 1996.

 */

int inet_pton_gedik (int af, const char *src, void *dst)

{

    switch (af)

    {

    case AF_INET:

      return (inet_pton4_gedik (src, dst));

    case AF_INET6:

      return (inet_pton6_gedik (src, dst));

    default:

      errno = EAFNOSUPPORT;

      return (-1);

    }

  /* NOTREACHED */

}

 

/* int  inet_pton4(src, dst)

 *          like inet_aton() but without all the hexadecimal, octal (with the

 *          exception of 0) and shorthand.

 * return:

 *          1 if `src' is a valid dotted quad, else 0.

 * notice:

 *          does not touch `dst' unless it's returning 1.

 * original author: Paul Vixie, 1996.

 */

static int inet_pton4_gedik (const char *src, unsigned char *dst)

{

  int saw_digit, octets, ch;

  unsigned char tmp[NS_INADDRSZ], *tp;

 

  saw_digit = 0;

  octets = 0;

  *(tp = tmp) = 0;

  while ((ch = *src++) != '\0')

    {

      if (ch >= '0' && ch <= '9')

            {

              unsigned new = *tp * 10 + (ch - '0');

 

              if (saw_digit && *tp == 0)

                return (0);

              if (new > 255)

                return (0);

              *tp = new;

              if (!saw_digit)

                {

                  if (++octets > 4)

                        return (0);

                  saw_digit = 1;

                }

            }

      else if (ch == '.' && saw_digit)

            {

              if (octets == 4)

                return (0);

              *++tp = 0;

              saw_digit = 0;

            }

      else

            return (0);

    }

  if (octets < 4)

    return (0);

  memcpy (dst, tmp, NS_INADDRSZ);

  return (1);

}

 

/* int inet_pton6(src, dst)

 *          convert presentation level address to network order binary form.

 * return:

 *          1 if `src' is a valid [RFC1884 2.2] address, else 0.

 * notice:

 *          (1) does not touch `dst' unless it's returning 1.

 *          (2) :: in a full address is silently ignored.

 * credit:

 *          inspired by Mark Andrews.

 * original author: Paul Vixie, 1996.

 */

 

static int inet_pton6_gedik (const char *src, unsigned char *dst)

{

  static const char xdigits[ ] = "0123456789abcdef";

  unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;

  const char *curtok;

  int ch, saw_xdigit;

  unsigned val;

 

  tp = memset (tmp, '\0', NS_IN6ADDRSZ);

  endp = tp + NS_IN6ADDRSZ;

  colonp = NULL;

  /* Leading :: requires some special handling. */

  if (*src == ':')

    if (*++src != ':')

      return (0);

  curtok = src;

  saw_xdigit = 0;

  val = 0;

  while ((ch = tolower (*src++)) != '\0')

    {

      const char *pch;

 

      pch = strchr (xdigits, ch);

      if (pch != NULL)

      {

              val <<= 4;

              val |= (pch - xdigits);

              if (val > 0xffff)

                return (0);

              saw_xdigit = 1;

              continue;

      }

      if (ch == ':')

      {

              curtok = src;

              if (!saw_xdigit)

              {

                  if (colonp)

                        return (0);

                  colonp = tp;

                  continue;

              }

              else if (*src == '\0')

              {

                  return (0);

              }

              if (tp + NS_INT16SZ > endp)

                return (0);

              *tp++ = (u_char) (val >> 8) & 0xff;

              *tp++ = (u_char) val & 0xff;

              saw_xdigit = 0;

              val = 0;

              continue;

            }

      if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4_gedik (curtok, tp) > 0)

      {

              tp += NS_INADDRSZ;

              saw_xdigit = 0;

              break;                       /* '\0' was seen by inet_pton4(). */

      }

      return (0);

  }

  if (saw_xdigit)

  {

      if (tp + NS_INT16SZ > endp)

            return (0);

      *tp++ = (u_char) (val >> 8) & 0xff;

      *tp++ = (u_char) val & 0xff;

  }

  if (colonp != NULL)

  {

      /*

       * Since some memmove()'s erroneously fail to handle

       * overlapping regions, we'll do the shift by hand.

       */

      const int n = tp - colonp;

      int i;

 

      if (tp == endp)

            return (0);

      for (i = 1; i <= n; i++)

      {

              endp[-i] = colonp[n - i];

              colonp[n - i] = 0;

      }

      tp = endp;

    }

  if (tp != endp)

    return (0);

  memcpy (dst, tmp, NS_IN6ADDRSZ);

  return (1);

}

 

// ========================================================================

// Some constants used by this program

#define BUFFER_LENGTH    256

#define FALSE              0

#define SERVER_NAME     "127.0.0.1"

// What is the maximum hostname/FQDN/Server name?

#define NI_MAXHOST  1025

 

// Pass in 1 parameter which is either the

// address or host name of the server, or

// set the server name in the #define SERVER_NAME

int main(int argc, char *argv[])

{

            // Variable and structure declaration an definition

            WSADATA    wsaData;

            int    sd = -1, rc, j = 0, bytesReceived = 0;

            char   buffer[BUFFER_LENGTH];

            char   server[NI_MAXHOST];

            char   *servport = "7171";

            struct in6_addr serveraddr;

            struct addrinfo hints, *res=NULL;

 

            // Initialize the use of the Winsock DLL by a process

            if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)

            {

                        printf("Client: WSAStartup() failed with error code %ld\n", WSAGetLastError());

                        return 1;

            }

            else

                        printf("Client: WSAStartup() looks fine!\n");

 

            // Using do/while(FALSE) loop for 'efficient' error cleanup

            do

            {

                        // If an argument was passed in, use this as the server, otherwise

                        // use the default value set in the #define directive

                        if (argc > 1)

                                    strcpy_s(server, sizeof(server), argv[1]);

                        else

                                    strcpy_s(server, sizeof(server), SERVER_NAME);

 

                        memset(&hints, 0x00, sizeof(hints));

                        hints.ai_flags    = AI_NUMERICSERV;

                        hints.ai_family   = AF_UNSPEC;

                        hints.ai_socktype = SOCK_STREAM;

 

                        // Check if we were provided the address of the server using

                        // inet_pton() to convert the text form of the address to binary

                        // form. If it is numeric then we want to prevent getaddrinfo()

                        // from doing any name resolution. Here we are using a custom made

                        // inet_pton() for system < Vista or Windows 2008 server...

 

                        // If no error occurs, the inet_pton()/InetPton() function returns

                        // a value of 1 and the buffer pointed to by the pAddrBuf (&serveraddr) parameter

                        // contains the binary numeric IP address in network byte order. The InetPton()

                        // function returns a value of 0 if the pAddrBuf parameter points to a string

                        // that is not a valid IPv4 dotted-decimal string or a valid IPv6 address string.

                        // Otherwise, a value of -1 is returned, and a specific error code can be retrieved

                        // by calling the WSAGetLastError()

                        rc = inet_pton_gedik(AF_INET, server, &serveraddr);

                        printf("Client: First rc returned value is %d\n", rc);

                        if (rc == 1)    // valid IPv4 text address?

                        {

                                    // When the AI_NUMERICHOST bit is set, the nodename (hostname) parameter

                                    // must contain a non-NULL numeric host address string, otherwise the

                                    // EAI_NONAME error is returned. This flag prevents a name resolution

                                    // service from being called.

                                    hints.ai_family = AF_INET;

                                    // If neither AI_CANONNAME nor AI_NUMERICHOST is used,

                                    // the getaddrinfo() function attempts resolution. If a literal

                                    // string is passed getaddrinfo() attempts to convert the string,

                                    // and if a host name is passed the getaddrinfo() function attempts

                                    // to resolve the name to an address or multiple addresses.

                                    hints.ai_flags |= AI_NUMERICHOST;

                        }

                        else

                        {

                                    rc = inet_pton_gedik(AF_INET6, server, &serveraddr);

                                    printf("Client: Second rc returned value is %d\n", rc);

                                    if (rc == 1) // valid IPv6 text address?

                                    {

                                                hints.ai_family = AF_INET6;

                                                hints.ai_flags |= AI_NUMERICHOST;

                                    }

                        }

                        // Get the address information for the server re-using getaddrinfo()

                        rc = getaddrinfo(server, servport, &hints, &res);

 

                        if (rc != 0)

                        {

                                    printf("Client: getaddrinfo() failed with error code %ld\n", WSAGetLastError());

                                    break;

                        }

                        printf("Client: getaddrinfo() returns %ld\n", rc);

                        // The socket() function returns a socket descriptor, which represents

                        // an endpoint.  The statement also identifies the address family,

                        // socket type, and protocol using the information returned from getaddrinfo()

                        sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

                        if (sd == INVALID_SOCKET)

                        {

                                    printf("Client: socket() failed with error code %ld\n", WSAGetLastError());

                                    break;

                        }

                        else

                                    printf("Client: socket() is OK...\n");

 

                        // Use the connect() function to establish a connection to the server.

                        rc = connect(sd, res->ai_addr, res->ai_addrlen);

                        if (rc == SOCKET_ERROR)

                        {

                                    // The res is a linked list of addresses found for server

                                    // If the connect() fails to the first one, subsequent addresses

                                    // (if any) in the list can be tried if required

                                    printf("Client: connect() failed with error code %ld\n", WSAGetLastError());

                                    break;

                        }

                        else

                                    printf("Client: connect() also OK!\n");

 

                        // Send 256 bytes of $'s to the server

                        memset(buffer, '$', sizeof(buffer));

 

                        rc = send(sd, buffer, sizeof(buffer), 0);

                        if (rc == SOCKET_ERROR)

                        {

                                    printf("Client: send() failed lol with error code %ld\n", WSAGetLastError());

                                    break;

                        }

                        else

                                    printf("Client: send() seems working!\n");

 

                        // In this example we know that the server is going to echo with

                        // the same 256 bytes that we just sent.  Since we know that 256

                        // bytes are going to be sent back to us, we can use the SO_RCVTIMEO

                        // socket option and then issue a single recv() and retrieve all of the dat

 

                        // The use of SO_RCVTIMEO is already illustrated in the server side of this

                        // example, so we will do something different here. The 256 bytes of the

                        // data may arrive in separate packets, therefore we will issue recv() over

                        // and over again until all 256 bytes have arrived

                        while (bytesReceived < BUFFER_LENGTH)

                        {

                                    rc = recv(sd, &buffer[bytesReceived], BUFFER_LENGTH - bytesReceived, 0);

                                    if (rc == SOCKET_ERROR)

                                    {

                                                printf("Client: recv() not working with error code %ld\n", WSAGetLastError());

                                                break;

                                    }

                                    else if (rc == 0)

                                    {

                                                printf("Client: The server closed the connection...\n");

                                                break;

                                    }

                                    else

                                                printf("Client: recv() is in progress...\n");

 

                                    // Increment the number of bytes that have been received so far

                                    bytesReceived += rc;

                        }

 

                        // Just for display!

                        printf("Client: Received money is - ");

                        for(j=0; buffer[j] == '$';j++)

                                    printf("%c",buffer[j]);

                        printf("\n");

            } while (FALSE);

 

            // Do the shutdown() for TCP so receive and send

            // operation will no longer be allowed

            printf("Client: Ready to do the TCP shutdown()...\n");

            if(shutdown(sd, SD_BOTH) == SOCKET_ERROR)

                        printf("Client: shutdown sd socket and the other end failed with error %ld\n", WSAGetLastError());

            else

                        printf("Client: shutdown() on sd socket and the other end looks OK...\n");

 

            // Close down any open socket descriptors

            if(closesocket(sd) == 0)

                        printf("Client: sd socket closed successfully!\n");

            else

                        printf("Client: Failed to close sd socket!\n");

 

            // Terminates the use of the Ws2_32.DLL for all threads (if any)

            printf("Client: Doing the WSACleanup()...\n");

            if( WSACleanup() == SOCKET_ERROR)

                        printf("Client: WSACleanup() failed with error code %ld\n", WSAGetLastError());

            else

                        printf("Client: WSACleanup() confirmed OK!\n");

 

            // Free up results returned from getaddrinfo()

            printf("Client: Deallocate the allocated resources if any...\n");

            if (res != NULL)

                        freeaddrinfo(res);

 

            return 0;

}

 

The following screenshot shows a sample client output run when there is no listening server.

 

------------------------------------------------------------

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv6 client sample program output

 

Testing IPv6 Client and Server Programs

 

Firstly we run the previously created IPv6 server program and make sure the machine used to run the program is IPv6 enabled.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: running the IPv6 server program - it is in listening mode. This program will fail on the IPv4 machine

 

Then we run the client using localhost as the host name/server name and this is a default value hardcoded in the program. The client program can be run from the command prompt.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: running the IPv4/IPv6 client program. The communication has been setup and completed successfully

 

The following screenshot shows the previous server output.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv6 server program sample output which shows a completed communication

 

Using IPv4 address will fail because the server is listening on the IPv6 address. You may want to test this client program using IPv4 server created in other program examples in this tutorial.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv6 client with IPv4 address failed miserabily

 

Then, using the IPv6 for the client should be fine.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv6 client program with IPv6 address as the argument looks fine

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv6 server program sample output which shows a completed communication with the IPv6 client

 

 

 


< IPv4 & IPv6 Server Examples | Internet Protocol Main | Chap 4: Other Supported Protocols >