Monday, August 20, 2012

IPv6 in Perl on Windows

The state of IPv6 support in Perl has long been lamented but there are those trying to change this including Paul Evans who's updating many of the networking modules - including the Socket core module - to support IPv6. It's a shame the C compiler shipping with Strawberry Perl still doesn't accommodate IPv6 on Windows.

I've been using Strawberry Perl after switching from ActiveState since version 5.10. I found myself needing more and more XS modules and the ActiveState PPM process was always lagging behind. I had a copy of MinGW gcc - the free Windows port of the 'gcc' compiler - and with 'dmake', I could build my own modules directly from CPAN. However, I had some issues and when I learned about Strawberry Perl including the gcc compiler in the distribution, I switched. I've used Strawberry Perl on Windows XP 32-bit and now Windows 7 64-bit.

I've also recently been doing a lot of IPv6 work and using Perl as a quick development platform to test the IPv6 protocol proved easy with the Net::Frame modules detailed in an early series of blog posts. However, when looking to create sockets with Perl, I found errors, most notably:

Socket::inet_ntop not implemented on this architecture

The lack of this function prevented the display of IPv6 addresses and it's missing complement - inet_pton - prevents the resolution of IPv6. These functions are certainly present in Windows 7.

The issue lies in the header and library files distributed with MinGW gcc compiler in the Strawberry Perl release. They simply don't identify the functions in the headers, nor provide linkage to them in the libraries. Fortunately, tools shipped with the MinGW gcc compiler in Strawberry Perl can be used to remedy the issue and get full IPv6 functionality. Here's how.

First off, we need to update the header file with the function prototypes. I did this by creating a new header file called 'ws2tcpip-win.h':


#ifndef WS2TCPIP_WIN_H
#define WS2TCPIP_WIN_H

typedef USHORT ADDRESS_FAMILY;

#if (NTDDI_VERSION >= NTDDI_VISTA)
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT, PCSTR, PVOID);
WINSOCK_API_LINKAGE INT WSAAPI InetPtonW(INT, PCWSTR, PVOID);

WINSOCK_API_LINKAGE PCSTR WSAAPI inet_ntop(INT, PVOID, PSTR, size_t);
WINSOCK_API_LINKAGE PCWSTR WSAAPI InetNtopW(INT, PVOID, PWSTR, size_t);

#define InetPtonA       inet_pton
#define InetNtopA       inet_ntop

#ifdef UNICODE
#define InetPton        InetPtonW
#define InetNtop        InetNtopW
#else
#define InetPton        InetPtonA
#define InetNtop        InetNtopA
#endif

#if INCL_WINSOCK_API_TYPEDEFS
typedef INT (WSAAPI * LPFN_INET_PTONA)(INT Family, PCSTR pszAddrString, PVOID pAddrBuf);
typedef INT (WSAAPI * LPFN_INET_PTONW)(INT Family, PCWSTR pszAddrString, PVOID pAddrBuf);

typedef PCSTR (WSAAPI * LPFN_INET_NTOPA)(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize);
typedef PCWSTR (WSAAPI * LPFN_INET_NTOPW)(INT Family, PVOID pAddr, PWSTR pStringBuf, size_t StringBufSize);

#ifdef UNICODE
#define LPFN_INET_PTON          LPFN_INET_PTONW
#define LPFN_INET_NTOP          LPFN_INET_NTOPW
#else
#define LPFN_INET_PTON          LPFN_INET_PTONA
#define LPFN_INET_NTOP          LPFN_INET_NTOPA
#endif

#endif  //  TYPEDEFS
#endif  //  (NTDDI_VERSION >= NTDDI_VISTA)

#endif

Next, copy that file to the appropriate location:

copy ws2tcpip-win.h c:\strawberry\c\x86_64-w64-mingw32\include

and update the existing 'ws2tcpip.h' file in that location with the following:

#include <ws2tcpip-win.h>

Now we need to create the library. This is done by first exporting the symbols from the appropriate DLL file - in this case, ws2_32.dll - and then creating the link library. The version of the MinGW gcc compiler with Strawberry Perl has 'pexports' included, so simply create the DEF file:

pexports c:\windows\system32\ws2_32.dll > ws2_32.def

Now create the library:

dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libws2_32-new.a --input-def WS2_32.def

Then put the new library in the appropriate location:

copy libws2_32-new.a c:\strawberry\c\x86_64-w64-mingw32\lib

Now we're ready to go. Get the latest release of Socket from CPAN, unzip and extract the tar file. Change into the directory and start the build process:

perl Makefile.PL

Now, before doing the 'dmake', edit the generated Makefile. Find the "DEFINE =" and "LDLOADLIBS =" lines and update them as per below:

DEFINE = -DHAS_GETADDRINFO -DHAS_GETNAMEINFO -DHAS_SOCKADDR_IN6 -DHAS_IP_MREQ -DHAS_IP_MREQ_SOURCE -DHAS_IPV6_MREQ -DHAS_INETNTOP -DHAS_INETPTON -DAF_INET6

LDLOADLIBS = -lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 -luuid -lws2_32 -lmpr -lwinmm -lversion -lodbc32 -lodbccp32 -lcomctl32 -lws2_32-new

Finally, 'dmake', 'dmake test' and 'dmake install' and you're good to go with IPv6!

Friday, August 17, 2012

The Adventures of Rich and Vince




Characters courtesy of SP Studio
 

Copyright © VinsWorld. All Rights Reserved.