src/solaris/native/java/net/Inet6AddressImpl.c

Print this page

        

*** 31,41 **** #include <netdb.h> #include <string.h> #include <strings.h> #include <stdlib.h> #include <ctype.h> ! #ifdef _ALLBSD_SOURCE #include <unistd.h> /* gethostname */ #endif #include "jvm.h" #include "jni_util.h" --- 31,43 ---- #include <netdb.h> #include <string.h> #include <strings.h> #include <stdlib.h> #include <ctype.h> ! #ifdef MACOSX ! #include <ifaddrs.h> ! #include <net/if.h> #include <unistd.h> /* gethostname */ #endif #include "jvm.h" #include "jni_util.h"
*** 121,130 **** --- 123,282 ---- static jmethodID ni_ia4ctrID; static jmethodID ni_ia6ctrID; static jfieldID ni_ia6ipaddressID; static int initialized = 0; + static void initializeInetClasses(JNIEnv *env) + { + if (!initialized) { + ni_iacls = (*env)->FindClass(env, "java/net/InetAddress"); + ni_iacls = (*env)->NewGlobalRef(env, ni_iacls); + ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address"); + ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls); + ni_ia6cls = (*env)->FindClass(env, "java/net/Inet6Address"); + ni_ia6cls = (*env)->NewGlobalRef(env, ni_ia6cls); + ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V"); + ni_ia6ctrID = (*env)->GetMethodID(env, ni_ia6cls, "<init>", "()V"); + ni_ia6ipaddressID = (*env)->GetFieldID(env, ni_ia6cls, "ipaddress", "[B"); + initialized = 1; + } + } + + #ifdef MACOSX + /* also called from Inet4AddressImpl.c */ + __private_extern__ jobjectArray + lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6) + { + jobjectArray result = NULL; + jboolean preferIPv6Address; + char myhostname[MAXHOSTNAMELEN+1]; + struct ifaddrs *ifa = NULL; + int familyOrder = 0; + int count = 0, i; + int addrs4 = 0, addrs6 = 0, numLoopbacks = 0; + jboolean includeLoopback = JNI_FALSE; + jobject name; + + // Make sure static variables we need are set. + if (!initialized) initializeInetClasses(env); + + /* get the address preference */ + preferIPv6Address = (*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID); + + /* If the requested name matches this host's hostname, return IP addresses + * from all attached interfaces. (#2844683 et al) This prevents undesired + * PPP dialup, but may return addresses that don't actually correspond to + * the name (if the name actually matches something in DNS etc. + */ + myhostname[0] = '\0'; + JVM_GetHostName(myhostname, MAXHOSTNAMELEN); + myhostname[MAXHOSTNAMELEN] = '\0'; + + if (strcmp(myhostname, hostname) != 0) { + // Non-self lookup + return NULL; + } + + if (getifaddrs(&ifa) != 0) { + NET_ThrowNew(env, errno, "Can't get local interface addresses"); + return NULL; + } + + name = (*env)->NewStringUTF(env, hostname); + + /* Iterate over the interfaces, and total up the number of IPv4 and IPv6 + * addresses we have. Also keep a count of loopback addresses. We need to + * exclude them in the normal case, but return them if we don't get an IP + * address. + */ + struct ifaddrs *iter = ifa; + while (iter) { + int family = iter->ifa_addr->sa_family; + if (iter->ifa_name[0] != '\0' && iter->ifa_addr) + { + jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK; + if (family == AF_INET) { + addrs4++; + if (isLoopback) numLoopbacks++; + } else if (family == AF_INET6 && includeV6) { + addrs6++; + if (isLoopback) numLoopbacks++; + } else { + /* We don't care e.g. AF_LINK */ + } + } + iter = iter->ifa_next; + } + + if (addrs4 == 1) { + // We don't have a real IP address, just loopback. We need to include + // loopback in our results. + includeLoopback = JNI_TRUE; + } + + /* Create and fill the Java array. */ + int arraySize = addrs4 + addrs6 - (includeLoopback ? 0 : numLoopbacks); + result = (*env)->NewObjectArray(env, arraySize, ni_iacls, NULL); // AWT_THREADING Safe (known object) + if (!result) goto done; + + if (preferIPv6Address) { + familyOrder = 1; + } + + /* Now we iterate twice over the list, once for IPv4 addresses and once for + * IPv6 addresses. preferIPv6Address property decides for us which + * addresses to look for first - IPv4 or IPv6. The familyOrder mod'ding + * helps reduce code a bit. + */ + for (i = 0; i < 2; i++, familyOrder++) { + int family; + if (familyOrder % 2) + family = AF_INET6; + else + family = AF_INET; + + if (family == AF_INET6 && !includeV6) + continue; // No need to send back IPv6 addresses + + // Now loop around the ifaddrs + iter = ifa; + while (iter != NULL) { + if (iter->ifa_name[0] != '\0' && iter->ifa_addr && + (family == iter->ifa_addr->sa_family)) + { + jboolean addAddress = JNI_FALSE; + jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK; + + // Non-loopback addresses always get added + if (!isLoopback) + addAddress = JNI_TRUE; + + // If we're a loopback address, we need to add it only if + // includeLoopback is set + if (isLoopback && includeLoopback) + addAddress = JNI_TRUE; + + if (addAddress) { + int port; + jobject o = NET_SockaddrToInetAddress(env, iter->ifa_addr, &port); + if (!o) goto done; // object allocation failed + (*env)->SetObjectArrayElement(env, result, count, o); + (*env)->DeleteLocalRef(env, o); + count++; + } + } + iter = iter->ifa_next; + } + } + + done: + freeifaddrs(ifa); + + return result; + } + #endif + /* * Find an internet address for a given hostname. Note that this * code only works for addresses of type INET. The translation * of %d.%d.%d.%d to an address (int) occurs in java now, so the * String "host" shouldn't *ever* be a %d.%d.%d.%d string
*** 145,174 **** int error=0; #ifdef AF_INET6 struct addrinfo hints, *res, *resNew = NULL; #endif /* AF_INET6 */ ! if (!initialized) { ! ni_iacls = (*env)->FindClass(env, "java/net/InetAddress"); ! ni_iacls = (*env)->NewGlobalRef(env, ni_iacls); ! ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address"); ! ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls); ! ni_ia6cls = (*env)->FindClass(env, "java/net/Inet6Address"); ! ni_ia6cls = (*env)->NewGlobalRef(env, ni_ia6cls); ! ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V"); ! ni_ia6ctrID = (*env)->GetMethodID(env, ni_ia6cls, "<init>", "()V"); ! ni_ia6ipaddressID = (*env)->GetFieldID(env, ni_ia6cls, "ipaddress", "[B"); ! initialized = 1; ! } if (IS_NULL(host)) { JNU_ThrowNullPointerException(env, "host is null"); return 0; } hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); CHECK_NULL_RETURN(hostname, NULL); #ifdef AF_INET6 static jfieldID ia_preferIPv6AddressID; if (ia_preferIPv6AddressID == NULL) { jclass c = (*env)->FindClass(env,"java/net/InetAddress"); if (c) { --- 297,327 ---- int error=0; #ifdef AF_INET6 struct addrinfo hints, *res, *resNew = NULL; #endif /* AF_INET6 */ ! if (!initialized) initializeInetClasses(env); if (IS_NULL(host)) { JNU_ThrowNullPointerException(env, "host is null"); return 0; } hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); CHECK_NULL_RETURN(hostname, NULL); + #ifdef MACOSX + /* If we're looking up the local machine, bypass DNS lookups and get + * address from getifaddrs. This ensures we get an IPv6 address for the + * local machine. + */ + ret = lookupIfLocalhost(env, hostname, JNI_TRUE); + if (ret != NULL) { + JNU_ReleaseStringPlatformChars(env, host, hostname); + return ret; + } + #endif + #ifdef AF_INET6 static jfieldID ia_preferIPv6AddressID; if (ia_preferIPv6AddressID == NULL) { jclass c = (*env)->FindClass(env,"java/net/InetAddress"); if (c) {