Recently in programming Category

I've blogged before about the shoddy support for IPv6 in Perl. Last week, Perl 5.14 was released with improved IPv6 support in the core distribution:

Improved IPv6 support The Socket module provides new affordances for IPv6, including implementations of the Socket::getaddrinfo() and Socket::getnameinfo() functions, along with related constants and a handful of new functions. See Socket.

This change brings the standard getaddrinfo()/getnameinfo() socket calls into the core library. Prior to this, developers had to use a separate module, socket6, to get IPv6-capable socket calls. This change is long overdue. The getaddrinfo() and getnameinfo() calls were first defined in RFC 2113, which published in 1997!

In 2011, I can't imagine why anyone would want to write new code in Perl, but I'm glad that the core library finally has IPv6 support. There's still a lot of legacy Perl code out there, and much of it will have to get IPv6 support. This should make that process much easier.

I've recently been handed an assignment that requires me to write some Perl code. I'm not a fan of Perl anymore. I haven't used it routinely in almost seven years. It certainly filled a very useful niche in the '80s and early '90s, but by 2008, I think it's been superceeded by Python or Ruby. Frankly, I think it's an anachronism. But this isn't a blog about programming.

I've griped about the shoddy IPv6 support in Perl before. This post is in the same vein.

This code needs to use Perl-LDAP. Natually, I checked if that library supports IPv6 (remember, kids, in Perl, you have to check if every single library supports it!). Fortunately, it got support a few months ago, in version 0.35. All's well, right? Not so fast, kiddo. The changelog says it "add option to support IPv6". What does "option" mean?

Looking at the docs, I found this gem:

inet6 => N

Try to connect to the server using IPv6 if HOST resolves to an IPv6 target address. If it resolves to an IPv4 address, the connection is tried using IPv4, the same way as if this option was not given.

Please note that IPv6 support is considered experimental in IO::Socket::SSL, which is used of SSL/TLS support, and there are a few issues to take care of. See "IPv6" in IO::Socket::SSL for details.

So let me get this straightish. To get IPv6 support in my LDAP apps, I have to pass a special "no, really, use IPv6" flag to every LDAP object I create? What happens if the hostname resolves to both an IPv6 and an IPv4 address? And I better just hope that I'm not using SSL. This is just charming. But it gets better. If you follow the link to IO::Socket::SSL, you find this:

Currently, there is no support for using IPv4 and IPv6 simultaneously in a single program, but it is planned for a future release.

Gah! You can't possibly be serious. Java has supported SSL-over-IPv6 and mixed v6-v4 apps for 5 years! Hell, it's supported it for so long that the first version to get support is now EOL. Python has had support for the same amount of time.

Folks, this is not the way to design a maintainable, supportable, enterprise language. How people continue to use Perl in production totally escapes me. And this does not bode well for IPv6-enabling the large mountain of legacy Perl scripts that hold this University's IT systems together.

Severity: Normal

Today's bug - GMime doesn't parse URLs with literal IPv6 addresses. This has been reported to them as bug 515088.

GMime is a popular library for handling MIME. Penn State's WebMail system uses it. It has a very useful module that converts plaintext email containing URLs into HTML. WebMail uses this capability. Unfortunately, it doesn't support URLs with IPv6 addresses, such as http://[2610:8:6800:1::7]/. This is a valid URL, per RFC 2732.

From what I hear, the GMime developers are very responsive to bug reports. I look forward to this being fixed. It'll be one small step to making WebMail IPv6-capable. :)
I've been looking into what it would take to get several of our applications IPv6-enabled. In some cases, it's trivial. In others, it's going to be hell. This post is about how several programming languages have added IPv6 support to their standard libraries. Well, it's not really a post. It's more of a rant. Because I'm angry. I'm angry that two of the most popular languages appear to have put no forethought into future-proofing their networking libraries.

PHP and Perl are excellent examples of how not to add IPv6 support to a library. The status of IPv6 in both systems is an utter mess that will take years to fix.

We have many apps written in PHP and Perl, which we will eventually need to IPv6-enable. Given the abysmal state of IPv6 in these languages, I'm not optimistic about success. Critically, it not possible to write applications in PHP or Perl that use a single API and will work on IPv4-only, IPv6-only and IPv4/IPv6 dual-stacked hosts. The inability to do this is dooming.

Non-programmers can skip to the last paragraph. What follows is techy.

Let's look at PHP. It got IPv6 support in version 5, but its gethostbyname() function still doesn't support IPv6. Of course, the documentation doesn't mention this fact. Instead, users should use dns_get_record(). (As an aside, why does dns_get_record() still support A6 records, which were deprecated years ago?). Alternatively, developers may use PEAR's Net_DNS class to resolve names. It appears as if there are cases where Net_DNS_Resolver::query() can fail to use IPv6 to connect to a DNS server, however (if using UDP and the "enhanced sockets library").

Then there are the IP address utility libraries: Net_IPv4 and Net_IPv6. First question: Why are there two of them? Why is there not a single network address library that can handle both formats? But it gets worse: The two libraries have different APIs. There is considerable functional overlap between them -- there are functions to apply a netmask to an address, parse CIDR-formatted addresses, etc -- but the function prototypes look nothing alike. For example, some of Net_IPv4::parseAddress()'s abilities are duplicated in Net_IPv6::getNetmask() and Net_IPv6::removeNetmaskSpec(). Both Net_IPv4 and Net_IPv6 have functions to verify if a string is a valid address, but they have different names: Net_IPv4::validateIP() vs Net_IPv6::checkIPv6(). There are other examples of this sort of mismatch.

