| /* | |
| * Copyright (c) 2002, 2023, 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 <stdio.h> | |
| #include <stddef.h> | |
| #include <time.h> | |
| #include <assert.h> | |
| #include "net_util.h" | |
| #include "jni_util.h" | |
| #define MAX_STR_LEN 1024 | |
| #define BUFF_SIZE 15360 | |
| #define MAX_TRIES 3 | |
| #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; | |
| /* | |
| * return an array of IP_ADAPTER_ADDRESSES containing one element | |
| * for each adapter on the system. Returned in *adapters. | |
| * Buffer is malloc'd and must be freed (unless error returned) | |
| */ | |
| static int getAdapters (JNIEnv *env, int flags, IP_ADAPTER_ADDRESSES **adapters) { | |
| DWORD ret; | |
| IP_ADAPTER_ADDRESSES *adapterInfo; | |
| ULONG len; | |
| int try; | |
| *adapters = NULL; | |
| adapterInfo = (IP_ADAPTER_ADDRESSES *) malloc(BUFF_SIZE); | |
| if (adapterInfo == NULL) { | |
| JNU_ThrowByName(env, "java/lang/OutOfMemoryError", | |
| "Native heap allocation failure"); | |
| return -1; | |
| } | |
| len = BUFF_SIZE; | |
| ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo, &len); | |
| for (try = 0; ret == ERROR_BUFFER_OVERFLOW && try < MAX_TRIES; ++try) { | |
| IP_ADAPTER_ADDRESSES * newAdapterInfo = NULL; | |
| if (len < (ULONG_MAX - BUFF_SIZE)) { | |
| len += BUFF_SIZE; | |
| } | |
| newAdapterInfo = | |
| (IP_ADAPTER_ADDRESSES *) realloc (adapterInfo, len); | |
| if (newAdapterInfo == NULL) { | |
| free(adapterInfo); | |
| JNU_ThrowByName(env, "java/lang/OutOfMemoryError", | |
| "Native heap allocation failure"); | |
| return -1; | |
| } | |
| adapterInfo = newAdapterInfo; | |
| ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo, &len); | |
| } | |
| if (ret != ERROR_SUCCESS) { | |
| free (adapterInfo); | |
| switch (ret) { | |
| case ERROR_INVALID_PARAMETER: | |
| JNU_ThrowInternalError(env, | |
| "IP Helper Library GetAdaptersAddresses function failed: " | |
| "invalid parameter"); | |
| break; | |
| case ERROR_NOT_ENOUGH_MEMORY: | |
| JNU_ThrowOutOfMemoryError(env, | |
| "IP Helper Library GetAdaptersAddresses function failed: " | |
| "not enough memory"); | |
| break; | |
| case ERROR_NO_DATA: | |
| // not an error | |
| *adapters = NULL; | |
| return ERROR_SUCCESS; | |
| default: | |
| SetLastError(ret); | |
| JNU_ThrowByNameWithMessageAndLastError(env, | |
| JNU_JAVANETPKG "SocketException", | |
| "IP Helper Library GetAdaptersAddresses function failed"); | |
| break; | |
| } | |
| return -1; | |
| } | |
| *adapters = adapterInfo; | |
| return ERROR_SUCCESS; | |
| } | |
| /* | |
| * Utility routine to append s2 to s1 with a comma 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 comma character */ | |
| len++; | |
| if (len + strlen(s2) > MAX_STR_LEN) /* insufficient space */ | |
| return; | |
| if (s1[0] != 0) { | |
| strcat(s1, ","); | |
| } | |
| strcat(s1, s2); | |
| } | |
| /* | |
| * Use DNS server addresses returned by GetAdaptersAddresses for currently | |
| * active interfaces. | |
| */ | |
| static int loadConfig(JNIEnv *env, char *sl, char *ns) { | |
| IP_ADAPTER_ADDRESSES *adapters, *adapter; | |
| IP_ADAPTER_DNS_SERVER_ADDRESS *dnsServer; | |
| WCHAR *suffix; | |
| DWORD ret, flags; | |
| DWORD dwLen; | |
| ULONG ulType; | |
| char result[MAX_STR_LEN]; | |
| HANDLE hKey; | |
| SOCKADDR *sockAddr; | |
| struct sockaddr_in6 *sockAddrIpv6; | |
| /* | |
| * 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); | |
| } | |
| } | |
| RegCloseKey(hKey); | |
| } | |
| // We only need DNS server addresses so skip everything else. | |
| flags = GAA_FLAG_SKIP_UNICAST; | |
| flags |= GAA_FLAG_SKIP_ANYCAST; | |
| flags |= GAA_FLAG_SKIP_MULTICAST; | |
| flags |= GAA_FLAG_SKIP_FRIENDLY_NAME; | |
| ret = getAdapters(env, flags, &adapters); | |
| if (ret != ERROR_SUCCESS) { | |
| return STS_ERROR; | |
| } | |
| adapter = adapters; | |
| while (adapter != NULL) { | |
| // Only load config from enabled adapters. | |
| if (adapter->OperStatus == IfOperStatusUp) { | |
| dnsServer = adapter->FirstDnsServerAddress; | |
| while (dnsServer != NULL) { | |
| sockAddr = dnsServer->Address.lpSockaddr; | |
| if (sockAddr->sa_family == AF_INET6) { | |
| sockAddrIpv6 = (struct sockaddr_in6 *)sockAddr; | |
| if (sockAddrIpv6->sin6_scope_id != 0) { | |
| // An address with a scope is either link-local or | |
| // site-local, which aren't valid for DNS queries so | |
| // we can skip them. | |
| dnsServer = dnsServer->Next; | |
| continue; | |
| } | |
| } | |
| dwLen = sizeof(result); | |
| ret = WSAAddressToStringA(sockAddr, | |
| dnsServer->Address.iSockaddrLength, NULL, | |
| result, &dwLen); | |
| if (ret == 0) { | |
| strappend(ns, result); | |
| } | |
| dnsServer = dnsServer->Next; | |
| } | |
| // Add connection-specific search domains in addition to global one. | |
| suffix = adapter->DnsSuffix; | |
| if (suffix != NULL) { | |
| ret = WideCharToMultiByte(CP_UTF8, 0, suffix, -1, | |
| result, sizeof(result), NULL, NULL); | |
| if (ret != 0) { | |
| strappend(sl, result); | |
| } | |
| } | |
| } | |
| adapter = adapter->Next; | |
| } | |
| free(adapters); | |
| return STS_SL_FOUND & STS_NS_FOUND; | |
| } | |
| /* | |
| * Initialize JNI field IDs and classes. | |
| */ | |
| JNIEXPORT void JNICALL | |
| Java_sun_net_dns_ResolverConfigurationImpl_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: 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'; | |
| if (loadConfig(env, searchlist, nameservers) != STS_ERROR) { | |
| /* | |
| * Populate static fields in sun.net.DefaultResolverConfiguration | |
| */ | |
| 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); | |
| } | |
| } | |
| /* | |
| * 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; | |
| 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; | |
| } |