summaryrefslogtreecommitdiffstats
path: root/luni/src/main/native/java_net_InetAddress.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/main/native/java_net_InetAddress.cpp')
-rw-r--r--luni/src/main/native/java_net_InetAddress.cpp280
1 files changed, 178 insertions, 102 deletions
diff --git a/luni/src/main/native/java_net_InetAddress.cpp b/luni/src/main/native/java_net_InetAddress.cpp
index a7693cd..84c9751 100644
--- a/luni/src/main/native/java_net_InetAddress.cpp
+++ b/luni/src/main/native/java_net_InetAddress.cpp
@@ -33,6 +33,8 @@
#include <sys/socket.h>
+static jclass byteArrayClass = NULL;
+
static jstring InetAddress_gethostname(JNIEnv* env, jobject obj)
{
char name[256];
@@ -57,10 +59,24 @@ static void throwNullPointerException(JNIEnv* env)
}
}
-static jbyteArray getHostByNameAdb(JNIEnv* env, const char* name)
+static void logIpString(struct addrinfo* ai, const char* name)
+{
+ char ipString[INET6_ADDRSTRLEN];
+ int result = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipString,
+ sizeof(ipString), NULL, 0, NI_NUMERICHOST);
+ if (result == 0) {
+ LOGD("%s: %s (family %d, proto %d)", name, ipString, ai->ai_family,
+ ai->ai_protocol);
+ } else {
+ LOGE("%s: getnameinfo: %s", name, gai_strerror(result));
+ }
+}
+
+static jobjectArray getAllByNameUsingAdb(JNIEnv* env, const char* name)
{
struct in_addr outaddr;
- jbyteArray out = NULL;
+ jobjectArray addressArray = NULL;
+ jbyteArray byteArray;
#if 0
LOGI("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u",
@@ -70,156 +86,210 @@ static jbyteArray getHostByNameAdb(JNIEnv* env, const char* name)
#endif
if (adb_networking_gethostbyname(name, &outaddr) >= 0) {
- out = env->NewByteArray(4);
- env->SetByteArrayRegion(out, 0, 4, (jbyte*) &outaddr.s_addr);
+ addressArray = env->NewObjectArray(1, byteArrayClass, NULL);
+ byteArray = env->NewByteArray(4);
+ if (addressArray && byteArray) {
+ env->SetByteArrayRegion(byteArray, 0, 4, (jbyte*) &outaddr.s_addr);
+ env->SetObjectArrayElement(addressArray, 1, byteArray);
+ }
}
-
- return out;
+ return addressArray;
}
-static jbyteArray getHostByNameGetAddrInfo(JNIEnv* env, const char* name, jboolean preferIPv6Address)
+static jobjectArray getAllByNameUsingDns(JNIEnv* env, const char* name,
+ jboolean preferIPv4Stack)
{
- struct addrinfo hints, *res = NULL;
- jbyteArray out = NULL;
+ struct addrinfo hints, *addressList = NULL, *addrInfo;
+ jobjectArray addressArray = NULL;
memset(&hints, 0, sizeof(hints));
- hints.ai_family = preferIPv6Address ? AF_UNSPEC : AF_INET;
-
- int ret = getaddrinfo(name, NULL, &hints, &res);
- if (ret == 0 && res) {
- struct sockaddr* saddr = res[0].ai_addr;
- size_t addrlen = 0;
- void* rawaddr;
-
- switch (res[0].ai_family) {
- // Find the raw address length and start pointer.
- case AF_INET6:
- addrlen = 16;
- rawaddr = &((struct sockaddr_in6*) saddr)->sin6_addr.s6_addr;
- break;
- case AF_INET:
- addrlen = 4;
- rawaddr = &((struct sockaddr_in*) saddr)->sin_addr.s_addr;
- break;
- default:
- // Do nothing. addrlength = 0, so we will return NULL.
- break;
+ /*
+ * If we don't specify a socket type, every address will appear twice, once
+ * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
+ * anyway, just pick one.
+ */
+ hints.ai_family = preferIPv4Stack ? AF_INET : AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ int result = getaddrinfo(name, NULL, &hints, &addressList);
+ if (result == 0 && addressList) {
+ // Count results so we know how to size the output array.
+ int addressCount = 0;
+ for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) {
+ if (addrInfo->ai_family == AF_INET ||
+ addrInfo->ai_family == AF_INET6) {
+ addressCount++;
+ }
}
- if (addrlen) {
- out = env->NewByteArray(addrlen);
- env->SetByteArrayRegion(out, 0, addrlen, (jbyte*) rawaddr);
+ // Prepare output array.
+ addressArray = env->NewObjectArray(addressCount, byteArrayClass, NULL);
+ if (addressArray == NULL) {
+ // Appropriate exception will be thrown.
+ LOGE("getAllByNameUsingDns: could not allocate output array");
+ freeaddrinfo(addrInfo);
+ return NULL;
}
- } else if (ret == EAI_SYSTEM && errno == EACCES) {
+
+ // Examine returned addresses one by one, save them in the output array.
+ int index = 0;
+ for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) {
+ struct sockaddr* address = addrInfo->ai_addr;
+ size_t addressLength = 0;
+ void* rawAddress;
+
+ switch (addrInfo->ai_family) {
+ // Find the raw address length and start pointer.
+ case AF_INET6:
+ addressLength = 16;
+ rawAddress =
+ &((struct sockaddr_in6*) address)->sin6_addr.s6_addr;
+ logIpString(addrInfo, name);
+ break;
+ case AF_INET:
+ addressLength = 4;
+ rawAddress =
+ &((struct sockaddr_in*) address)->sin_addr.s_addr;
+ logIpString(addrInfo, name);
+ break;
+ default:
+ // Unknown address family. Skip this address.
+ LOGE("getAllByNameUsingDns: Unknown address family %d",
+ addrInfo->ai_family);
+ continue;
+ }
+
+ // Convert each IP address into a Java byte array.
+ jbyteArray bytearray = env->NewByteArray(addressLength);
+ if (bytearray == NULL) {
+ // Out of memory error will be thrown on return.
+ LOGE("getAllByNameUsingDns: Can't allocate %d-byte array",
+ addressLength);
+ addressArray = NULL;
+ break;
+ }
+ env->SetByteArrayRegion(bytearray, 0, addressLength,
+ (jbyte*) rawAddress);
+ env->SetObjectArrayElement(addressArray, index, bytearray);
+ env->DeleteLocalRef(bytearray);
+ index++;
+ }
+ } else if (result == EAI_SYSTEM && errno == EACCES) {
/* No permission to use network */
jniThrowException(
- env, "java/lang/SecurityException",
- "Permission denied (maybe missing INTERNET permission)");
+ env, "java/lang/SecurityException",
+ "Permission denied (maybe missing INTERNET permission)");
+ } else {
+ // Do nothing. Return value will be null and the caller will throw an
+ // UnknownHostExeption.
}
- if (res) {
- freeaddrinfo(res);
+ if (addressList) {
+ freeaddrinfo(addressList);
}
- return out;
+ return addressArray;
}
-jbyteArray InetAddress_gethostbyname(JNIEnv* env, jobject obj, jstring nameStr, jboolean preferIPv6Address)
+jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj,
+ jstring javaName,
+ jboolean preferIPv4Stack)
{
- if (nameStr == NULL) {
+ if (javaName == NULL) {
throwNullPointerException(env);
- return 0;
+ return NULL;
}
- const char* name = env->GetStringUTFChars(nameStr, NULL);
- jbyteArray out = NULL;
+ const char* name = env->GetStringUTFChars(javaName, NULL);
+ jobjectArray out = NULL;
char useAdbNetworkingProperty[PROPERTY_VALUE_MAX];
char adbConnected[PROPERTY_VALUE_MAX];
- property_get ("android.net.use-adb-networking",
+ property_get("android.net.use-adb-networking",
useAdbNetworkingProperty, "");
- property_get ("adb.connected",
+ property_get("adb.connected",
adbConnected, "");
// Any non-empty string value for use-adb-networking is considered "set"
if ((strlen(useAdbNetworkingProperty) > 0)
&& (strlen(adbConnected) > 0) ) {
- out = getHostByNameAdb(env, name);
+ out = getAllByNameUsingAdb(env, name);
} else {
- out = getHostByNameGetAddrInfo(env, name, preferIPv6Address);
+ out = getAllByNameUsingDns(env, name, preferIPv4Stack);
}
if (!out) {
LOGI("Unknown host %s, throwing UnknownHostException", name);
jniThrowException(env, "java/net/UnknownHostException", name);
}
- env->ReleaseStringUTFChars(nameStr, name);
+ env->ReleaseStringUTFChars(javaName, name);
return out;
}
-static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj, jstring addrStr)
+static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj,
+ jbyteArray javaAddress)
{
- if (addrStr == NULL) {
+ if (javaAddress == NULL) {
throwNullPointerException(env);
- return false;
- }
-
- jstring result;
- const char* addr = env->GetStringUTFChars(addrStr, NULL);
-
- struct hostent* ent = gethostbyaddr(addr, strlen(addr), AF_INET);
-
- if (ent != NULL && ent->h_name != NULL) {
- result = env->NewStringUTF(ent->h_name);
- } else {
- result = NULL;
+ return NULL;
}
- env->ReleaseStringUTFChars(addrStr, addr);
-
- return result;
-}
-
-
-static jobjectArray InetAddress_getaliasesbyname(JNIEnv* env, jobject obj, jstring nameStr)
-{
- if (nameStr == NULL) {
+ size_t addrlen = env->GetArrayLength(javaAddress);
+ jbyte* rawAddress = env->GetByteArrayElements(javaAddress, NULL);
+ if (rawAddress == NULL) {
throwNullPointerException(env);
return NULL;
}
- jclass clazz = env->FindClass("java/lang/String");
- if (clazz == NULL) {
- jniThrowException(env, "java/lang/ClassNotFoundException", "couldn't find class java.lang.String");
- return NULL;
+ // Convert the raw address bytes into a socket address structure.
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+ size_t socklen;
+ switch (addrlen) {
+ case 4:
+ socklen = sizeof(struct sockaddr_in);
+ memset(sin, 0, sizeof(struct sockaddr_in));
+ sin->sin_family = AF_INET;
+ memcpy(&sin->sin_addr.s_addr, rawAddress, 4);
+ break;
+ case 16:
+ socklen = sizeof(struct sockaddr_in6);
+ memset(sin6, 0, sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ memcpy(&sin6->sin6_addr.s6_addr, rawAddress, 4);
+ break;
+ default:
+ jniThrowException(env, "java/net/UnknownHostException",
+ "Invalid address length");
+ return NULL;
}
- jobjectArray result;
- const char* name = env->GetStringUTFChars(nameStr, NULL);
+ // Convert the socket address structure to an IP string for logging.
+ int ret;
+ char ipstr[INET6_ADDRSTRLEN];
+ ret = getnameinfo((struct sockaddr *) &ss, socklen, ipstr, sizeof(ipstr),
+ NULL, 0, NI_NUMERICHOST);
+ if (ret) {
+ LOGE("gethostbyaddr: getnameinfo: %s", gai_strerror(ret));
+ return NULL;
+ }
- struct hostent* ent = gethostbyname(name);
-
- if (ent != NULL) {
- // Count aliases
- int count = 0;
- while (ent->h_aliases[count] != NULL) {
- count++;
- }
-
- // Create an array of String objects and fill it.
- result = env->NewObjectArray(count, clazz, NULL);
- int i;
- for (i = 0; i < count; i++) {
- env->SetObjectArrayElement(result, i, env->NewStringUTF(ent->h_aliases[i]));
- }
+ // Look up the IP address from the socket address structure.
+ jstring result = NULL;
+ char name[NI_MAXHOST];
+ ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name),
+ NULL, 0, 0);
+ if (ret == 0) {
+ LOGI("gethostbyaddr: getnameinfo: %s = %s", ipstr, name);
+ result = env->NewStringUTF(name);
} else {
- result = env->NewObjectArray(0, clazz, NULL);
+ LOGE("gethostbyaddr: getnameinfo: unknown host %s: %s", ipstr,
+ gai_strerror(ret));
}
- env->ReleaseStringUTFChars(nameStr, name);
-
return result;
}
@@ -228,18 +298,24 @@ static jobjectArray InetAddress_getaliasesbyname(JNIEnv* env, jobject obj, jstri
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "getaliasesbyname", "(Ljava/lang/String;)[Ljava/lang/String;",
- (void*) InetAddress_getaliasesbyname },
- { "gethostbyaddr", "(Ljava/lang/String;)Ljava/lang/String;",
+ { "gethostbyaddr", "([B)Ljava/lang/String;",
(void*) InetAddress_gethostbyaddr },
- { "gethostbyname", "(Ljava/lang/String;Z)[B",
- (void*) InetAddress_gethostbyname },
+ { "getallbyname", "(Ljava/lang/String;Z)[[B",
+ (void*) InetAddress_getallbyname },
{ "gethostname", "()Ljava/lang/String;",
- (void*) InetAddress_gethostname }
+ (void*) InetAddress_gethostname },
};
extern "C" int register_java_net_InetAddress(JNIEnv* env)
{
+ jclass tempClass = env->FindClass("[B");
+ if (tempClass) {
+ byteArrayClass = (jclass) env->NewGlobalRef(tempClass);
+ }
+ if (!byteArrayClass) {
+ LOGE("register_java_net_InetAddress: cannot allocate byte array class");
+ return -1;
+ }
return jniRegisterNativeMethods(env, "java/net/InetAddress",
gMethods, NELEM(gMethods));
}