http://www.developer.com/tech/article.php/3382721/Running-IPv6-Code-in-Multiple-Windows-Environments.htm
IPv6, or "Internet protocol Version 6," has been around for a long time now. It took a long time to reach the level of acceptance it has reached today, but its growth in terms of acceptance and popularity has been phenomenal. Many vendors are already supporting IPv6 in their products. Microsoft has been a little late in entering this field. There is no IPv6 support in older versions of Windows, such as Windows NT 4 or Windows 98. There is some support for Windows 2000, but that is just for experiments and is not a production quality implementation. But, Microsoft has started providing full support for IPv6 in its operating systems from Windows XP SP1 and Windows Server 2003 onwards. This different level of support has created problems for vendors who support their IPv6 enabled products on multiple Windows environments. To make your code IPv6 enabled, you need to use new function calls and data structures introduced in Window Sockets v2 API. But, implementation of these new programming elements is available only in Windows XP SP1 or Windows Server 2003. Then, how can you run the same code on Windows 2000 platform? This article addresses the above problem. It tells you how to use new IPv6 function calls in your code and run the same code seamlessly on Windows 2000, Windows XP, and Windows Server 2003. Microsoft handles this backward compatibility of newly introduced function calls in a very cryptic way that a normal programmer doesn't understand easily. In this article, I will show you what happens behind the scenes that makes the new function calls work even on platforms where these are not available. As mentioned earlier, Microsoft does not provide support for IPv6 on all Windows platforms. The support in different versions of OS is given here: Even in the supported platforms, IPv6 is not installed by default. If required, administrators need to install the IPv6 protocol manually on the system. On Windows 2000 (with the IPv6 technology preview installed), the IPv6 routines are available in wship6.dll whereas on Windows XP (SP1) and Windows Server 2003 all the IPv6 routines are available in ws2_32.dll along with legacy winsock routines. There are many implementations of sockets API available, but the two extensively used implementations across the industry are Windows Sockets and BSD sockets. The two implementations have very similar API and are compatible with each other to a great extent. But, we will not delve into that area and will concentrate only on Microsoft's implementation or Windows Sockets. Also, I'll assume that you are using Visual C++ 7.0 or later to build your code for all the samples here. The header and library files of Visual C++ 6 environment do not contain the new function calls. So, you can not compile the code containing new function calls in a default VC6 environment. Although it is possible to compile the code in VC6 by including the newer header files, I'll not discuss that here. Totally, there are nine new function calls and five new data structures that have been added to Windows Sockets for IPv6. You can see the list of these in the MSDN library. The purpose of this article is not to show you how to code for IPv6, but to show how the code written for IPv6 can also run on non-supported systems. So, I'll take an example of an often-used function call (among the newly added calls) and explain the solution with that. This function call is getaddrinfo(). The official documentation describes this call as this: "The getaddrinfo function provides protocol-independent translation from host name to address." This call should be used in place of the gethostbyname() function call when writing IPv6 code. Apart from retrieving host information corresponding to a host name, the new getaddrinfo() call also performs the processing work of many functions. The lines of code necessary to perform the usual work of creating, opening, and then binding a socket can be significantly reduced by using this new call. To explain the process, I'll take following very simple program that only has a Winsock function, namely getaddrinfo(): Listing 1: Simple Program The above program translates the given host name and service name to a protocol-independent network address and puts that information into an ADDRINFO structure. This structure then can be used to create a socket and perform operations such as connect. But, I will not show that here to keep the program simple. Now, let us compile and link this program. To get it compiled, we included the header file ws2tcpip.h, which has the declaration of the getaddrinfo() call. To get it linked, we need to add the additional library ws2_32.lib to the linker list to resolve the getaddrinfo symbol. Now, let us perform a dumpbin/imports on resulting executable. It shows the following output given in Listing 2 below. For simplicity sake I'm showing imports for WS2_32.dll only, but actual dumpbin ouput also has quite a few imports from KERNEL32.dll. Listing 2: Output of dumpbin/imports (shortened) You will notice that there are total 10 imports from WS2_32.dll. Ten imports for just one function call—getaddrinfo()! How's that possible? To get the answer, let's first see what these functions are. As these are imported by ordinal numbers, we need to see the dumpbin exports of WS2_32.dll. They are shown in Listing 3. Again, for simplicity, I'm not showing calls with the WSA prefix, except just one call, WSAGetLastError, that is being used in our program. Listing 3: Output of dumpbin/exports of WS2_32.dll (shortened) Now, matching the ordinal numbers from Listing 2 with those in Listing 3, we can find that our program uses the following functions: So, although we used a new function call that is introduced for IPv6, the linker is mapping that call to the legacy Winsock function calls that also were available for IPv4. How does this happen? We'll see that in later section. For now, we will find out the way to prevent it. In the next section, let's see how we target an IPv6 environment explicitly so that new IPv6 calls don't get mapped to legacy IPv4 calls. I'll take the same old program shown in Listing 1 and demonstrate how you can target that program for the IPv6 environment explicitly. All that you need to do is to add a preprocessor directive that instructs the compiler to build the program for Windows XP or later system. The directive is _WIN32_WINNT and its value for different operating systems is given in the following table: The values for _WIN32_WINNT are the version numbers that are used in Platform SDK header files. Defining this directive explicitly in the program enables the SDK APIs added for that version of OS. For example, defining this directive as 0x0501 will enable Windows XP-specific functions. Because IPv6 calls are supported from Windows XP (SP1) onwards, you can define _WIN32_WINNT>=0x0501 to target a program for IPv6 environment. You can do it in two ways: Listing 4: Program to target IPv6 environment Now, let us also perform dumpbin/imports on the resulting executable of this program. It shows the output given in Listing 5. Again, for simplicity's sake, I'm showing imports for WS2_32.dll only, but actual dumpbin ouput also has quite a few imports from KERNEL32.dll. Listing 5: Output of dumpbin/imports for IPv6 program (shortened) You'll notice that this time the executable has only one import from WS2_32.dll; that is for the function we actually called in the program. The output of dumpbin/imports in Listing 5 reveals one important concept. Microsoft has written header files for the Winsock2 API in such a way that the compiler can choose the appropriate functions to call based on a targeted environment. For the program in Listing 1, we didn't specify any particular environment to target. So, the compiler generated code that can run on all the environments, even on the environments where IPv6 is not supported. But for the program in Listing 4, we explicitly specified a preprocessor directive to target Windows XP. In this case, the compiler generated code with new IPv6 calls, which can run only on those environments that have IPv6 support. That explains how Microsoft maintains backward compatibility for the code written with newly added IPv6 calls. Now, if you run the executable from the program in Listing 4 on a default installation of Windows XP (SP1), it will run successfully, but the getaddrinfo() call will fail. The reason is that IPv6 protocol is not installed by default. You can install it by running the simple command "ipv6 install" on a command prompt. In Windows Server 2003 also, IPv6 doesn't come in a default installation. You can install it by using the Properties dialog box of Local Area Connection under Network Connections. Once you install the protocol, the program will run successfully and give the expected results. Now, you know exactly what happens that enables the backward compatibility. But, how does it happen? In this section, we'll explore the actual mechanism that is making this happen. The getaddrinfo() function is declared in ws2tcpip.h. At the bottom of this header file, you will find the following code: So, the call to getaddrinfo() goes to WspiapiGetAddrInfo(). The implementation of this function is provided in this header file only. Do not be surprised to see the implementation of the function in a header file. Microsoft had to do it to provide the backward compatibility. At run time, the calls to getaddrinfo() actually call the implementation of WspiapiGetAddrInfo() provided in this header file. Internally, WspiapiGetAddrInfo(), with the help of another function, WspiapiLoad(), calls the appropriate implementation of getaddrinfo() as per the current environment. This choice is made in the following steps: Exactly the same procedure that is followed for calling getaddrinfo() is also followed for the other two IPv6 calls, getnameinfo() and freeaddrinfo(). The ten function imports that you saw in dumpbin/imports output in Listing 2 was because of the implementations of these functions provided in the wspiapi.h file. Note that all this procedure is followed only when you do not define the _WIN32_WINNT directive or its value is less than or equal to 0x0500. So, the executable of the program in Listing 1 will go through all this procedure. But, if the _WIN32_WINNT macro is defined and its value is more than 0x0500, the compiler maps the function call statically and the header file wspiapi.h is not included. The support for IPv6 is of varied levels in different versions of Windows operating systems. But, you still can write code with new IPv6 functions and run that code on earlier versions of Windows operating systems that do not have IPv6 support. Internally, this is done by dynamically finding the implementation of new functions at run time. If the implementation of new functions is not found, the calls to them are mapped to older IPv4 functions. If you do not want this dynamic searching, you can define a preprocessor directive to target the IPv6 environment explicitly. The good thing about this is that everything happens behind the scenes and programmers do not need to worry about backward compatibility. Sanjay Narang is a software analyst at Hewlett-Packard, India. He is certified as a MCSD.NET and SCJP. He is a Post Graduate from the Indian Institute of Information Technology, Bangalore. Sanjay has been involved in designing and developing Micosoft-based solutions for more than five years in various domains: CRM, eGovernance, and Instant Messaging. He can be contacted at sanjai_narang@yahoo.com.
Running IPv6 Code in Multiple Windows Environments
July 19, 2004
Introduction
Different Levels of IPv6 Support
Newly Added Programming Elements
Building a Simple Program
#include <winsock2.h>
#include <ws2tcpip.h>
void main()
{
char* serverName = "server.domain.com";
char* serviceName = "service_name";
ADDRINFO *AI;
int retVal = getaddrinfo(serverName, serviceName, NULL, &AI);
}
Microsoft (R) COFF/PE Dumper Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file IPv6Article.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
WS2_32.dll
42D348 Import Address Table
42D194 Import Name Table
0 time date stamp
0 Index of first forwarder reference
Ordinal 15
Ordinal 56
Ordinal 51
Ordinal 52
Ordinal 111
Ordinal 11
Ordinal 9
Ordinal 55
Ordinal 8
Ordinal 12
Microsoft (R) COFF/PE Dumper Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file c:\windows\system32\ws2_32.dll
File Type: DLL
Section contains the following exports for WS2_32.dll
00000000 characteristics
3B7DE11B time date stamp Sat Aug 18 08:59:31 2001
0.00 version
1 ordinal base
500 number of functions
114 number of names
ordinal hint RVA name
111 1A 00001740 WSAGetLastError
1 51 0000868D accept
2 52 00003ECE bind
3 53 00001A6D closesocket
4 54 00003E5D connect
94 55 00003A2C freeaddrinfo
95 56 000033DF getaddrinfo
51 57 0000D755 gethostbyaddr
52 58 00002BBF gethostbyname
57 59 000032CA gethostname
96 5A 0000C076 getnameinfo
5 5B 0000F628 getpeername
53 5C 0000D24E getprotobyname
54 5D 0000D1A2 getprotobynumber
55 5E 0000D969 getservbyname
56 5F 0000D850 getservbyport
6 60 0000157E getsockname
7 61 00004122 getsockopt
8 62 000012A7 htonl
9 63 00001746 htons
11 64 000012F8 inet_addr
12 65 0000401C inet_ntoa
10 66 0000155A ioctlsocket
13 67 00005DE2 listen
14 68 000012A7 ntohl
15 69 00001746 ntohs
16 6A 00005690 recv
17 6B 00001444 recvfrom
18 6C 00001890 select
19 6D 00001AF4 send
20 6E 00001ED3 sendto
21 6F 00003F8D setsockopt
22 70 00008629 shutdown
23 71 00003C22 socket
Function
Ordinal Number ntohs
15 getservbyport
56 gethostbyaddr
51 bind
52 WSAGetLastError
111 inet_addr
11 htons
9 getservbyname
55 htonl
8 inet_ntoa
12 Building a Program for the IPv6 Environment
Operating System
Value of _WIN32_WINNT Windows 2000
0x0500 Windows XP
0x0501 Windows Server 2003
0x0502
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
void main()
{
char* serverName = "server.domain.com";
char* serviceName = "service_name";
ADDRINFO *AI;
int retVal = getaddrinfo(serverName, serviceName, NULL, &AI);
}
Microsoft (R) COFF/PE Dumper Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file IPv6Article.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
WS2_32.dll
42B314 Import Address Table
42B190 Import Name Table
0 time date stamp
0 Index of first forwarder reference
58 getaddrinfo
Comparing the Two Programs
Preparing the Environment for IPv6
Behind the Scenes
//
// Unless the build environment is explicitly targeting only
// platforms that include built-in getaddrinfo() support, include
// the backwards-compatibility version of the relevant APIs.
//
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT <= 0x0500)
#include <wspiapi.h>
#endif
In the file wspiapi.h there is following preprocessor instruction:
#define getaddrinfo WspiapiGetAddrInfo
Conclusion
References
About the Author