jdk/src/windows/native/java/net/NetworkInterface_win9x.c
changeset 2 90ce3da70b43
child 910 1f53246fb014
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/native/java/net/NetworkInterface_win9x.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1142 @@
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <windows.h>
+#include <winsock2.h>
+#include <assert.h>
+
+#include "jni_util.h"
+
+#include "NetworkInterface.h"
+
+/*
+ * Windows 9x specific routines to enumerate network interfaces and the
+ * IP addresses bound to those interfaces.
+ *
+ * Windows 95 does not include IP helper library support by default.
+ * Additionally Windows 98 can have its IP helper library support
+ * trashed by certain IE installations. For these environments we
+ * combine information from the registry with the list of IP addresses
+ * obtained via SIO_GET_INTERFACE_LIST.
+ */
+
+/*
+ * Header files are missing these
+ */
+#if !defined(SIO_GET_INTERFACE_LIST)
+#define SIO_GET_INTERFACE_LIST  _IOR('t', 127, u_long)
+
+struct in_addr6 {
+                u_char  s6_addr[16];
+};
+
+struct sockaddr_in6 {
+                short   sin6_family;
+                u_short sin6_port;
+                u_long  sin6_flowinfo;
+                struct in_addr6 sin6_addr;
+};
+
+typedef union sockaddr_gen{
+                struct sockaddr Address;
+                struct sockaddr_in  AddressIn;
+                struct sockaddr_in6 AddressIn6;
+} sockaddr_gen;
+
+typedef struct _INTERFACE_INFO
+{
+        u_long          iiFlags;
+        sockaddr_gen    iiAddress;
+        sockaddr_gen    iiBroadcastAddress;
+        sockaddr_gen    iiNetmask;
+} INTERFACE_INFO;
+
+#define IFF_UP              0x00000001
+#endif
+
+
+#define MAX_STR_LEN         256
+
+
+/*
+ * A network adapter (similiar to the netif structure except contains
+ * Windows 9x specific fields).
+ */
+typedef struct _adapter {
+    char *name;
+    char *displayName;
+    int index;
+    char *reg_key;
+    int is_wan_driver;
+    netaddr *addrs;
+    struct _adapter *next;
+} adapter;
+
+
+/*
+ * Cached adapter list.
+ */
+static CRITICAL_SECTION cacheLock;
+static adapter *cachedAdapterList;
+
+/*
+ * Initialize cache
+ */
+void init_win9x() {
+    InitializeCriticalSection(&cacheLock);
+}
+
+
+/*
+ * Free adapter list and any addresses bound to the adpater.
+ */
+static void free_adapters(adapter *adapterP) {
+    adapter *curr = adapterP;
+    while (curr != NULL) {
+        if (curr->name != NULL)
+            free(curr->name);
+
+        if (curr->displayName != NULL)
+            free(curr->displayName);
+
+        if (curr->reg_key != NULL)
+            free(curr->reg_key);
+
+        if (curr->addrs != NULL)
+            free_netaddr(curr->addrs);
+
+        adapterP = adapterP->next;
+        free(curr);
+        curr = adapterP;
+    }
+}
+
+
+/*
+ * Returns the SIO_GET_INTERFACE_LIST output
+ */
+static int getInterfaceList(JNIEnv *env, INTERFACE_INFO *infoP, DWORD dwSize) {
+    SOCKET sock;
+    DWORD ret;
+
+    /* create a socket and do the ioctl */
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == INVALID_SOCKET) {
+        JNU_ThrowByName(env, "java/lang/Error", "socket failed");
+        return -1;
+    }
+    ret = WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0,
+                   infoP, dwSize, &dwSize, NULL, NULL);
+    closesocket(sock);
+    if (ret == SOCKET_ERROR) {
+        JNU_ThrowByName(env, "java/lang/Error", "WSAIoctl failed");
+        return -1;
+    }
+    return dwSize;
+}
+
+
+/*
+ * Gross, ugly, and crude way of guessing if this is a WAN (dial-up) driver.
+ * Returns 1 if it's the normal PPCMAC VxD, otherwise 0.
+ */
+static int isWanDriver(char *driver) {
+    LONG ret;
+    HKEY hKey;
+    DWORD dwLen;
+    ULONG ulType;
+    char key[MAX_STR_LEN];
+    char vxd[MAX_STR_LEN];
+
+    sprintf(key, "System\\CurrentControlSet\\Services\\Class\\%s", driver);
+    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, (PHKEY)&hKey);
+    if (ret != ERROR_SUCCESS) {
+        return 0;
+    }
+    dwLen = sizeof(vxd);
+    ret = RegQueryValueEx(hKey, "DeviceVxDs",  NULL, &ulType,
+                         (LPBYTE)vxd, &dwLen);
+    RegCloseKey(hKey);
+    if (ret != ERROR_SUCCESS) {
+        return 0;
+    }
+    return (strcmp(vxd, "pppmac.vxd") == 0);
+}
+
+/*
+ * Windows 9x routine to get the network adapters using the registry.
+ * We enumerate HKEY_LOCAL_MACHINE\Enum and iterate through the tree
+ * looking for devices of class "Net". As these devices may not have a
+ * unique name we assign them a generated name.
+ *
+ * Returns a list of adapters without IP addresses (addrs member is NULL).
+ */
+static int getAdapters(JNIEnv *env, adapter **adapterPP)
+{
+    LONG ret;
+    HKEY enumKey;
+    DWORD dwLen;
+    DWORD dwEnumKeys;
+    DWORD enumIndex;
+    ULONG ulType;
+    int adapterCount = 0;
+    adapter *adapterP = NULL;
+    adapter *curr;
+
+    /*
+     * Start at HKEY_LOCAL_MACHINE\Enum
+     */
+    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Enum", 0, KEY_READ, (PHKEY)&enumKey);
+    if (ret != ERROR_SUCCESS) {
+        return -1;
+    }
+    ret = RegQueryInfoKey(enumKey, NULL, NULL, NULL, &dwEnumKeys,
+                          NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (ret != ERROR_SUCCESS) {
+        RegCloseKey(enumKey);
+        return -1;
+    }
+
+    /*
+     * Iterate through the sub-keys (PCI, Root, ...)
+     */
+    for(enumIndex = 0; enumIndex<dwEnumKeys; enumIndex++) {
+        TCHAR deviceType[MAX_STR_LEN];
+        HKEY deviceKey;
+        DWORD deviceIndex;
+        DWORD dwDeviceKeys;
+
+        dwLen = sizeof(deviceType);
+        ret = RegEnumKeyEx(enumKey, enumIndex, deviceType, &dwLen, NULL, NULL, NULL, NULL);
+        if (ret != ERROR_SUCCESS) {
+            /* ignore this tree */
+            continue;
+        }
+
+        ret = RegOpenKeyEx(enumKey, deviceType, 0, KEY_READ, (PHKEY)&deviceKey);
+        if (ret != ERROR_SUCCESS) {
+            /* ignore this tree */
+            continue;
+        }
+        ret = RegQueryInfoKey(deviceKey, NULL, NULL, NULL, &dwDeviceKeys,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+        if (ret != ERROR_SUCCESS) {
+            /* ignore this tree */
+            RegCloseKey(deviceKey);
+            continue;
+        }
+
+        /*
+         * Iterate through each of the sub-keys under PCI, Root, ...
+         */
+        for (deviceIndex=0; deviceIndex<dwDeviceKeys; deviceIndex++) {
+            TCHAR name[MAX_STR_LEN];
+            HKEY nameKey;
+            DWORD nameIndex;
+            DWORD dwNameKeys;
+
+            dwLen = sizeof(name);
+            ret = RegEnumKeyEx(deviceKey, deviceIndex, name, &dwLen, NULL, NULL, NULL, NULL);
+
+            if (ret != ERROR_SUCCESS) {
+                /* ignore this sub-tree */
+                continue;
+            }
+
+            ret = RegOpenKeyEx(deviceKey, name, 0, KEY_READ, (PHKEY)&nameKey);
+            if (ret != ERROR_SUCCESS) {
+                /* ignore this sub-tree */
+                continue;
+            }
+            ret = RegQueryInfoKey(nameKey, NULL, NULL, NULL, &dwNameKeys,
+                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+            if (ret != ERROR_SUCCESS) {
+                RegCloseKey(nameKey);
+                /* ignore this sub-tree */
+                continue;
+            }
+
+            /*
+             * Finally iterate through the Enum\Root\Net level keys
+             */
+            for (nameIndex=0; nameIndex<dwNameKeys; nameIndex++) {
+                TCHAR dev[MAX_STR_LEN];
+                TCHAR cls[MAX_STR_LEN];
+                HKEY clsKey;
+
+                dwLen = sizeof(dev);
+                ret = RegEnumKeyEx(nameKey, nameIndex, dev, &dwLen, NULL, NULL, NULL, NULL);
+                if (ret != ERROR_SUCCESS) {
+                    continue;
+                }
+
+                ret = RegOpenKeyEx(nameKey, dev, 0, KEY_READ, (PHKEY)&clsKey);
+                if (ret == ERROR_SUCCESS) {
+                    dwLen = sizeof(cls);
+                    ret = RegQueryValueEx(clsKey, "Class",  NULL, &ulType,
+                                          (LPBYTE)cls, &dwLen);
+
+                    if (ret == ERROR_SUCCESS) {
+                        if (strcmp(cls, "Net") == 0) {
+                            TCHAR deviceDesc[MAX_STR_LEN];
+
+                            dwLen = sizeof(deviceDesc);
+                            ret = RegQueryValueEx(clsKey, "DeviceDesc",  NULL, &ulType,
+                                                  (LPBYTE)deviceDesc, &dwLen);
+
+                            if (ret == ERROR_SUCCESS) {
+                                char key_name[MAX_STR_LEN];
+                                char ps_name[8];
+                                char driver[MAX_STR_LEN];
+                                int wan_device;
+
+                                /*
+                                 * Generate a pseudo device name
+                                 */
+                                sprintf(ps_name, "net%d", adapterCount);
+
+                                /*
+                                 * Try to determine if this a WAN adapter. This is
+                                 * useful when we try to eliminate WAN adapters from
+                                 * the interface list when probing for DHCP info
+                                 */
+                                dwLen = sizeof(driver);
+                                ret = RegQueryValueEx(clsKey, "Driver",  NULL,
+                                                      &ulType, (LPBYTE)driver, &dwLen);
+                                if (ret == ERROR_SUCCESS) {
+                                    wan_device = isWanDriver(driver);
+                                } else {
+                                    wan_device = 0;
+                                }
+
+                                /*
+                                 * We have found a Net device. In order to get the
+                                 * static IP addresses we must note the key.
+                                 */
+                                sprintf(key_name, "Enum\\%s\\%s\\%s", deviceType, name, dev);
+
+                                /*
+                                 * Create the net adapter
+                                 */
+                                curr = (adapter *)calloc(1, sizeof(adapter));
+                                if (curr != NULL) {
+                                    curr->is_wan_driver = wan_device;
+                                    curr->name = (char *)malloc(strlen(ps_name) + 1);
+                                    if (curr->name) {
+                                        curr->displayName = (char *)malloc(strlen(deviceDesc) + 1);
+                                        if (curr->displayName) {
+                                            curr->reg_key = (char *)malloc(strlen(key_name)+1);
+                                            if (curr->reg_key == NULL) {
+                                                free(curr->displayName);
+                                                free(curr->name);
+                                                free(curr);
+                                                curr = NULL;
+                                            }
+                                        } else {
+                                            free(curr->name);
+                                            free(curr);
+                                            curr = NULL;
+                                        }
+                                    } else {
+                                        free(curr);
+                                        curr = NULL;
+                                    }
+                                }
+
+                                /* At OutOfMemory occurred */
+                                if (curr == NULL) {
+                                    JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+                                    free_adapters(adapterP);
+                                    RegCloseKey(clsKey);
+                                    RegCloseKey(nameKey);
+                                    RegCloseKey(deviceKey);
+                                    RegCloseKey(enumKey);
+                                    return -1;
+                                }
+
+                                /* index starts at 1 (not 0) */
+                                curr->index = ++adapterCount;
+
+                                strcpy(curr->name, ps_name);
+                                strcpy(curr->displayName, deviceDesc);
+                                strcpy(curr->reg_key, key_name);
+
+                                /*
+                                 * Put the adapter at the end of the list.
+                                 */
+                                if (adapterP == NULL) {
+                                    adapterP = curr;
+                                } else {
+                                    adapter *tail = adapterP;
+                                    while (tail->next != NULL) {
+                                        tail = tail->next;
+                                    }
+                                    tail->next = curr;
+                                }
+                            }
+                        }
+                    }
+                }
+                RegCloseKey(clsKey);
+            }
+            RegCloseKey(nameKey);
+        }
+        RegCloseKey(deviceKey);
+    }
+    RegCloseKey(enumKey);
+
+    /*
+     * Insert an entry for the loopback interface
+     */
+    curr = (adapter *)calloc(1, sizeof(adapter));
+    if (curr == NULL) {
+        JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+        free_adapters(adapterP);
+        return -1;
+    }
+    curr->index = ++adapterCount;
+    curr->name = strdup("lo");
+    curr->displayName = strdup("TCP Loopback interface");
+    curr->next = adapterP;
+    *adapterPP = curr;
+
+    return adapterCount;
+}
+
+/*
+ * Windows 9x routine to obtain any static addresses for a specified
+ * TCP/IP binding.
+ *
+ * We first open Enum\Network\${binding} and check that the driver
+ * is TCP/IP. If so we pick up the driver and check for any IP addresses
+ * in System\\CurrentControlSet\\Services\\Class\\${driver}
+ *
+ * Returns 0 if found, otherwise -1.
+ */
+static int getStaticAddressEntry(char *binding, char *addresses) {
+    LONG ret;
+    HKEY hKey;
+    char name[255];
+    char desc[255];
+    char driver[255];
+    char ipaddr[255];
+    DWORD dwName;
+    ULONG ulType;
+
+    /* assume nothing will be returned */
+    strcpy(addresses, "");
+
+    /*
+     * Open the binding and check that it's TCP/IP
+     */
+    sprintf(name, "Enum\\Network\\%s", binding);
+    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, name, 0, KEY_READ, (PHKEY)&hKey);
+    if (ret != ERROR_SUCCESS) {
+        return -1;
+    }
+    dwName = sizeof(desc);
+    ret = RegQueryValueEx(hKey, "DeviceDesc",  NULL, &ulType,
+                         (LPBYTE)desc, &dwName);
+    if (ret != ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        return -1;
+    }
+    if (strcmp(desc, "TCP/IP") != 0) {
+        /* ignore non-TCP/IP bindings */
+        RegCloseKey(hKey);
+        return -1;
+    }
+
+    /*
+     * Get the driver for this TCP/IP binding
+     */
+    dwName = sizeof(driver);
+    ret = RegQueryValueEx(hKey, "Driver",  NULL, &ulType,
+                          (LPBYTE)driver, &dwName);
+    RegCloseKey(hKey);
+    if (ret != ERROR_SUCCESS) {
+        return -1;
+    }
+
+    /*
+     * Finally check if there is an IPAddress value for this driver.
+     */
+    sprintf(name, "System\\CurrentControlSet\\Services\\Class\\%s", driver);
+    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, name, 0, KEY_READ, (PHKEY)&hKey);
+    if (ret != ERROR_SUCCESS) {
+        return -1;
+    }
+    dwName = sizeof(ipaddr);
+    ret = RegQueryValueEx(hKey, "IPAddress",  NULL, &ulType,
+                          (LPBYTE)ipaddr, &dwName);
+    RegCloseKey(hKey);
+    if (ret != ERROR_SUCCESS) {
+        return -1;
+    }
+
+    /* Return the address(es) */
+    strcpy( addresses, ipaddr );
+    return 0;
+}
+
+/*
+ * Windows 9x routine to enumerate the static IP addresses on a
+ * particular interface using the registry.
+ *
+ * Returns a count of the number of addresses found.
+ */
+static int getStaticAddresses(JNIEnv *env, char *reg_key, netaddr **netaddrPP)
+{
+    LONG ret;
+    HKEY enumKey, bindingKey;
+    DWORD dwLen;
+    ULONG ulType;
+    TCHAR driver[MAX_STR_LEN];
+    char addresses[MAX_STR_LEN];
+    unsigned long addr;     /* IPv4 address */
+    unsigned char byte;
+    netaddr *netaddrP, *curr;
+    int i, addrCount, if_count;
+
+    /*
+     * Open the HKEY_LOCAL_MACHINE\Enum\%s\%s\%s key
+     */
+    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_key, 0, KEY_READ,
+                       (PHKEY)&enumKey);
+    if (ret != ERROR_SUCCESS) {
+        /* interface has been removed */
+        *netaddrPP = NULL;
+        return 0;
+    }
+
+    /*
+     * Iterate through each of the bindings to find any TCP/IP bindings
+     * and any static address assoicated with the binding.
+     */
+    strcpy(addresses, "");
+    addrCount = 0;
+    netaddrP = NULL;
+
+    ret = RegOpenKeyEx(enumKey, "Bindings", 0, KEY_READ, (PHKEY)&bindingKey);
+    if (ret == ERROR_SUCCESS) {
+        DWORD dwBindingKeys;
+        DWORD dwBindingIndex;
+
+        ret = RegQueryInfoKey(bindingKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwBindingKeys,
+                              NULL, NULL, NULL, NULL);
+        if (ret == ERROR_SUCCESS) {
+            TCHAR binding[MAX_STR_LEN];
+
+            dwBindingIndex=0;
+            while (dwBindingIndex<dwBindingKeys) {
+                dwLen = sizeof(binding);
+                ret = RegEnumValue(bindingKey, dwBindingIndex, binding, &dwLen,
+                                   NULL, &ulType, NULL, NULL);
+                if (ret == ERROR_SUCCESS) {
+                    if (getStaticAddressEntry(binding, addresses) == 0) {
+                        /*
+                         * On Windows 9x IP addresses are strings. Multi-homed hosts have
+                         * the IP addresses seperated by commas.
+                         */
+                        addr = 0;
+                        byte = 0;
+                        i = 0;
+                        while ((DWORD)i < strlen(addresses)+1) {
+                            /* eof or seperator */
+                            if (addresses[i] == ',' || addresses[i] == 0) {
+                                if (addr != 0) {
+                                    addr = (addr << 8) | byte;
+
+                                    curr = (netaddr *)malloc(sizeof(netaddr));
+                                    if (curr == NULL) {
+                                        JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+                                        free_netaddr(netaddrP);
+                                        RegCloseKey(enumKey);
+                                        return -1;
+                                    }
+                                    curr->addr.him4.sin_family = AF_INET;
+                                    curr->addr.him4.sin_addr.s_addr = htonl(addr);
+                                    curr->next = netaddrP;
+
+                                    netaddrP = curr;
+                                    addrCount++;
+
+                                    /* reset the address for the next iteration */
+                                    addr = 0;
+                                }
+                                byte = 0;
+                            } else {
+                                if (addresses[i] == '.') {
+                                    addr = (addr << 8) | byte;
+                                    byte = 0;
+                                } else {
+                                    byte = (byte * 10) + (addresses[i] - '0');
+                                }
+                            }
+                            i++;
+                        }
+                    }
+                }
+                if (addrCount > 0) {
+                    break;
+                }
+                dwBindingIndex++;
+            }
+        }
+        RegCloseKey(bindingKey);
+    }
+
+    /* close the registry */
+    RegCloseKey(enumKey);
+
+
+    /* return the list */
+    *netaddrPP = netaddrP;
+    return addrCount;
+}
+
+/*
+ * Windows 9x routine to probe the registry for a DHCP allocated address.
+ * This routine is only useful if we know that only one interface has its
+ * address allocated using DHCP. Returns 0.0.0.0 if none or multiple
+ * addresses found.0
+ */
+static DWORD getDHCPAddress()
+{
+    LONG ret;
+    HKEY hKey;
+    DWORD dwLen;
+    ULONG ulType;
+    char key[MAX_STR_LEN];
+    int index;
+    DWORD dhcp_addr = 0;
+
+    index = 0;
+    while (index < 99) {
+        DWORD addr;
+
+        sprintf(key, "SYSTEM\\CurrentControlSet\\Services\\VxD\\DHCP\\DhcpInfo%02d", index);
+
+        ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, (PHKEY)&hKey);
+        if (ret != ERROR_SUCCESS) {
+            return dhcp_addr;
+        }
+
+        /*
+         * On Windows 9x the DHCP address is in the DhcpIPAddress key. We
+         * are assuming here that this is Windows Socket 2. If Windows
+         * Sockets is the original 1.1 release then this doesn't work because
+         * the IP address if in the DhcpInfo key (a blob with the first 4
+         * bytes set to the IP address).
+         */
+        dwLen = sizeof(addr);
+        ret = RegQueryValueEx(hKey, "DhcpIPAddress",  NULL, &ulType,
+                              (LPBYTE)&addr, &dwLen);
+        RegCloseKey(hKey);
+
+        if (ret == ERROR_SUCCESS) {
+            if (addr) {
+                /* more than 1 DHCP address in registry */
+                if (dhcp_addr) {
+                    return 0;
+                }
+                dhcp_addr = htonl(addr);
+            }
+        }
+        index++;
+    }
+
+    /* if we get here it means we've examined 100 registry entries */
+    return 0;
+}
+
+
+/*
+ * Attempt to allocate the remaining addresses on addrList to the adpaters
+ * on adapterList. Returns the number of address remaining.
+ */
+int allocateRemaining(adapter *adapterList, int address_count, netaddr *addrList) {
+    adapter *adapterP = adapterList;
+    adapter *nobindingsP = NULL;
+
+    /*
+     * If all addresses have been assigned there's nothing to do.
+     */
+    if (address_count == 0) {
+        return 0;
+    }
+
+    /*
+     * Determine if there is only one adapter without an address
+     */
+    while (adapterP != NULL) {
+        if (adapterP->addrs == NULL) {
+            if (nobindingsP == NULL) {
+                nobindingsP = adapterP;
+            } else {
+                nobindingsP = NULL;
+                break;
+            }
+        }
+        adapterP = adapterP->next;
+    }
+
+    /*
+     * Found (only one)
+     */
+    if (nobindingsP) {
+        nobindingsP->addrs = addrList;
+        address_count = 0;
+    }
+
+    return address_count;
+}
+
+
+/*
+ * 1. Network adapters are enumerated by traversing through the
+ *    HKEY_LOCAL_MACHINE\Enum tree and picking out class "Net" devices.
+ *
+ * 2. Address enumeration starts with the list of IP addresses returned
+ *    by SIO_GET_INTERFACE_LIST and then we "allocate" the addresses to
+ *    the network adapters enumerated in step 1. Allocation works as
+ *    follows :-
+ *
+ *    i.   Loopback address is assigned to the loopback interface. If there
+ *         is one network adapter then all other addresses must be bound
+ *         to that adapter.
+ *
+ *    ii.  Enumerate all static IP addresses using the registry. This allows
+ *         us to allocate all static IP address to the corresponding adapter.
+ *
+ *    iii. After step ii. if there is one network adapter that has not been
+ *         allocated an IP address then we know that the remaining IP addresses
+ *         must be bound to this adapter.
+ *
+ *    iv.  If we get to this step it means we are dealing with a complex
+ *         configuration whereby multiple network adapters have their address
+ *         configured dynamically (eg: NIC using DHCP plus modem using PPP).
+ *         We employ a gross hack based on a crude determination done in step 1.
+ *         If there is a DHCP address configured and if one remaining
+ *         network adapter that is not a WAN adapter then the DHCP address
+ *         must be bound to it.
+ */
+static adapter *loadConfig(JNIEnv *env) {
+    adapter *adapterList;
+    int adapter_count;
+    INTERFACE_INFO interfaceInfo[8];
+    DWORD dwSize;
+    int address_count, i;
+    netaddr *addrList;
+
+    /*
+     * Enumerate the network adapters
+     */
+    adapter_count = getAdapters(env, &adapterList);
+    if (adapter_count < 0) {
+        return NULL;
+    }
+    /* minimum of loopback interface */
+    assert(adapter_count >= 1);
+
+    /*
+     * Enumerate all IP addresses as known to winsock
+     */
+    dwSize = getInterfaceList(env, interfaceInfo, sizeof(interfaceInfo));
+    if (dwSize < 0) {
+        free_adapters(adapterList);
+        return NULL;
+    }
+    address_count = dwSize/sizeof(INTERFACE_INFO);
+
+    /* minimum of loopback address */
+    assert(address_count >= 1);
+
+    /*
+     * Create an address list (addrList) from the INTERFACE_INFO
+     * structure.
+     */
+    addrList = NULL;
+    for (i=0; i<address_count; i++) {
+        netaddr *addrP = (netaddr *)calloc(1, sizeof(netaddr));
+        if (addrP == NULL) {
+            JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+            free_netaddr(addrList);
+            free(adapterList);
+            return NULL;
+        }
+
+        addrP->addr.him4.sin_family = AF_INET;
+        addrP->addr.him4.sin_addr.s_addr =
+            ((SOCKADDR_IN *)&(interfaceInfo[i].iiAddress))->sin_addr.S_un.S_addr;
+
+        addrP->next = addrList;
+        addrList = addrP;
+    }
+
+
+    /*
+     * First we assign the loopback address to the lo adapter.
+     * If lo is the only adapter then we are done.
+     */
+    {
+        adapter *loopbackAdapter;
+        netaddr *addrP, *prevP;
+
+        /* find the loopback adapter */
+        loopbackAdapter = adapterList;
+        while (strcmp(loopbackAdapter->name, "lo") != 0) {
+            loopbackAdapter = loopbackAdapter->next;
+        }
+        assert(loopbackAdapter != NULL);
+
+        /* find the loopback address and move it to the loopback adapter */
+        addrP = addrList;
+        prevP = NULL;
+        while (addrP != NULL) {
+            if (addrP->addr.him4.sin_addr.s_addr == htonl(0x7f000001)) {
+                loopbackAdapter->addrs = addrP;
+                if (prevP == NULL) {
+                    addrList = addrP->next;
+                } else {
+                    prevP->next = addrP->next;
+                }
+                loopbackAdapter->addrs->next = NULL;
+                address_count--;
+                break;
+            }
+            prevP = addrP;
+            addrP = addrP->next;
+        }
+    }
+
+
+    /*
+     * Special case. If there's only one network adapter then all remaining
+     * IP addresses must be bound to that adapter.
+     */
+    address_count = allocateRemaining(adapterList, address_count, addrList);
+    if (address_count == 0) {
+        return adapterList;
+    }
+
+    /*
+     * Locate any static IP addresses defined in the registry. Validate the
+     * addresses against the SIO_GET_INTERFACE_LIST (as registry may have
+     * stale settings). If valid we move the addresses from addrList to
+     * the adapter.
+     */
+    {
+        adapter *adapterP;
+
+        adapterP = adapterList;
+        while (adapterP != NULL) {
+            int cnt;
+            netaddr *static_addrP;
+
+            /*
+             * Skip loopback
+             */
+            if (strcmp(adapterP->name, "lo") == 0) {
+                adapterP = adapterP->next;
+                continue;
+            }
+
+            /*
+             * Get the static addresses for this adapter.
+             */
+            cnt = getStaticAddresses(env, adapterP->reg_key, &static_addrP);
+            if (cnt < 0) {
+                free_netaddr(addrList);
+                free(adapterList);
+                return NULL;
+            }
+
+            /*
+             * Validate against the SIO_GET_INTERFACE_LIST.
+             * (avoids stale registry settings).
+             */
+            while (static_addrP != NULL) {
+                netaddr *addrP = addrList;
+                netaddr *prev = NULL;
+
+                while (addrP != NULL) {
+                    if (addrP->addr.him4.sin_addr.s_addr == static_addrP->addr.him4.sin_addr.s_addr)
+                        break;
+
+                    prev = addrP;
+                    addrP = addrP->next;
+                }
+
+                /*
+                 * if addrP is not NULL it means we have a match
+                 * (ie: address from the registry is valid).
+                 */
+                if (addrP != NULL) {
+                    /* remove from addrList */
+                    if (prev == NULL) {
+                        addrList = addrP->next;
+                    } else {
+                        prev->next = addrP->next;
+                    }
+                    address_count--;
+
+                    /* add to adapter list */
+                    addrP->next = adapterP->addrs;
+                    adapterP->addrs = addrP;
+                }
+
+                /*
+                 * On the next static address.
+                 */
+                static_addrP = static_addrP->next;
+            }
+
+            /* not needed */
+            free_netaddr(static_addrP);
+
+            adapterP = adapterP->next;
+        }
+    }
+
+
+    /*
+     * Static addresses are now assigned so try again to allocate the
+     * remaining addresses. This will succeed if there is one adapter
+     * with a dynamically assigned address (DHCP or PPP).
+     */
+    address_count = allocateRemaining(adapterList, address_count, addrList);
+    if (address_count == 0) {
+        return adapterList;
+    }
+
+    /*
+     * Next we see if there is a DHCP address in the registry. If there is
+     * an address (and it's valid) then we know it must be bound to a LAN
+     * adapter. Additionally, when we enumerate the network adapters
+     * we made a crude determination on if an adapter is dial-up. Thus if
+     * we know there is one remaining LAN adapter without an IP address
+     * then the DHCP address must be bound to it.
+     */
+    {
+        long dhcp_addr = getDHCPAddress(); /* returns in network order */
+        if (dhcp_addr) {
+            netaddr *addrP, *prevP;
+
+            /*
+             * Check that the DHCP address is valid
+             */
+            addrP = addrList;
+            prevP = NULL;
+            while (addrP != NULL) {
+                if (addrP->addr.him4.sin_addr.s_addr == dhcp_addr) {
+                    break;
+                }
+                prevP = addrP;
+                addrP = addrP->next;
+            }
+
+            /*
+             * Address is valid - now check how many non-WAN adapters
+             * don't have addresses yet.
+             */
+            if (addrP != NULL) {
+                adapter *adapterP = adapterList;
+                adapter *nobindingsP = NULL;
+
+                while (adapterP != NULL) {
+                    if (adapterP->addrs == NULL && !adapterP->is_wan_driver) {
+                        if (nobindingsP == NULL) {
+                            nobindingsP = adapterP;
+                        } else {
+                            /* already found one */
+                            nobindingsP = NULL;
+                            break;
+                        }
+                    }
+                    adapterP = adapterP->next;
+                }
+
+                /*
+                 * One non-WAN adapter remaining
+                 */
+                if (nobindingsP != NULL) {
+                    nobindingsP->addrs = addrP;
+
+                    /* remove from addrList */
+                    if (prevP == NULL) {
+                        addrList = addrP->next;
+                    } else {
+                        prevP->next = addrP->next;
+                    }
+                    addrP->next = NULL;
+                    address_count--;
+                }
+            }
+        }
+    }
+
+    /*
+     * Finally we do one final attempt to re-assign any remaining
+     * addresses. This catches the case of 2 adapters that have their
+     * addresses dynamically assigned (specifically NIC with DHCP, and
+     * Modem using RAS/PPP).
+     */
+    address_count = allocateRemaining(adapterList, address_count, addrList);
+    if (address_count == 0) {
+        return adapterList;
+    }
+
+    /*
+     * Free any unallocated addresses
+     */
+    if (address_count > 0) {
+        free_netaddr(addrList);
+    }
+
+    /*
+     * Return the adapter List.
+     */
+    return adapterList;
+
+}
+
+
+/*
+ * Enumerate network interfaces. If successful returns the number of
+ * network interfaces and netifPP returning a list of netif structures.
+ * Returns -1 with exception thrown if error.
+ */
+int enumInterfaces_win9x(JNIEnv *env, netif **netifPP) {
+    adapter *adapters, *adapterP;
+    int cnt = 0;
+    netif *netifP = NULL;
+
+    /* enumerate network configuration */
+    adapters = loadConfig(env);
+    if (adapters == NULL) {
+        return -1;
+    }
+
+    /*
+     * loadConfig returns an adapter list - we need to create a corresponding
+     * list of netif structures.
+     */
+    adapterP = adapters;
+    while (adapterP != NULL) {
+        netif *ifs = (netif *)calloc(1, sizeof(netif));
+
+        if (ifs == NULL) {
+            JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+            free_adapters(adapters);
+            free_netif(netifP);
+            return -1;
+        }
+
+        ifs->name = strdup(adapterP->name);
+        ifs->displayName = strdup(adapterP->displayName);
+        ifs->dwIndex = adapterP->index;
+        ifs->index = adapterP->index;
+        ifs->next = netifP;
+        netifP = ifs;
+
+        if (ifs->name == NULL || ifs->displayName == NULL) {
+            JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+            free_adapters(adapters);
+            free_netif(netifP);
+            return -1;
+        }
+
+        cnt++;
+        adapterP = adapterP->next;
+    }
+
+    /*
+     * Put the adapter list in the cache
+     */
+    EnterCriticalSection(&cacheLock);
+    {
+        if (cachedAdapterList != NULL) {
+            free_adapters(cachedAdapterList);
+        }
+        cachedAdapterList = adapters;
+    }
+    LeaveCriticalSection(&cacheLock);
+
+    /*
+     * Return the netif list
+     */
+    *netifPP = netifP;
+    return cnt;
+}
+
+/*
+ * Enumerate the addresses for the specified network interface. If successful
+ * returns the number of addresses bound to the interface and sets netaddrPP
+ * to be a list of netaddr structures. Returns -1 if error.
+ */
+int enumAddresses_win9x(JNIEnv *env, netif *netifP, netaddr **netaddrPP) {
+
+    EnterCriticalSection(&cacheLock);
+    {
+        adapter *adapterP = cachedAdapterList;
+        while (adapterP != NULL) {
+            if (strcmp(adapterP->name, netifP->name) == 0) {
+
+                netaddr *newlist = NULL;
+                netaddr *curr = adapterP->addrs;
+                int cnt = 0;
+
+                while (curr != NULL) {
+                    /*
+                     * Clone the netaddr and add it to newlist.
+                     */
+                    netaddr *tmp = (netaddr *)calloc(1, sizeof(netaddr));
+                    if (tmp == NULL) {
+                        LeaveCriticalSection(&cacheLock);
+                        JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+                        free_netaddr(newlist);
+                        return -1;
+                    }
+                    tmp->addr = curr->addr;
+                    tmp->next = newlist;
+                    newlist = tmp;
+
+                    cnt++;
+                    curr = curr->next;
+                }
+
+                *netaddrPP = newlist;
+                LeaveCriticalSection(&cacheLock);
+                return cnt;
+            }
+            adapterP = adapterP->next;
+        }
+    }
+    LeaveCriticalSection(&cacheLock);
+
+    *netaddrPP = NULL;
+    return 0;
+}