This is the page of my IPv6 patch for Tiberian Sun, a game originally made by Westwood and recently rereleased as freeware by Electronic Arts (the current rights holder).
It is in all imaginable ways a horrible, detestable hack (see below for technical details), and should of course be considered proof-of-concept above anything else. It does, however, work pretty well for me, and allows Nod to take over the world even in the future dystopia of pervasive double-NATed IPv4. :-)
Tiebrian Sun IPv6 patch 0.1 (44 kB, includes source). See below for installation instructions.
NOTE: You will need Windows Vista or newer for this to work. (WINE 1.2 prereleases or newer also work fine—in fact WINE was my primary development platform—but I've had reports that WINE 1.0 doesn't work.) Specifically, Windows XP will not cut it, even if you've enabled IPv6 manually.
The big question is, of course, why? There are many reasons, some perhaps more than others:
A small list of subproblems to tackle for a binary patch like this; mostly for the sufficiently perverse. Probably quite uninteresting if you're not into programming.
First, we need to somehow get our code into the Tiberian Sun process. There are many ways of doing this, but the simplest is to put it into a DLL (which gets its own address space, can have its own imports, etc.) and make the process somehow load that DLL. (On DLL load, DllMain() is called, from which we can do pretty much whatever we want to the process.)
Tiberian Sun is bilingual (English/German) and uses a file called language.dll to store its message resources. This DLL is the only one loaded before any networking code, so it's an obviou target for us. We simply move the original DLL out of the way (to languageorig.dll) and put our own code there instead. Any requests for the language resources are just proxied on to languageorig.dll, so the effect on the messages displayed is zero.
In order to reduce complexity (changing data structure using binary patching is very cumbersome), all Tiberian Sun networking code still believes it's talking IPv4. To this extent, a technique that looks a bit like a weird inverse form of NAT-PT is used; whenever we need to talk to an IPv6 address, an IPv4 address from unused address space (22.214.171.124/3) is allocated, and that address is the one the game sees. (This works because although there's trillions and trillions of IPv6 addresses out there, a typical program doen't need to talk to more than a couple of hundred or thousand during its lifetime.)
We put a small layer between Tiberian Sun and the Winsock layer that deals with this patching (mostly by changing the entries in the import tables to point to our own functions). Whenever it sees attempts at IPv4 communication with one of these special addresses, it looks up that address in its translation table and sends that request over IPv6 instead. Similarly, inbound traffic from IPv6 hosts is rewritten to one of these shim addresses. Also, of course, all attempts to open IPv4 sockets are replaced by attempts to open dual-stack IPv4/IPv6 sockets instead.
Being able to talk IPv6 is not very useful without others knowing how to contact you. To this end, the Tiberian Sun lobby protocol has been extended using a few extra commands—and thanks to Olof van der Spek of XWIS (who currently runs the Tiberian Sun servers on a community basis), the server understands these extensions.
The protocol used actually looks a lot like IRC (although with a ton of weird extensions), so it's text-based and relatively easy to understand. The main extra change is that the places that usually hold an IPv4 address (for some reason specified in decimal form, so 126.96.36.199 becomes 0x04030201 = 67305985) can also hold an IPv6 address. Colon (:) has a special meaning in the IRC protocol, and the game's protocol parser is pretty rudimentary, so to avoid confusion addresses are sent back and forth with underscores instead (e.g. 2001_700_300_1880__2). Parsing of these addresses happen a few places, but they all happen to call atol(), so by redirecting that function into something that can handle IPv6 addresses (and add IPv4 translation entries as needed) the parsing problem is solved as well. :-)
Finally the client needs to give out an IPv6 address where it can be contacted (using a new command, IPV6ADDR), since the client typically connects over IPv4 (otherwise, we'd need to find and report the IPv4 address, which is a lot harder due to NAT). This is one of the few places where there's actual code patching going on, as opposed to just redirecting functions; some extra code is patched into the initial handshake to find a usable IPv6 address and report it.
Last updated July 28th, 2010.