Language standard library support for IPv6.
What follows is an attempt to view the support for IPv6 in various
languages.
Java
Java has clean support for IPV6, and makes it easy to go between
IPv4 and IPv6 addresses. Example:
import java.net.*;public class IPTest{ private static void displayClasses(String host){ System.out.print("looking up host" +host+"tt"); try{ InetAddress[] address = InetAddress.getAllByName(host); System.out.print("[Success]:"); for (int i =0; i < address.length;i++){ System.out.println(address[i].getClass().getName()); } }catch(UnknownHostException e){ System.out.println("[Unknown]"); } } public static void main(String[] args) { displayClasses("fe80::218:8bff:fec4:284b"); displayClasses("fe80::218:8bff:fec4:284b/64"); displayClasses("00:18:8B:C4:28:4B"); displayClasses("::10.17.126.126"); displayClasses("10.17.126.126"); displayClasses("vmware.com"); displayClasses("adyoung-laptop"); } }
This code produces the following output
adyoung@adyoung-laptop$ java IPTest looking up hostfe80::218:8bff:fec4:284b [Success]:java.net.Inet6Address looking up hostfe80::218:8bff:fec4:284b/64 [Unknown] looking up host00:18:8B:C4:28:4B [Unknown] looking up host::10.17.126.126 [Success]:java.net.Inet6Address looking up host10.17.126.126 [Success]:java.net.Inet4Address looking up hostvmware.com [Success]:java.net.Inet4Address looking up hostadyoung-laptop [Success]:java.net.Inet4Address
C++
While C++ can always default to C for network support, I wanted to
see what existed in the C++ way of doing things. There is nothing in
the standard library for network support, and nothing pending in TR1.
The third party libary for Asynchronous I/O (asio) does support
IPv6. Boost has not accepted this package yet, but the acceptance
process appears to be underway. This package has a class for ip
address abstraction: asio::ip::address.
#include <iostream> #include <boost/array.hpp> #include <asio.hpp>using asio::ip::address; using namespace std; void displayAddr(char * addr){ cout << "parsing addr " << addr; try{ address::from_string(addr) ; cout << "t[success]" ; }catch(...){ cout << "t[Failed]"; } cout << endl; } int main(int argc, char* argv[]){ displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); return 0; }
This produces the following output:
parsing addr fe80::218:8bff:fec4:284b [success] parsing addr fe80::218:8bff:fec4:284b/64 [Failed] parsing addr 00:18:8B:C4:28:4B [Failed] parsing addr ::10.17.126.126 [success] parsing addr 10.17.126.126 [success] parsing addr vmware.com [Failed] parsing addr adyoung-laptop [Failed]
So the major distinction between this and the Java code is that the
Java code accepts hostnames, this only accepts well formed IP
Addresses.
Python
Python has IPv6 support built in to recent version.
#!/usr/bin/pythonimport socket def displayAddr(addr): try : addr_info = socket.getaddrinfo(addr,""); print "Parsing ", addr , "t[Succeeded]" except socket.gaierror : print "Parsing ", addr, "t[Failed]" def main(): displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); if __name__ == '__main__': main()
This Code produces the following output
adyoung@adyoung-laptop$ ./SockTest.py Parsing fe80::218:8bff:fec4:284b [Succeeded] Parsing fe80::218:8bff:fec4:284b/64 [Failed] Parsing 00:18:8B:C4:28:4B [Failed] Parsing ::10.17.126.126 [Succeeded] Parsing 10.17.126.126 [Succeeded] Parsing vmware.com [Succeeded] Parsing adyoung-laptop [Succeeded]
So, like Java, hostnames are correctly parsed the same as IP
addressed.
PERL
Perl has two APIs that look promising: Socket and Net::IP.
#!/usr/bin/perl use strict; use Socket; sub displayAddr{ my ($addr) = @_; my $host = gethostbyname ($addr); if ($host){ print ("parsed ".$addr."n"); }else{ print ("Unable to parse ".$addr."n"); } } displayAddr("fec0::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); displayAddr("ip6-allhosts"); displayAddr("ip6-localnet");
This produces:
adyoung@adyoung-laptop$ less ip-test.pl adyoung@adyoung-laptop$ ./ip-test.pl IP : fec0:0000:0000:0000:0218:8bff:fe81:f81e Type: RESERVED IP : fe80:0000:0000:0000:0218:8bff:fe81:f81e Type: LINK-LOCAL-UNICAST IP : fe80:0000:0000:0000:0218:8bff:fec4:284b Type: LINK-LOCAL-UNICAST cannot parse fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b cannot parse fe80::218:8bff:fec4:284b/64 cannot parse 00:18:8B:C4:28:4B IP : 0000:0000:0000:0000:0000:0000:0a11:7e7e Type: IPV4COMP IP : 10.17.126.126 Type: PRIVATE cannot parse vmware.com cannot parse adyoung-laptop cannot parse ip6-allhosts cannot parse ip6-localnet
So it handles IPv4 and IPv6., but not host names.
The alternate API, Socket, is older. It doesn ot seem to have the
new Posix function getaddrinfo, so I treid the old gethostbyname:
#!/usr/bin/perl
use strict; use Socket; sub displayAddr{ my ($addr) = @_; my $host = gethostbyname ($addr); if ($host){ print ("parsed ".$addr."n"); }else{ print ("Unable to parse ".$addr."n"); } } displayAddr("fec0::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); displayAddr("ip6-allhosts"); displayAddr("ip6-localnet");
This produced the following output:
Unable to parse fec0::218:8bff:fe81:f81e Unable to parse fe80::218:8bff:fe81:f81e Unable to parse fe80::218:8bff:fec4:284b Unable to parse fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b Unable to parse fe80::218:8bff:fec4:284b/64 Unable to parse 00:18:8B:C4:28:4B Unable to parse ::10.17.126.126 parsed 10.17.126.126 parsed vmware.com parsed adyoung-laptop Unable to parse ip6-allhosts Unable to parse ip6-localnet
It was able to handle IPv4 and domain names, but not IPv6. This was
on a system that had an IPv6 interface, so it was not the problem
shown in the straight C section
C#
This code exercizes the IPAddress and Dns classes.
using System; using System.Net;public class NetTest { public static void Main(string[] args){ DisplayAddr("fec0::218:8bff:fe81:f81e"); DisplayAddr("fe80::218:8bff:fe81:f81e"); DisplayAddr("fe80::218:8bff:fec4:284b"); DisplayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b"); DisplayAddr("fe80::218:8bff:fec4:284b/64"); DisplayAddr("00:18:8B:C4:28:4B"); DisplayAddr("::10.17.126.126"); DisplayAddr("10.17.126.126"); DisplayAddr("vmware.com"); DisplayAddr("adyoung-laptop"); DisplayAddr("ip6-allhosts"); DisplayAddr("ip6-localnet"); } public static void DisplayAddr(string host){ try{ IPAddress addr = IPAddress.Parse(host); Console.WriteLine("addr has address:" + addr.ToString()); }catch(Exception ){ Console.WriteLine("unable to parse :" + host); try{ IPHostEntry hostEntry = Dns.GetHostByName(host); Console.WriteLine("addr has address:" + hostEntry.AddressList[0] .ToString()); }catch(Exception ){ Console.WriteLine("Cannot get host from DNS:",host); } } } }
This code produces the following output.
addr has address:fec0::218:8bff:fe81:f81e addr has address:fe80::218:8bff:fe81:f81e addr has address:fe80::218:8bff:fec4:284b unable to parse :fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b Cannot get host from DNS: addr has address:fe80::218:8bff:fec4:284b unable to parse :00:18:8B:C4:28:4B Cannot get host from DNS: addr has address:::10.17.126.126 addr has address:10.17.126.126 unable to parse :vmware.com addr has address:10.19.249.99 unable to parse :adyoung-laptop addr has address:10.17.124.70 unable to parse :ip6-allhosts Cannot get host from DNS: unable to parse :ip6-localnet Cannot get host from DNS:
C# Handles both IPv4 and IPv6 address equally well. The IPAddress
class does not handle host names. The Dns service does an explicit
DNS lookup, not a call via the NSSwitch functions. This leaves a
hole for hosts declared via YP, /etc/hosts, LDAP, or other naming
services. I ran this under both Mono and Microsoft Visual Studio.
On MSVS, it called out that GetHostByName was deprecated, but the
replacement call, GetHostEntry, had the same behavior.
Straight C
(Posix)
The obvious method does not work:
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h>void displayAddr(char *addr){ struct hostent * hostent; hostent =gethostbyname(addr); if (hostent){ printf( "Parsing %s t[Succeeded]n", addr); }else{ printf( "Parsing %s t[Failed]n", addr); } } int main(){ displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); return 0; }
This code produces the following output:
adyoung@adyoung-laptop$ ./socktest Parsing fe80::218:8bff:fec4:284b [Failed] Parsing fe80::218:8bff:fec4:284b/64 [Failed] Parsing 00:18:8B:C4:28:4B [Failed] Parsing ::10.17.126.126 [Failed] Parsing 10.17.126.126 [Succeeded] Parsing vmware.com [Succeeded] Parsing adyoung-laptop [Succeeded]
Thus it does not deal with IPv6 addresses correctly. This seems to
be at odds with the man page which states:
The gethostbyname() function returns a structure of type hostent for the given host name. Here name is either a host name, or an IPv4 address in standard dot notation, or an IPv6 address in colon (and possibly dot) notation.
The next attempt is to using the call specified in the porting
doc:getaddrinfo
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h>void displayAddr(char *addr){ struct addrinfo * addrinfo; int rc = getaddrinfo(addr,NULL,NULL,&addrinfo); if (0 == rc ){ printf( "Parsing %s t[Succeeded]n", addr); freeaddrinfo(addrinfo); }else{ printf( "Parsing %s t[Failed]n", addr); } } int main(){ displayAddr("fec0::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); return 0; }
This Code Worked differently Depending on whether the machine had an
IPv6 interface configured. Without and IPv6 Interface:
adyoung@adyoung-laptop$ ./getaddrinfo-test Parsing fec0::218:8bff:fe81:f81e [Failed] Parsing fe80::218:8bff:fe81:f81e [Failed] Parsing fe80::218:8bff:fec4:284b [Failed] Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b [Failed] Parsing fe80::218:8bff:fec4:284b/64 [Failed] Parsing 00:18:8B:C4:28:4B [Failed] Parsing ::10.17.126.126 [Failed] Parsing 10.17.126.126 [Succeeded] Parsing vmware.com [Succeeded] Parsing adyoung-laptop [Succeeded]
With an IPv6 interface.
-bash-3.00$ ./getaddrinfo-test Parsing fec0::218:8bff:fe81:f81e [Succeeded] Parsing fe80::218:8bff:fe81:f81e [Succeeded] Parsing fe80::218:8bff:fec4:284b [Succeeded] Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b [Failed] Parsing fe80::218:8bff:fec4:284b/64 [Failed] Parsing 00:18:8B:C4:28:4B [Failed] Parsing ::10.17.126.126 [Succeeded] Parsing 10.17.126.126 [Succeeded] Parsing vmware.com [Succeeded] Parsing adyoung-laptop [Failed]
The getaddrinfo function call gives us a way to determine the
correct family to use to connect to the host. If we add this to the
displayAddr function:
switch( addrinfo->ai_family){ case AF_INET6: printf( "socket family = AF_INET6n"); break; case AF_INET: printf( "socket family = AF_INETn"); break; }
and request the resolution of a few more hosts:
displayAddr("ip6-allhosts"); displayAddr("ip6-localnet");
We get:
Parsing fec0::218:8bff:fe81:f81e [Succeeded]socket family = AF_INET6 Parsing fe80::218:8bff:fe81:f81e [Succeeded]socket family = AF_INET6 Parsing fe80::218:8bff:fec4:284b [Succeeded]socket family = AF_INET6 Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b [Failed] Name or service not known Parsing fe80::218:8bff:fec4:284b/64 [Failed] Name or service not known Parsing 00:18:8B:C4:28:4B [Failed] Name or service not known Parsing ::10.17.126.126 [Succeeded]socket family = AF_INET6 Parsing 10.17.126.126 [Succeeded]socket family = AF_INET Parsing vmware.com [Succeeded]socket family = AF_INET Parsing adyoung-laptop [Succeeded]socket family = AF_INET Parsing ip6-allhosts [Succeeded]socket family = AF_INET6 Parsing ip6-localnet [Succeeded]socket family = AF_INET6
So we can take the approach where the applications store hosts in a
free string format, presumably by host names, but perhaps by IPv4 or
IPv6 addresses, and we will use gethostinfo to decide how to connect.
For example, (without error handling)
void connectTo(char * host){ struct addrinfo * addrinfo; struct protoent * protoent; protoent = getprotobyname("tcp"); int rc = getaddrinfo(host,NULL,NULL,&addrinfo); int sockfd = socket( addrinfo->ai_family,SOCK_STREAM,protoent->p_proto); }
Visual C++
The code for VC++ is very similar to posix, with slightly
different build requirments. Note the addition of the winsock
initialization code.
// nettest.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <stdio.h> void displayAddr(char *addr){ struct addrinfo * addrinfo; int rc = getaddrinfo(addr,NULL,NULL,&addrinfo); if (0 == rc ){ printf( "Parsing %s t[Succeeded]", addr); switch( addrinfo->ai_family){ case AF_INET6: printf( "socket family = AF_INET6n"); break; case AF_INET: printf( "socket family = AF_INETn"); break; } freeaddrinfo(addrinfo); }else{ printf( "Parsing %s t[Failed]n", addr); printf("%sn",gai_strerror(rc)); } } void connectTo(char * host){ struct addrinfo * addrinfo; struct protoent * protoent; protoent = getprotobyname("tcp"); int rc = getaddrinfo(host,NULL,NULL,&addrinfo); int sockfd = socket( addrinfo->ai_family,SOCK_STREAM,protoent->p_proto); } WSAData wsaData; int _tmain(int argc, _TCHAR* argv[]) { int iResult; // Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %dn", iResult); return 1; } displayAddr("fec0::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fe81:f81e"); displayAddr("fe80::218:8bff:fec4:284b"); displayAddr("fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b"); displayAddr("fe80::218:8bff:fec4:284b/64"); displayAddr("00:18:8B:C4:28:4B"); displayAddr("::10.17.126.126"); displayAddr("10.17.126.126"); displayAddr("vmware.com"); displayAddr("adyoung-laptop"); displayAddr("ip6-allhosts"); displayAddr("ip6-localnet"); return 0; }
The file stdafx.h contains the includes
#include <winsock2.h> #include <ws2tcpip.h>
This produced the output:
Parsing fec0::218:8bff:fe81:f81e [Succeeded]socket family = AF_INET6 Parsing fe80::218:8bff:fe81:f81e [Succeeded]socket family = AF_INET6 Parsing fe80::218:8bff:fec4:284b [Succeeded]socket family = AF_INET6 Parsing fe80:0:0:0:0:0:0:0:0:0:0:0:218:8bff:fec4:284b [Failed] N Parsing fe80::218:8bff:fec4:284b/64 [Failed] N Parsing 00:18:8B:C4:28:4B [Failed] N Parsing ::10.17.126.126 [Succeeded]socket family = AF_INET6 Parsing 10.17.126.126 [Succeeded]socket family = AF_INET Parsing vmware.com [Succeeded]socket family = AF_INET Parsing adyoung-laptop [Failed] N Parsing ip6-allhosts [Succeeded]socket family = AF_INET6 Parsing ip6-localnet [Succeeded]socket family = AF_INET6
Again, this only worked if there was an IPv6 interface configured on
the system. Also, the default localhost, allhosts, and localnet
names from Linux were not supported on Windows. These had been
declared in /etc/hosts. Once these entries were added to
c:\windows\system32\drivers\etc\hosts it worked correctly. The error
reporting function does not work, but it does resolve and link. It
merely prints out the letter ‘N’.