Share via


Annotated Server Code with Checkv4 Output

Other versions of this page are also available for the following:

Windows Mobile Not SupportedWindows Embedded CE Supported

8/28/2008

The Checkv4 utility can help you find code that is sensitive to using in a version-independent scenario. Checkv4 provides enough information to get you started on changing your code.

You can obtain the Checkv4 utility from the IPv6 Technology Preview, available for download at this Microsoft Web site.

These examples show the Checkv4 output for the server code that you created in How to Convert an Application from IPv4 to IPv4/IPv6, and annotated code that describes the changes that you must make for the application to support both IPv4 and IPv6.

The following commenting conventions are used in the code example.

Commenting convention Description

//checkv4 comment: <comment>

Code included in the checkv4 output

// Begin changes not included in the checkv4 output: <change needed>

<code>

//End changes.

Code to be changed

The following shows the Checkv4.exe output for the server code:

server.cpp(40) : sockaddr_in : use sockaddr_storage instead, or use sockaddr_in6 in addition for IPv6 support

server.cpp(43) : hostent : use addrinfo instead

server.cpp(63) : AF_INET : use AF_INET6 in addition for IPv6 support

server.cpp(75) : AF_INET : use AF_INET6 in addition for IPv6 support

server.cpp(76) : INADDR_ANY : use getaddrinfo with nodename=NULL and AI_PASSIVE instead, or use in6addr_any in addition for IPv6 support

server.cpp(167) : AF_INET : use AF_INET6 in addition for IPv6 support

server.cpp(167) : gethostbyaddr : use getnameinfo instead

server.cpp(176) : in_addr : use in6_addr in addition for IPv6 support

server.cpp(176) : inet_ntoa : use WSAAddressToString or getnameinfo with NI_NUMERICHOST instead

Annotated IPv4-Only Source Code

The following IPv4-only server source code example shows code that is annotated with changes you should make to fully support IPv4 and IPv6:

// Begin changes not included in the checkv4 output: you must also add winsock2.h and ws2tcpip.h to get access to IP version independent Winsock function calls and data structures
#include <winsock.h> 
#include <windows.h>
#include <tchar.h>
#include <strsafe.h.>
//End changes

#define DEFAULT_FAMILY      AF_UNSPEC
#define DEFAULT_SOCKTYPE   SOCK_STREAM
// Begin changes not included in the checkv4 output: also change port numbers to string format 
#define DEFAULT_PORT      1234
//End changes

#define BUFFER_SIZE         23            // length of "WinCE Echo Test Packet"

#define RAS_SIZE 128

#define MIN(a,b) ((a) <= (b)) ? (a) : (b)

void
Print(
   TCHAR *pFormat, 
   ...)
{
   va_list ArgList;
   TCHAR   Buffer[256];

   va_start (ArgList, pFormat);

   (void)StringCchVPrintf(Buffer, 256, pFormat, ArgList);

#ifndef UNDER_CE
   _putts(Buffer);
#else
   OutputDebugString(Buffer);
#endif

   va_end(ArgList);
}

