/* * Copyright 2002-2008 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 #include #include #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; enumIndexis_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; char addresses[MAX_STR_LEN]; unsigned long addr; /* IPv4 address */ unsigned char byte; netaddr *netaddrP, *curr; int i, addrCount; /* * 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 (dwBindingIndexaddr.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; iaddr.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; }