From b663ba5604080f14f682b2defed9bea57346d5b5 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 17 Sep 2009 15:01:47 -0700 Subject: [PATCH] Use native code to convert IP addresses to strings. - Add a byteArrayToIpString method to the INetworkSystem interface and implement it in native code - Fix the native code so it does better error reporting - Change InetAddress's getHostAddress, getHostName and getCanonicalHostName methods to use it and remove the IPv4-only Java implementations - Remove Inet6Util.createIPAddrStringFromByteArray - Fix InetAddress so getByAddress(null) throws UnknownHostException instead of NullPointerException for compatibility with the RI. Change-Id: I26548922e9eed63b295173456183c4ab3ce20718 --- .../luni/src/main/java/java/net/Inet4Address.java | 20 +- .../luni/src/main/java/java/net/Inet6Address.java | 13 +- .../luni/src/main/java/java/net/InetAddress.java | 83 ++++---- .../harmony/luni/platform/INetworkSystem.java | 5 +- .../harmony/luni/platform/OSNetworkSystem.java | 3 + .../org/apache/harmony/luni/util/Inet6Util.java | 49 ----- ...pache_harmony_luni_platform_OSNetworkSystem.cpp | 221 ++++++++++++++------- 7 files changed, 201 insertions(+), 193 deletions(-) diff --git a/libcore/luni/src/main/java/java/net/Inet4Address.java b/libcore/luni/src/main/java/java/net/Inet4Address.java index 5e1a420db..b467b6419 100644 --- a/libcore/luni/src/main/java/java/net/Inet4Address.java +++ b/libcore/luni/src/main/java/java/net/Inet4Address.java @@ -217,22 +217,10 @@ public final class Inet4Address extends InetAddress { return prefix >= 0xEFC0 && prefix <= 0xEFC3; } - /** - * Returns a textual representation of this IP address. - * - * @return the textual representation of this host address. - */ - @Override - public String getHostAddress() { - String hostAddress = ""; //$NON-NLS-1$ - for (int i = 0; i < 4; i++) { - hostAddress += ipaddress[i] & 255; - if (i != 3) { - hostAddress += "."; //$NON-NLS-1$ - } - } - return hostAddress; - } + // BEGIN android-removed + // public String getHostAddress() { + // } + // END android-removed // BEGIN android-removed // public int hashCode() { diff --git a/libcore/luni/src/main/java/java/net/Inet6Address.java b/libcore/luni/src/main/java/java/net/Inet6Address.java index 91ec48eff..f333770ad 100644 --- a/libcore/luni/src/main/java/java/net/Inet6Address.java +++ b/libcore/luni/src/main/java/java/net/Inet6Address.java @@ -362,15 +362,10 @@ public final class Inet6Address extends InetAddress { return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 8; } - /** - * Gets the textual representation of this IP address. - * - * @return the as a dotted string formatted IP address. - */ - @Override - public String getHostAddress() { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); - } + // BEGIN android-removed + // public String getHostAddress() { + // } + // END android-removed /** * Gets the scope id as a number if this address is linked to an interface. diff --git a/libcore/luni/src/main/java/java/net/InetAddress.java b/libcore/luni/src/main/java/java/net/InetAddress.java index a7ea3f6eb..0fa4796dd 100644 --- a/libcore/luni/src/main/java/java/net/InetAddress.java +++ b/libcore/luni/src/main/java/java/net/InetAddress.java @@ -301,14 +301,31 @@ public class InetAddress extends Object implements Serializable { return getAllByNameImpl(host, false)[0]; } + /* + * Convenience method to convert a byte array to a string, converting + * exceptions to runtime exceptions. This is used when passing in byte + * arrays that have been verified to be correct and is necessary because + * some methods, such as getHostName(), are not allowed to throw exceptions + * but are implemented using byteArrayToIpString(), which throws + * UnknownHostException. Exceptions should never occur when the address is + * valid, but they cannot be simply ignored because they could be due to + * runtime errors such as out-of-memory conditions. + */ + private static String ipAddressToString(byte[] ipaddress) { + try { + return NETIMPL.byteArrayToIpString(ipaddress); + } catch(UnknownHostException e) { + throw new RuntimeException(e); + } + } + /** * Gets the textual representation of this IP address. * - * @return the textual representation of this host address in form of a - * dotted string. + * @return the textual representation of host's IP address. */ public String getHostAddress() { - return inetNtoaImpl(bytesToInt(ipaddress, 0)); + return ipAddressToString(ipaddress); } /** @@ -325,18 +342,17 @@ public class InetAddress extends Object implements Serializable { if (ipaddress.length == 4) { address = bytesToInt(ipaddress, 0); if (address == 0) { - return hostName = inetNtoaImpl(address); + return hostName = ipAddressToString(ipaddress); } } hostName = getHostByAddrImpl(ipaddress).hostName; if (hostName.equals("localhost") && ipaddress.length == 4 //$NON-NLS-1$ && address != 0x7f000001) { - return hostName = inetNtoaImpl(address); + return hostName = ipAddressToString(ipaddress); } } } catch (UnknownHostException e) { - return hostName = Inet6Util - .createIPAddrStringFromByteArray(ipaddress); + return hostName = ipAddressToString(ipaddress); } SecurityManager security = System.getSecurityManager(); try { @@ -345,7 +361,7 @@ public class InetAddress extends Object implements Serializable { security.checkConnect(hostName, -1); } } catch (SecurityException e) { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); + return ipAddressToString(ipaddress); } return hostName; } @@ -365,12 +381,12 @@ public class InetAddress extends Object implements Serializable { if (ipaddress.length == 4) { address = bytesToInt(ipaddress, 0); if (address == 0) { - return inetNtoaImpl(address); + return ipAddressToString(ipaddress); } } canonicalName = getHostByAddrImpl(ipaddress).hostName; } catch (UnknownHostException e) { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); + return ipAddressToString(ipaddress); } SecurityManager security = System.getSecurityManager(); try { @@ -379,7 +395,7 @@ public class InetAddress extends Object implements Serializable { security.checkConnect(canonicalName, -1); } } catch (SecurityException e) { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); + return ipAddressToString(ipaddress); } return canonicalName; } @@ -550,10 +566,9 @@ public class InetAddress extends Object implements Serializable { private static native String gethostbyaddr(byte[] addr); // END android-changed - static int inetAddr(String host) throws UnknownHostException { - return (host.equals("255.255.255.255")) ? 0xFFFFFFFF //$NON-NLS-1$ - : inetAddrImpl(host); - } + // BEGIN android-removed + // static int inetAddr(String host) throws UnknownHostException + // END android-removed /** * Convert a string containing an IPv4 Internet Protocol dotted address into @@ -561,41 +576,17 @@ public class InetAddress extends Object implements Serializable { * exception, so this value should not be used as an argument. See also * inetAddr(String). */ - // BEGIN android-changed + // BEGIN android-removed // static native int inetAddrImpl(String host) throws UnknownHostException; - static int inetAddrImpl(String host) throws UnknownHostException { - // TODO Probably not exactly what we want, and also inefficient. Provide native later. - try { - String[] args = host.split("\\."); - - int a = Integer.parseInt(args[0]) << 24; - int b = Integer.parseInt(args[1]) << 16; - int c = Integer.parseInt(args[2]) << 8; - int d = Integer.parseInt(args[3]) ; - - return a | b | c | d; - } catch (Exception ex) { - throw new UnknownHostException(host); - } - } - // END android-changed + // END android-removed /** * Convert a binary address into a string containing an Ipv4 Internet * Protocol dotted address. */ - // BEGIN android-changed + // BEGIN android-removed // static native String inetNtoaImpl(int hipAddr); - static String inetNtoaImpl(int hipAddr) { - // TODO Inefficient and probably wrong. Provide proper (native?) implementation later. - int a = (hipAddr >> 24) & 0xFF; - int b = (hipAddr >> 16) & 0xFF; - int c = (hipAddr >> 8) & 0xFF; - int d = (hipAddr ) & 0xFF; - - return "" + a + "." + b + "." + c + "." + d; - } - // END android-changed + // END android-removed // BEGIN android-removed /** @@ -1223,7 +1214,11 @@ public class InetAddress extends Object implements Serializable { static InetAddress getByAddressInternal(String hostName, byte[] ipAddress, int scope_id) throws UnknownHostException { if (ipAddress == null) { - throw new NullPointerException(); + // We don't throw NullPointerException here for RI compatibility, + // but we do say "address is null" (K0331), instead of "addr is of + // illegal length". + throw new UnknownHostException( + Msg.getString("K0331", hostName)); //$NON-NLS-1$ } switch (ipAddress.length) { case 4: diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java index eae5261ca..13453417c 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java @@ -217,7 +217,10 @@ public interface INetworkSystem { throws UnknownHostException; public void setInetAddress(InetAddress sender, byte[] address); - + + public String byteArrayToIpString(byte[] address) + throws UnknownHostException; + // BEGIN android-removed // public boolean isReachableByICMP(InetAddress dest,InetAddress source,int ttl,int timeout); // END android-removed diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java index 70432a6a0..0fa25eaae 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java @@ -239,6 +239,9 @@ final class OSNetworkSystem implements INetworkSystem { return getSocketFlagsImpl(); } + public native String byteArrayToIpString(byte[] address) + throws UnknownHostException; + static native int getSocketFlagsImpl(); public InetAddress getSocketLocalAddress(FileDescriptor fd, diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java index d8b261d03..4dae1c3d9 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java @@ -144,55 +144,6 @@ public class Inet6Util { } - // BEGIN android-changed - static char[] hexCharacters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - // END android-changed - - public static String createIPAddrStringFromByteArray(byte ipByteArray[]) { - if (ipByteArray.length == 4) { - return addressToString(bytesToInt(ipByteArray, 0)); - } - - if (ipByteArray.length == 16) { - if (isIPv4MappedAddress(ipByteArray)) { - byte ipv4ByteArray[] = new byte[4]; - for (int i = 0; i < 4; i++) { - ipv4ByteArray[i] = ipByteArray[i + 12]; - } - return addressToString(bytesToInt(ipv4ByteArray, 0)); - } - StringBuilder buffer = new StringBuilder(); - // BEGIN android-changed - for (int i = 0; i < 8; i++) { // ipByteArray.length / 2 - - int num = (ipByteArray[2 * i] & 0xff) << 8; - num ^= ipByteArray[2 * i + 1] & 0xff; - - // count the digits to display without leading 0 - int count = 1, j = num; - while ((j >>>= 4) != 0) { - count++; - } - - char[] buf = new char[count]; - do { - int t = num & 0x0f; - buf[--count] = hexCharacters[t]; - num >>>= 4; - } while (count > 0); - - buffer.append(buf); - if ((i + 1) < 8) { // ipByteArray.length / 2 - buffer.append(":"); - } - } - // END android-changed - return buffer.toString(); - } - return null; - } - /** Converts a 4 character hex word into a 2 byte word equivalent */ public static void convertToBytes(String hexWord, byte ipByteArray[], int byteIndex) { diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp index d689fc797..909f21154 100644 --- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -333,53 +333,151 @@ static jobject socketAddressToInetAddress(JNIEnv *env, * @param port the port number * @param sockaddress the sockaddr_storage structure to write to * - * @return 0 on success, -1 on failure + * @return 0 on success, a system error code on failure * * @exception SocketError if the address family is unknown */ -static int inetAddressToSocketAddress(JNIEnv *env, - jobject inetaddress, int port, struct sockaddr_storage *sockaddress) { - - // Get the byte array that stores the IP address bytes in the InetAddress. - jbyteArray addressByteArray; - addressByteArray = (jbyteArray)env->GetObjectField(inetaddress, - gCachedFields.iaddr_ipaddress); +static int byteArrayToSocketAddress(JNIEnv *env, + jbyteArray addressByteArray, int port, sockaddr_storage *sockaddress) { if (addressByteArray == NULL) { throwNullPointerException(env); - return -1; + return EFAULT; } - - // Get the raw IP address bytes. - jbyte *addressBytes = env->GetByteArrayElements(addressByteArray, NULL); - if (addressBytes == NULL) { - throwNullPointerException(env); - return -1; - } - // Convert the IP address bytes to the proper IP address type. size_t addressLength = env->GetArrayLength(addressByteArray); - int result = 0; if (addressLength == 4) { // IPv4 address. - struct sockaddr_in *sin = (struct sockaddr_in *) sockaddress; - memset(sin, 0, sizeof(struct sockaddr_in)); + sockaddr_in *sin = (sockaddr_in *) sockaddress; + memset(sin, 0, sizeof(sockaddr_in)); sin->sin_family = AF_INET; sin->sin_port = htons(port); - memcpy(&sin->sin_addr.s_addr, addressBytes, 4); + jbyte *rawBytes = (jbyte *) &sin->sin_addr.s_addr; + env->GetByteArrayRegion(addressByteArray, 0, 4, rawBytes); } else if (addressLength == 16) { // IPv6 address. - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sockaddress; - memset(sin6, 0, sizeof(struct sockaddr_in6)); + sockaddr_in6 *sin6 = (sockaddr_in6 *) sockaddress; + memset(sin6, 0, sizeof(sockaddr_in6)); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); - memcpy(&sin6->sin6_addr.s6_addr, addressBytes, 16); + jbyte *rawBytes = (jbyte *) &sin6->sin6_addr.s6_addr; + env->GetByteArrayRegion(addressByteArray, 0, 16, rawBytes); } else { // Unknown address family. throwSocketException(env, SOCKERR_BADAF); - result = -1; + return EAFNOSUPPORT; } - env->ReleaseByteArrayElements(addressByteArray, addressBytes, 0); - return result; + return 0; +} + +/** + * Converts an InetAddress object and port number to a native address structure. + * Throws a NullPointerException or a SocketException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + * + * @param inetaddress the InetAddress object to convert + * @param port the port number + * @param sockaddress the sockaddr_storage structure to write to + * + * @return 0 on success, -1 on failure + * @throw UnknownHostException if any error occurs + * + * @exception SocketError if the address family is unknown + */ +static int inetAddressToSocketAddress(JNIEnv *env, + jobject inetaddress, int port, sockaddr_storage *sockaddress) { + + // Get the byte array that stores the IP address bytes in the InetAddress. + jbyteArray addressByteArray; + addressByteArray = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + return byteArrayToSocketAddress(env, addressByteArray, port, sockaddress); +} + +/** + * Convert a sockaddr_storage structure to a Java string. + * + * @param address pointer to sockaddr_storage structure to convert. + * @param withPort whether to include the port number in the output as well. + * + * @return 0 on success, a getnameinfo return code on failure. + * + * @throws SocketException the address family was unknown. + */ +static int socketAddressToString(sockaddr_storage *address, char *ipString, + int len, bool withPort) { + // TODO: getnameinfo seems to want its length parameter to be exactly + // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an + // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and + // then remove this hack. + int size; + if (address->ss_family == AF_INET) { + size = sizeof(sockaddr_in); + } else if (address->ss_family == AF_INET6) { + size = sizeof(sockaddr_in6); + } else { + errno = EAFNOSUPPORT; + return EAI_SYSTEM; + } + + char tmp[INET6_ADDRSTRLEN]; + int result = getnameinfo((sockaddr *)address, size, tmp, sizeof(tmp), NULL, + 0, NI_NUMERICHOST); + if (result != 0) { + return result; + } + + int port; + if (withPort) { + if (address->ss_family == AF_INET6) { + sockaddr_in6 *sin6 = (sockaddr_in6 *) address; + port = ntohs(sin6->sin6_port); + snprintf(ipString, len, "[%s]:%d", tmp, port); + } else { + sockaddr_in *sin = (sockaddr_in *) address; + port = ntohs(sin->sin_port); + snprintf(ipString, len, "%s:%d", tmp, port); + } + } else { + strncpy(ipString, tmp, len); + } + return 0; +} + +/** + * Convert a Java byte array representing an IP address to a Java string. + * + * @param addressByteArray the byte array to convert. + * + * @return a string with the textual representation of the address. + * + * @throws SocketException the address family was unknown. + */ +static jstring osNetworkSystem_byteArrayToIpString(JNIEnv *env, jclass clazz, + jbyteArray byteArray) { + // For compatibility, ensure that an UnknownHostException is thrown if the + // address is null. + if (byteArray == NULL) { + jniThrowException(env, "java/net/UnknownHostException", + strerror(EFAULT)); + return NULL; + } + struct sockaddr_storage ss; + int ret = byteArrayToSocketAddress(env, byteArray, 0, &ss); + if (ret) { + jniThrowException(env, "java/net/UnknownHostException", strerror(ret)); + return NULL; + } + char ipString[INET6_ADDRSTRLEN]; + ret = socketAddressToString(&ss, ipString, sizeof(ipString), false); + if (ret) { + env->ExceptionClear(); + jniThrowException(env, "java/net/UnknownHostException", + gai_strerror(ret)); + return NULL; + } + return env->NewStringUTF(ipString); } /** @@ -507,49 +605,6 @@ static bool useAdbNetworkingForAddress(struct sockaddr_storage *address) { } /** - * Convert a sockaddr_storage structure to a string for logging purposes. - * - * @param address pointer to sockaddr_storage structure to print - * - * @return a string with the textual representation of the address. - * - * @note Returns a statically allocated buffer, so is not thread-safe. - */ -static char *socketAddressToString(struct sockaddr_storage *address) { - static char invalidString[] = ""; - static char ipString[INET6_ADDRSTRLEN + sizeof("[]:65535")]; - - char tmp[INET6_ADDRSTRLEN]; - int port; - // TODO: getnameinfo seems to want its length parameter to be exactly - // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an - // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and - // then remove this hack. - int size = (address->ss_family == AF_INET) ? - sizeof(sockaddr_in) : sizeof(sockaddr_in6); - int result = getnameinfo((struct sockaddr *)address, - size, tmp, sizeof(tmp), NULL, 0, - NI_NUMERICHOST); - - if (result != 0) - return invalidString; - - if (address->ss_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) address; - port = ntohs(sin6->sin6_port); - sprintf(ipString, "[%s]:%d", tmp, port); - return ipString; - } else if (address->ss_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *) address; - port = ntohs(sin->sin_port); - sprintf(ipString, "%s:%d", tmp, port); - return ipString; - } else { - return invalidString; - } -} - -/** * Answer the errorString corresponding to the errorNumber, if available. * This function will answer a default error string, if the errorNumber is not * recognized. @@ -930,6 +985,23 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { } /** + * Converts an IPv4-mapped IPv6 address to an IPv4 address. Performs no error + * checking. + * + * @param address the address to convert. Must contain an IPv4-mapped address. + * @param outputAddress the converted address. Will contain an IPv4 address. + */ +static void convertMappedToIpv4(sockaddr_storage *address, + sockaddr_storage *outputAddress) { + memset(outputAddress, 0, sizeof(sockaddr_in)); + const sockaddr_in6 *sin6 = ((sockaddr_in6 *) address); + sockaddr_in *sin = ((sockaddr_in *) outputAddress); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; + sin->sin_port = sin6->sin6_port; +} + +/** * Converts an IPv4 address to an IPv4-mapped IPv6 address. Performs no error * checking. * @@ -937,9 +1009,9 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { * @param outputAddress the converted address. Will contain an IPv6 address. * @param mapUnspecified if true, convert 0.0.0.0 to ::ffff:0:0; if false, to :: */ -static void ipv4ToMappedAddress(struct sockaddr_storage *address, +static void convertIpv4ToMapped(struct sockaddr_storage *address, struct sockaddr_storage *outputAddress, bool mapUnspecified) { - memset(outputAddress, 0, sizeof(struct sockaddr_storage)); + memset(outputAddress, 0, sizeof(struct sockaddr_in6)); const struct sockaddr_in *sin = ((struct sockaddr_in *) address); struct sockaddr_in6 *sin6 = ((struct sockaddr_in6 *) outputAddress); sin6->sin6_family = AF_INET6; @@ -962,7 +1034,7 @@ static int doConnect(int socket, struct sockaddr_storage *socketAddress) { struct sockaddr_storage *realAddress; if (socketAddress->ss_family == AF_INET && getSocketAddressFamily(socket) == AF_INET6) { - ipv4ToMappedAddress(socketAddress, &mappedAddress, true); + convertIpv4ToMapped(socketAddress, &mappedAddress, true); realAddress = &mappedAddress; } else { realAddress = socketAddress; @@ -987,7 +1059,7 @@ static int doBind(int socket, struct sockaddr_storage *socketAddress) { struct sockaddr_storage *realAddress; if (socketAddress->ss_family == AF_INET && getSocketAddressFamily(socket) == AF_INET6) { - ipv4ToMappedAddress(socketAddress, &mappedAddress, false); + convertIpv4ToMapped(socketAddress, &mappedAddress, false); realAddress = &mappedAddress; } else { realAddress = socketAddress; @@ -3610,6 +3682,7 @@ static JNINativeMethod gMethods[] = { { "socketCloseImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_socketCloseImpl }, { "setInetAddressImpl", "(Ljava/net/InetAddress;[B)V", (void*) osNetworkSystem_setInetAddressImpl }, { "inheritedChannelImpl", "()Ljava/nio/channels/Channel;", (void*) osNetworkSystem_inheritedChannelImpl }, + { "byteArrayToIpString", "([B)Ljava/lang/String;", (void*) osNetworkSystem_byteArrayToIpString }, }; int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) { -- 2.11.0