int _tmain (int argc, TCHAR* argv[])
{
// Begin changes not included in the checkv4 output :  SockServ, the socket used in the accept call, must be an array of sockets to be able to accept on more than one IP address
   SOCKET sock, SockServ;
//End changes

   int nSockType = DEFAULT_SOCKTYPE;
// Begin changes not included in the checkv4 output : you must change the type of sPort to char* 
   short sPort = DEFAULT_PORT;
   //End changes

   sockaddr_in saAddr, saRemoteAddr;
//checkv4 comment: server.cpp(40) : sockaddr_in : use sockaddr_storage instead, or use sockaddr_in6 in addition for IPv6 support

   int cbRemoteAddrSize, cbXfer, cbTotalRecvd;
   WSADATA wsaData;
   struct hostent* h;
//checkv4 comment: server.cpp(43) : hostent : use addrinfo instead

   fd_set fdSockSet;
   char pBuf[BUFFER_SIZE];
   char szRemoteAddrString[RAS_SIZE];

   Print(TEXT("** Simple IPv4 Server **"));

// Begin changes not included in the checkv4 output: You must use MAKEWORD(2,2) so that Winsock2.2 is used 
   if(WSAStartup(MAKEWORD(1,1), &wsaData))
   //End changes

   {
      // WSAStartup failed
      return 1;
   }

   sock = INVALID_SOCKET;
   SockServ = INVALID_SOCKET;
   
   //
   // Create socket
   //

// Begin changes not included in the checkv4 output : see the checkv4 comments below.
   SockServ = socket(AF_INET, nSockType, 0);
   //End changes
//checkv4 comment: server.cpp(63) : AF_INET : use AF_INET6 in addition for IPv6 support

// Begin changes not included in the checkv4 output : See comment below (*) that requires the above line to be included as part of a bigger change
   if(SockServ == INVALID_SOCKET)
   {
      Print(TEXT("ERROR: socket() failed with error %d"), WSAGetLastError());
      goto Cleanup;
   }

   //
   // bind the socket
   //

   saAddr.sin_family = AF_INET;
//End changes
//checkv4 comment: server.cpp(75) : AF_INET : use AF_INET6 in addition for IPv6 support

// Begin changes not included in the checkv4 output : See comment below (*) that requires the above line to be included as part of a bigger change
   saAddr.sin_addr.s_addr = INADDR_ANY;
//End changes
//checkv4 comment: server.cpp(76) : INADDR_ANY : use getaddrinfo with nodename=NULL and AI_PASSIVE instead, or use in6addr_any in addition for IPv6 support

// Begin changes not included in the checkv4 output : See comment below (*) that requires the above line to be included as part of a bigger change
   saAddr.sin_port = htons(sPort);

   if(bind(SockServ, (SOCKADDR *)&saAddr, sizeof(saAddr)) == SOCKET_ERROR)
   {
      Print(TEXT("ERROR: bind() failed with error %d"), WSAGetLastError());
      goto Cleanup;
   }

   if(nSockType == SOCK_STREAM)
   {
      // listen
      if(listen(SockServ, 5) == SOCKET_ERROR)
      {
         Print(TEXT("ERROR: listen() failed with error %d"), WSAGetLastError());
         goto Cleanup;
      }
   }
//End changes

// (*) Create a list of serving sockets, one for each address, then bind and listen (if socket type is SOCK_STREAM) on all the addresses

   //
   // Wait for incomming data/connections
   //

   FD_ZERO(&fdSockSet);
// Begin changes not included in the checkv4 output : Set up the file descriptor set to include all of the sockets created and bound to above
   FD_SET(SockServ, &fdSockSet);
//End changes

   Print(TEXT("Waiting for incoming data/connections..."));

   if (select(1, &fdSockSet, 0, 0, NULL) == SOCKET_ERROR)
   {
      Print(TEXT("ERROR: select() failed with error = %d\r\n"), WSAGetLastError());
      goto Cleanup;
   }

// Begin changes not included in the checkv4 output : Because select is called on multiple sockets, loop through all sockets to see which one has the incoming data/connection
   if (FD_ISSET(SockServ, &fdSockSet))   // proceed for connected socket
   {
      FD_CLR(SockServ, &fdSockSet);
      if(nSockType == SOCK_STREAM)
      {
         cbRemoteAddrSize = sizeof(saRemoteAddr);
         sock = accept(SockServ, (SOCKADDR*)&saRemoteAddr, &cbRemoteAddrSize);
         if(sock == INVALID_SOCKET) 
         {
            Print(TEXT("ERROR: accept() failed with error = %d\r\n"), WSAGetLastError());
            goto Cleanup;
         }

         Print(TEXT("Accepted TCP connection from socket 0x%08x\r\n"), sock);
      }
      else
      {
         sock = SockServ;
         Print(TEXT("UDP data available on socket 0x%08x\r\n"), sock);
      }
   }
   else
   {
      Print(TEXT("ERROR: select returned success but FD_ISSET returns false for socket = 0x%08x\r\n"), SockServ);
      goto Cleanup;
   }
//End changes

   //
   // Receive data from a client
   //

   cbTotalRecvd = 0;
   do
   {
      cbRemoteAddrSize = sizeof(saRemoteAddr);
      cbXfer = recvfrom(sock, pBuf + cbTotalRecvd, sizeof(pBuf) - cbTotalRecvd, 0,
         (sockaddr *)&saRemoteAddr, &cbRemoteAddrSize);
      cbTotalRecvd += cbXfer;
   } while(cbXfer > 0 && cbTotalRecvd < sizeof(pBuf));

   if(cbXfer == SOCKET_ERROR)
   {
      Print(TEXT("ERROR: Couldn't receive the data! Error = %d\r\n"), WSAGetLastError());
      goto Cleanup;
   }
   else if(cbXfer == 0)
   {
      Print(TEXT("ERROR: Didn't get all the expected data from the client!\r\n"));
      goto Cleanup;
   }

   if(nSockType == SOCK_STREAM)
   {
      cbRemoteAddrSize = sizeof(saRemoteAddr);
      getpeername(sock, (SOCKADDR *)&saRemoteAddr, &cbRemoteAddrSize);
   }
   
// Begin changes not included in the checkv4 output : See the checkv4 output in the comments below. 
   h = gethostbyaddr((char *)&(saRemoteAddr.sin_addr.s_addr), sizeof(saRemoteAddr.sin_addr.s_addr), AF_INET);
   if(h != NULL)
   {
      memcpy(szRemoteAddrString, h->h_name, MIN(strlen(h->h_name)+1, RAS_SIZE-1));
      szRemoteAddrString[RAS_SIZE-1] = _T('\0');      
   }
   else
   {
      Print(TEXT("Warning: Cannot obtain name for address 0x%08x"), saRemoteAddr.sin_addr.s_addr);
      strcpy(szRemoteAddrString, inet_ntoa(*((in_addr*)&(saRemoteAddr.sin_addr.s_addr))));
   }
//End changes
//checkv4 comment: server.cpp(167) : AF_INET : use AF_INET6 in addition for IPv6 support
//checkv4 comment: server.cpp(167) : gethostbyaddr : use getnameinfo instead
//checkv4 comment: server.cpp(176) : in_addr : use in6_addr in addition for IPv6 support
//checkv4 comment: server.cpp(176) : inet_ntoa : use WSAAddressToString or getnameinfo with NI_NUMERICHOST instead

   Print(TEXT("SUCCESS - Received %d bytes from client %hs\r\n"), cbTotalRecvd, szRemoteAddrString);

   //
   // Echo the data back to the client
   //

   cbXfer = 0;
   cbXfer = sendto(sock, pBuf, cbTotalRecvd, 0, (sockaddr *)&saRemoteAddr, cbRemoteAddrSize);

   if(cbXfer != cbTotalRecvd)
      Print(TEXT("ERROR: Couldn't send the data! error = %d\r\n"), WSAGetLastError());
   else
      Print(TEXT("SUCCESS - Echo'd %d bytes back to the client\r\n"), cbXfer);

Cleanup:
// Begin changes not included in the checkv4 output : Because multiple sockets were created, make sure all sockets are closed
   if (SockServ != INVALID_SOCKET)
      closesocket(SockServ);
//End changes

   if(sock != INVALID_SOCKET)
   {
      shutdown(sock, 2);
      closesocket(sock);
   }

   WSACleanup();

   Print(TEXT("** Simple IPv4 Server, Exiting **"));

   return 0;
}

See Also

Tasks

Changing the Application Source Code to Support IPv6
How to Convert an Application from IPv4 to IPv4/IPv6