--- old/src/solaris/native/java/net/Inet6AddressImpl.c Fri Aug 30 15:43:44 2013 +++ new/src/solaris/native/java/net/Inet6AddressImpl.c Fri Aug 30 15:43:43 2013 @@ -33,7 +33,9 @@ #include #include #include -#ifdef _ALLBSD_SOURCE +#ifdef MACOSX +#include +#include #include /* gethostname */ #endif @@ -123,6 +125,156 @@ 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, "", "()V"); + ni_ia6ctrID = (*env)->GetMethodID(env, ni_ia6cls, "", "()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 @@ -147,18 +299,7 @@ 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, "", "()V"); - ni_ia6ctrID = (*env)->GetMethodID(env, ni_ia6cls, "", "()V"); - ni_ia6ipaddressID = (*env)->GetFieldID(env, ni_ia6cls, "ipaddress", "[B"); - initialized = 1; - } + if (!initialized) initializeInetClasses(env); if (IS_NULL(host)) { JNU_ThrowNullPointerException(env, "host is null"); @@ -167,6 +308,18 @@ 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) {