Annotated Server Code with Checkv4 Output (Windows Embedded CE 6.0)
1/6/2010
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