Running IPv6 Code in Multiple Windows Environments
Introduction
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.
Different Levels of IPv6 Support
As mentioned earlier, Microsoft does not provide support for IPv6 on all Windows platforms. The support in different versions of OS is given here:
- Windows 95/98/Me/NT: no support
- Windows 2000: non-production-quality support with IPv6 Technology Preview
- Windows XP (SP1), Windows Server 2003, and later: Full support
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.
Newly Added Programming Elements
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.
Building a Simple Program
To explain the process, I'll take following very simple program that only has a Winsock function, namely getaddrinfo():
Listing 1: 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); }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)
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
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)
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
Now, matching the ordinal numbers from Listing 2 with those in Listing 3, we can find that our program uses the following functions:
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 |
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.
Page 1 of 2
This article was originally published on July 19, 2004