blob: 21a87afa25ecf2f3ddef3d7a5ac3dd203db1b206 [file] [log] [blame]
/*
* Copyright (c) 2002, 2008, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
#include <iprtrmib.h>
#include <time.h>
#include <assert.h>
#include "jni_util.h"
#define MAX_STR_LEN 256
#define STS_NO_CONFIG 0x0 /* no configuration found */
#define STS_SL_FOUND 0x1 /* search list found */
#define STS_NS_FOUND 0x2 /* name servers found */
#define IS_SL_FOUND(sts) (sts & STS_SL_FOUND)
#define IS_NS_FOUND(sts) (sts & STS_NS_FOUND)
/*
* Visual C++ SP3 (as required by J2SE 1.4.0) is missing some of
* the definitions required for the IP helper library routines that
* were added in Windows 98 & Windows 2000.
*/
#ifndef MAX_ADAPTER_NAME_LENGTH
#define MAX_ADAPTER_ADDRESS_LENGTH 8
#define MAX_ADAPTER_DESCRIPTION_LENGTH 128
#define MAX_ADAPTER_NAME_LENGTH 256
#define MAX_HOSTNAME_LEN 128
#define MAX_DOMAIN_NAME_LEN 128
#define MAX_SCOPE_ID_LEN 256
typedef struct {
char String[4 * 4];
} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
typedef struct _IP_ADDR_STRING {
struct _IP_ADDR_STRING* Next;
IP_ADDRESS_STRING IpAddress;
IP_MASK_STRING IpMask;
DWORD Context;
} IP_ADDR_STRING, *PIP_ADDR_STRING;
typedef struct _IP_ADAPTER_INFO {
struct _IP_ADAPTER_INFO* Next;
DWORD ComboIndex;
char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
UINT AddressLength;
BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD Index;
UINT Type;
UINT DhcpEnabled;
PIP_ADDR_STRING CurrentIpAddress;
IP_ADDR_STRING IpAddressList;
IP_ADDR_STRING GatewayList;
IP_ADDR_STRING DhcpServer;
BOOL HaveWins;
IP_ADDR_STRING PrimaryWinsServer;
IP_ADDR_STRING SecondaryWinsServer;
time_t LeaseObtained;
time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
typedef struct _FIXED_INFO {
char HostName[MAX_HOSTNAME_LEN + 4] ;
char DomainName[MAX_DOMAIN_NAME_LEN + 4];
PIP_ADDR_STRING CurrentDnsServer;
IP_ADDR_STRING DnsServerList;
UINT NodeType;
char ScopeId[MAX_SCOPE_ID_LEN + 4];
UINT EnableRouting;
UINT EnableProxy;
UINT EnableDns;
} FIXED_INFO, *PFIXED_INFO;
#endif
/* IP helper library routine used on 98/2000/XP */
static int (PASCAL FAR *GetNetworkParams_fn)();
static int (PASCAL FAR *GetAdaptersInfo_fn)();
static int (PASCAL FAR *NotifyAddrChange_fn)();
/*
* Routines to obtain domain name and name servers are OS specific
*/
typedef int (*LoadConfig)(char *sl, char *ns);
static LoadConfig loadconfig_fn;
/*
* JNI ids
*/
static jfieldID searchlistID;
static jfieldID nameserversID;
/*
* Utility routine to append s2 to s1 with a space delimiter.
* strappend(s1="abc", "def") => "abc def"
* strappend(s1="", "def") => "def
*/
void strappend(char *s1, char *s2) {
int len;
if (s2[0] == '\0') /* nothing to append */
return;
len = strlen(s1)+1;
if (s1[0] != 0) /* needs space character */
len++;
if (len + strlen(s2) > MAX_STR_LEN) /* insufficient space */
return;
if (s1[0] != 0) {
strcat(s1, " ");
}
strcat(s1, s2);
}
/*
* Windows 95/98/ME for static TCP/IP configuration.
*
* Use registry approach for statically configured TCP/IP settings.
* Registry entries described in "MS TCP/IP and Windows 95 Networking"
* (Microsoft TechNet site).
*/
static int loadStaticConfig9x(char *sl, char *ns) {
LONG ret;
HANDLE hKey;
DWORD dwLen;
ULONG ulType;
char result[MAX_STR_LEN];
int sts = STS_NO_CONFIG;
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP",
0,
KEY_READ,
(PHKEY)&hKey);
if (ret == ERROR_SUCCESS) {
/*
* Determine suffix list
*/
result[0] = '\0';
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "SearchList", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if ((ret != ERROR_SUCCESS) || (strlen(result) == 0)) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "Domain", NULL, &ulType,
(LPBYTE)&result, &dwLen);
}
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
if (strlen(result) > 0) {
strappend(sl, result);
sts |= STS_SL_FOUND;
}
}
/*
* Determine DNS name server(s)
*/
result[0] = '\0';
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "NameServer", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
if (strlen(result) > 0) {
strappend(ns, result);
sts |= STS_NS_FOUND;
}
}
RegCloseKey(hKey);
}
return sts;
}
/*
* Windows 95
*
* Use registry approach for statically configured TCP/IP settings
* (see loadStaticConfig9x).
*
* If DHCP is used we examine the DHCP vendor specific extensions. We parse
* this based on format described in RFC 2132.
*
* If Dial-up Networking (DUN) is used then this TCP/IP settings cannot
* be determined here.
*/
static int loadConfig95(char *sl, char *ns) {
int sts;
int index;
LONG ret;
HANDLE hKey;
DWORD dwLen;
ULONG ulType;
char optionInfo[MAX_STR_LEN];
/*
* First try static configuration - if found we are done.
*/
sts = loadStaticConfig9x(sl, ns);
if (IS_SL_FOUND(sts) && IS_NS_FOUND(sts)) {
return sts;
}
/*
* Try DHCP. DHCP information is stored in :-
* SYSTEM\CurrentControlSet\Services\VxD\DHCP\DhcpInfoXX
*
* The key is normally DhcpInfo00\OptionInfo (see Article Q255245 on
* Microsoft site). However when multiple cards are added & removed we
* have observed that it can be located in DhcpInfo{01,02, ...}.
* As a hack we search all DhcpInfoXX keys until we find OptionInfo.
*/
for (index=0; index<99; index++) {
char key[MAX_STR_LEN];
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) {
/* end of DhcpInfoXX entries */
break;
}
dwLen = sizeof(optionInfo);
ret = RegQueryValueEx(hKey, "OptionInfo", NULL, &ulType,
(LPBYTE)optionInfo, &dwLen);
RegCloseKey(hKey);
if (ret == ERROR_SUCCESS) {
/* OptionInfo found */
break;
}
}
/*
* If OptionInfo was found then we parse (as the 'options' field of
* the DHCP packet - see RFC 2132).
*/
if (ret == ERROR_SUCCESS) {
unsigned int pos = 0;
while (pos < dwLen) {
int code, len;
code = optionInfo[pos];
pos++;
if (pos >= dwLen) break; /* bad packet */
len = optionInfo[pos];
pos++;
if (pos+len > dwLen) break; /* bad packet */
/*
* Domain Name - see RFC 2132 section 3.17
*/
if (!IS_SL_FOUND(sts)) {
if (code == 0xf) {
char domain[MAX_STR_LEN];
assert(len < MAX_STR_LEN);
memcpy((void *)domain, (void *)&(optionInfo[pos]), (size_t)len);
domain[len] = '\0';
strappend(sl, domain);
sts |= STS_SL_FOUND;
}
}
/*
* DNS Option - see RFC 2132 section 3.8
*/
if (!IS_NS_FOUND(sts)) {
if (code == 6 && (len % 4) == 0) {
while (len > 0 && pos < dwLen) {
char addr[32];
sprintf(addr, "%d.%d.%d.%d",
(unsigned char)optionInfo[pos],
(unsigned char)optionInfo[pos+1],
(unsigned char)optionInfo[pos+2],
(unsigned char)optionInfo[pos+3]);
pos += 4;
len -= 4;
/*
* Append to list of name servers
*/
strappend(ns, addr);
sts |= STS_NS_FOUND;
}
}
}
/*
* Onto the next options
*/
pos += len;
}
}
return sts;
}
/*
* Windows 98/ME
*
* Use registry approach for statically configured TCP/IP settings
* (see loadStaticConfig9x).
*
* If configuration is not static then use IP helper library routine
* GetNetworkParams to obtain the network settings which include the
* domain name and the DNS servers. Note that we use the registry in
* preference to GetNetworkParams as the domain name is not populated
* by GetNetworkParams if the configuration is static.
*/
static int loadConfig98(char *sl, char *ns) {
FIXED_INFO *infoP;
ULONG size;
DWORD ret;
int sts;
/*
* Use registry approach to pick up static configuation.
*/
sts = loadStaticConfig9x(sl, ns);
if (IS_SL_FOUND(sts) && IS_NS_FOUND(sts)) {
return sts;
}
/*
* Use IP helper library to obtain dynamic configuration (DHCP and
* DUN).
*/
size = sizeof(FIXED_INFO);
infoP = (FIXED_INFO *)malloc(size);
if (infoP) {
ret = (*GetNetworkParams_fn)(infoP, &size);
if (ret == ERROR_BUFFER_OVERFLOW) {
infoP = (FIXED_INFO *)realloc(infoP, size);
if (infoP != NULL)
ret = (*GetNetworkParams_fn)(infoP, &size);
}
}
if (infoP == NULL) {
return sts;
}
if (ret == ERROR_SUCCESS) {
/*
* Use DomainName if search-list not specified.
*/
if (!IS_SL_FOUND(sts)) {
strappend(sl, infoP->DomainName);
sts |= STS_SL_FOUND;
}
/*
* Use DnsServerList if not statically configured.
*/
if (!IS_NS_FOUND(sts)) {
PIP_ADDR_STRING dnsP = &(infoP->DnsServerList);
do {
strappend(ns, (char *)&(dnsP->IpAddress));
dnsP = dnsP->Next;
} while (dnsP != NULL);
sts |= STS_NS_FOUND;
}
}
free(infoP);
return sts;
}
/*
* Windows NT
*
* Use registry approach based on settings described in "TCP/IP and
* NBT Configuration Parameters for Windows" - Article Q12062 on
* Microsoft site.
*
* All non-RAS TCP/IP settings are stored in HKEY_LOCAL_MACHINE in
* the SYSTEM\CurrentControlSet\Services\Tcpip\Parameters key.
*
* If SearchList if not provided then return Domain or DhcpDomain.
* If Domain is specified it overrides DhcpDomain even if DHCP is
* enabled.
*
* DNS name servers based on NameServer or DhcpNameServer settings.
* NameServer overrides DhcpNameServer even if DHCP is enabled.
*/
static int loadConfigNT(char *sl, char *ns) {
LONG ret;
HANDLE hKey;
DWORD dwLen;
ULONG ulType;
char result[MAX_STR_LEN];
int sts = STS_NO_CONFIG;
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
0,
KEY_READ,
(PHKEY)&hKey);
if (ret != ERROR_SUCCESS) {
return sts;
}
/*
* Determine search list
*/
result[0] = '\0';
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "SearchList", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if ((ret != ERROR_SUCCESS) || (strlen(result) == 0)) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "Domain", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if ((ret != ERROR_SUCCESS) || (strlen(result) == 0)) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "DhcpDomain", NULL, &ulType,
(LPBYTE)&result, &dwLen);
}
}
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
if (strlen(result) > 0) {
strappend(sl, result);
sts |= STS_SL_FOUND;
}
}
/*
* Determine DNS name server(s)
*/
result[0] = '\0';
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "NameServer", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if ((ret != ERROR_SUCCESS) || (strlen(result) == 0)) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "DhcpNameServer", NULL, &ulType,
(LPBYTE)&result, &dwLen);
}
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
if (strlen(result) > 0) {
strappend(ns, result);
sts |= STS_NS_FOUND;
}
}
RegCloseKey(hKey);
return sts;
}
/*
* Windows 2000/XP
*
* Use registry approach based on settings described in Appendix C
* of "Microsoft Windows 2000 TCP/IP Implementation Details".
*
* DNS suffix list is obtained from SearchList registry setting. If
* this is not specified we compile suffix list based on the
* per-connection domain suffix.
*
* DNS name servers and domain settings are on a per-connection
* basic. We therefore enumerate the network adapters to get the
* names of each adapter and then query the corresponding registry
* settings to obtain NameServer/DhcpNameServer and Domain/DhcpDomain.
*/
static int loadConfig2000(char *sl, char *ns) {
IP_ADAPTER_INFO *adapterP;
ULONG size;
DWORD ret;
DWORD dwLen;
ULONG ulType;
char result[MAX_STR_LEN];
HANDLE hKey;
int gotSearchList = 0;
/*
* First see if there is a global suffix list specified.
*/
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
0,
KEY_READ,
(PHKEY)&hKey);
if (ret == ERROR_SUCCESS) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "SearchList", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
if (strlen(result) > 0) {
strappend(sl, result);
gotSearchList = 1;
}
}
RegCloseKey(hKey);
}
/*
* Ask the IP Helper library to enumerate the adapters
*/
size = sizeof(IP_ADAPTER_INFO);
adapterP = (IP_ADAPTER_INFO *)malloc(size);
ret = (*GetAdaptersInfo_fn)(adapterP, &size);
if (ret == ERROR_BUFFER_OVERFLOW) {
adapterP = (IP_ADAPTER_INFO *)realloc(adapterP, size);
ret = (*GetAdaptersInfo_fn)(adapterP, &size);
}
/*
* Iterate through the list of adapters as registry settings are
* keyed on the adapter name (GUID).
*/
if (ret == ERROR_SUCCESS) {
IP_ADAPTER_INFO *curr = adapterP;
while (curr != NULL) {
char key[MAX_STR_LEN];
sprintf(key,
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s",
curr->AdapterName);
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
key,
0,
KEY_READ,
(PHKEY)&hKey);
if (ret == ERROR_SUCCESS) {
DWORD enableDhcp = 0;
/*
* Is DHCP enabled on this interface
*/
dwLen = sizeof(enableDhcp);
ret = RegQueryValueEx(hKey, "EnableDhcp", NULL, &ulType,
(LPBYTE)&enableDhcp, &dwLen);
/*
* If we don't have the suffix list when get the Domain
* or DhcpDomain. If DHCP is enabled then Domain overides
* DhcpDomain
*/
if (!gotSearchList) {
result[0] = '\0';
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "Domain", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if (((ret != ERROR_SUCCESS) || (strlen(result) == 0)) &&
enableDhcp) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "DhcpDomain", NULL, &ulType,
(LPBYTE)&result, &dwLen);
}
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
strappend(sl, result);
}
}
/*
* Get DNS servers based on NameServer or DhcpNameServer
* registry setting. If NameServer is set then it overrides
* DhcpNameServer (even if DHCP is enabled).
*/
result[0] = '\0';
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "NameServer", NULL, &ulType,
(LPBYTE)&result, &dwLen);
if (((ret != ERROR_SUCCESS) || (strlen(result) == 0)) &&
enableDhcp) {
dwLen = sizeof(result);
ret = RegQueryValueEx(hKey, "DhcpNameServer", NULL, &ulType,
(LPBYTE)&result, &dwLen);
}
if (ret == ERROR_SUCCESS) {
assert(ulType == REG_SZ);
strappend(ns, result);
}
/*
* Finished with this registry key
*/
RegCloseKey(hKey);
}
/*
* Onto the next adapeter
*/
curr = curr->Next;
}
}
/*
* Free the adpater structure
*/
if (adapterP) {
free(adapterP);
}
return STS_SL_FOUND & STS_NS_FOUND;
}
/*
* Initialization :-
*
* 1. Based on OS version set the function pointer for OS specific load
* configuration routine.
*
* 2. On 98/2000/XP load the IP helper library.
*
* 3. Initialize JNI field IDs.
*
*/
JNIEXPORT void JNICALL
Java_sun_net_dns_ResolverConfigurationImpl_init0(JNIEnv *env, jclass cls)
{
OSVERSIONINFO ver;
jboolean loadHelperLibrary = JNI_TRUE;
/*
* First we figure out which OS is running
*/
ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver);
if (ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
if ((ver.dwMajorVersion == 4) && (ver.dwMinorVersion == 0)) {
/*
* Windows 95
*/
loadHelperLibrary = JNI_FALSE;
loadconfig_fn = loadConfig95;
} else {
/*
* Windows 98/ME
*/
loadHelperLibrary = JNI_TRUE;
loadconfig_fn = loadConfig98;
}
}
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
if (ver.dwMajorVersion <= 4) {
/*
* Windows NT
*/
loadHelperLibrary = JNI_FALSE;
loadconfig_fn = loadConfigNT;
} else {
/*
* Windows 2000/XP
*/
loadHelperLibrary = JNI_TRUE;
loadconfig_fn = loadConfig2000;
}
}
/*
* On 98/2000/XP we load the IP Helper Library.
*/
if (loadHelperLibrary) {
HANDLE h = LoadLibrary("iphlpapi.dll");
if (h != NULL) {
GetNetworkParams_fn = (int (PASCAL FAR *)())GetProcAddress(h, "GetNetworkParams");
GetAdaptersInfo_fn = (int (PASCAL FAR *)())GetProcAddress(h, "GetAdaptersInfo");
NotifyAddrChange_fn = (int (PASCAL FAR *)())GetProcAddress(h, "NotifyAddrChange");
}
if (GetNetworkParams_fn == NULL || GetAdaptersInfo_fn == NULL) {
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", "iphlpapi.dll");
return;
}
}
/*
* Get JNI ids
*/
searchlistID = (*env)->GetStaticFieldID(env, cls, "os_searchlist",
"Ljava/lang/String;");
nameserversID = (*env)->GetStaticFieldID(env, cls, "os_nameservers",
"Ljava/lang/String;");
}
/*
* Class: sun_net_dns_ResolverConfgurationImpl
* Method: loadConfig0
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_net_dns_ResolverConfigurationImpl_loadDNSconfig0(JNIEnv *env, jclass cls)
{
char searchlist[MAX_STR_LEN];
char nameservers[MAX_STR_LEN];
jstring obj;
searchlist[0] = '\0';
nameservers[0] = '\0';
/* call OS specific routine */
(void)(*loadconfig_fn)(searchlist, nameservers);
/*
* Populate static fields in sun.net.DefaultResolverConfiguration
*/
obj = (*env)->NewStringUTF(env, searchlist);
(*env)->SetStaticObjectField(env, cls, searchlistID, obj);
obj = (*env)->NewStringUTF(env, nameservers);
(*env)->SetStaticObjectField(env, cls, nameserversID, obj);
}
/*
* Class: sun_net_dns_ResolverConfgurationImpl
* Method: notifyAddrChange0
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_sun_net_dns_ResolverConfigurationImpl_notifyAddrChange0(JNIEnv *env, jclass cls)
{
OVERLAPPED ol;
HANDLE h;
DWORD rc, xfer;
if (NotifyAddrChange_fn != NULL) {
ol.hEvent = (HANDLE)0;
rc = (*NotifyAddrChange_fn)(&h, &ol);
if (rc == ERROR_IO_PENDING) {
rc = GetOverlappedResult(h, &ol, &xfer, TRUE);
if (rc != 0) {
return 0; /* address changed */
}
}
}
/* NotifyAddrChange not support or error */
return -1;
}