/*
* Copyright (c) 2019, 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 <iphlpapi.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 STS_ERROR -1 /* error return lodConfig failed memory allccation failure*/
#define IS_SL_FOUND(sts) (sts & STS_SL_FOUND)
#define IS_NS_FOUND(sts) (sts & STS_NS_FOUND)
/* 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) {
size_t 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 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 loadConfig(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);
if (adapterP == NULL) {
return STS_ERROR;
}
ret = GetAdaptersInfo(adapterP, &size);
if (ret == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_INFO *newAdapterP = (IP_ADAPTER_INFO *)realloc(adapterP, size);
if (newAdapterP == NULL) {
free(adapterP);
return STS_ERROR;
}
adapterP = newAdapterP;
ret = GetAdaptersInfo(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;
}
/*
* Initialize JNI field IDs.
*/
JNIEXPORT void JNICALL
Java_jdk_dns_conf_DnsResolverConfiguration_init0(JNIEnv *env, jclass cls)
{
searchlistID = (*env)->GetStaticFieldID(env, cls, "os_searchlist",
"Ljava/lang/String;");
CHECK_NULL(searchlistID);
nameserversID = (*env)->GetStaticFieldID(env, cls, "os_nameservers",
"Ljava/lang/String;");
}
/*
* Class: jdk_dns_conf_DnsResolverConfguration
* Method: loadConfig0
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_jdk_dns_conf_DnsResolverConfiguration_loadDNSconfig0(JNIEnv *env, jclass cls)
{
char searchlist[MAX_STR_LEN];
char nameservers[MAX_STR_LEN];
jstring obj;
searchlist[0] = '\0';
nameservers[0] = '\0';
if (loadConfig(searchlist, nameservers) != STS_ERROR) {
/*
* Populate static fields in jdk.dns.conf.ResolverConfiguration
*/
obj = (*env)->NewStringUTF(env, searchlist);
CHECK_NULL(obj);
(*env)->SetStaticObjectField(env, cls, searchlistID, obj);
obj = (*env)->NewStringUTF(env, nameservers);
CHECK_NULL(obj);
(*env)->SetStaticObjectField(env, cls, nameserversID, obj);
} else {
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
}
}
/*
* Class: jdk_dns_conf_DnsResolverConfguration
* Method: notifyAddrChange0
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_jdk_dns_conf_DnsResolverConfiguration_notifyAddrChange0(JNIEnv *env, jclass cls)
{
OVERLAPPED ol;
HANDLE h;
DWORD rc, xfer;
ol.hEvent = (HANDLE)0;
rc = NotifyAddrChange(&h, &ol);
if (rc == ERROR_IO_PENDING) {
rc = GetOverlappedResult(h, &ol, &xfer, TRUE);
if (rc != 0) {
return 0; /* address changed */
}
}
/* error */
return -1;
}