I'll only mention briefly that there are two other modules for validating IP(v4) addresses: Net_CheckIP and Net_CheckIP2. Both of them only handle IPv4 and have different function names than the equivalent functions in  Net_IPv4 and Net_IPv6. This only serves to increase programmer confusion and encourage the development of non-IPv6-capable code.

And that's just the low-level code. There are still the higher-level libraries for dealing with IMAP, SMTP, HTTP, etc. I haven't begun an audit of PEAR's Networking library to check for IPv4-only code. I'm too afraid.

Perl isn't any better. For reasons that escape me, Perl decided to fork its network libraries into IPv4-only and IPv6 halves. Specifically, there are Socket (and it's object-oriented cousin, IO::Socket::INET) and Socket6 (and IO::Socket::INET6). Of course this means that developers have to change their code to get IPv6 support. Almost no OSes bundle Socket6 or IO::Socket::INET6, so developers are loathe to make such changes. (Kudos to Mac OS X 10.5 and Ubuntu 7.10 for supporting them).

Because the modules are separate, you have to write a bunch of ugly code. When your code starts, you check if you have one of the IPv6 socket modules installed. If so, set a flag. Then every single time you need to talk to the network, you check this flag and branch, either to Socket6 or Socket. This is a) ugly b) inefficient c) unlikely to be adopted by virtually all programmers d) all of the above. Check the source to the Net::DNS module for an example. I am not looking forward to submitting patches that do this. But someone is going to have to.

I contrast this to Java, Python and C which have sane, clean, standardized APIs that support both IPv4 and IPv6. In Java, it's unlikely that developers will have to change code. In Python, the changes are usually two or three lines. In C, it's slightly more lines than that, but generally not much more. I'll elaborate more on these languages in a later post. Readers at Penn State may want to look at my IPv6 programming notes in the ITS Wiki (which is IPv6-enabled).

When a user posts a comment to a MovableType 4 blog, MT4 logs the user's IP address. However, the relevant column in MT's database is only big enough to store an IPv4 address. Here's the relevant code from lib/MT/Comment.pm:


__PACKAGE__->install_properties({
column_defs => {
...
'ip' => 'string(16)',
...
},

PSU is using MovableType 4 for its blogging pilot, so I suppose I should submit a patch to fix this.

As an aside, it's disappointing that MovableType is storing the address as a string. I suppose this was done for portability reasons, but it means that MT can't take advantage of databases with intelligent IP address datatypes, like PostgreSQL. For almost a decade, PostgreSQL has had the inet and cidr datatypes, which are specifically designed for storing IP addresses. These datatypes have supported IPv6 since release 7.4 in late 2003.

It looks like the next version of Python will include an IPv6-safe convenience function to open a connection to a server: socket.create_connection(). You can see the gory details in the svn commit message.

This is A Good Thing. It wraps a rather verbose bit of code:


msg = "getaddrinfo returns an empty list"
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    sock = None
    try:
        sock = socket(af, socktype, proto)
        if timeout is not None:
            sock.settimeout(timeout)
        sock.connect(sa)
        return sock

    except error, msg:
        if sock is not None:
            sock.close()

raise error, msg


This code requests a list of all the addresses (both IPv4 and IPv6) for host using the socket.getaddrinfo() function. It then loops over that list, trying each address until it finds one that works. If none work, it raises an exception.

Python's socket.getaddrinfo() is quite handy. It's a Python wrapper around the C getaddrinfo(3) function, which is defined in RFC 3493: Basic Socket Interface Extensions for IPv6. I'll talk more about it in a future blog entry on how to write IPv6-clean programs (and make sure they still work with IPv4).

Here's a random little tip: To check if your platform's Python interpreter was built with IPv6 support, run this program:


$ python
Python 2.4.4 (#1, Jan 10 2007, 01:25:01) [C] on sunos5
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> socket.has_ipv6
True
>>> ^D

UMich code and IPv6

| | Comments (0) | TrackBacks (0)

The University of Michigan has developed a lot of open-source software. For example, PSU makes heavy use of their CoSign and radmind products. Their graphical SFTP client, Fugu is also very popular. Sadly, none of these are IPv6-clean.

As I mentioned previously, I've got patches to make CoSign IPv6-clean. This afternoon I looked at the radmind and Fugu sources, and it looks like they share some low-level networking libraries with CoSign. I suspect I'll be able to easily adapt my CoSign patches for them.

I setup the VMs for my CoSign testing this afternoon. I hope to actually get to testing things later this week.

Perhaps I'll make a blog entry about converting legacy C code to use the new IPv6-capable APIs. It's generally a straightforward conversion.

WebAccess and IPv6

| | Comments (0) | TrackBacks (0)

If you are IPv6-enabling a website protected by WebAccess, you might need to make a change to your web server's configuration. Specifically, you might need to set CosignCheckIP never in your Apache config. Otherwise, connections over IPv6 won't be authenticated. You can read more about why this happens in the ITS wiki.

Penn State's WebAccess product is based on CoSign, an open-source web single-sign-on system. The CoSign source code is not IPv6-clean, so even if ASET were to assign IPv6 addresses to the WebAccess servers, we'd still have problems. I'm working on a patch to CoSign to make it IPv6-clean. After the break, I'll do some more testing and submit it to the CoSign project for integration.