1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "InetAddress" 18 19 #define LOG_DNS 0 20 21 #include "JNIHelp.h" 22 #include "JniConstants.h" 23 #include "LocalArray.h" 24 #include "NetworkUtilities.h" 25 #include "ScopedLocalRef.h" 26 #include "ScopedUtfChars.h" 27 #include "jni.h" 28 #include "utils/Log.h" 29 30 #include <stdio.h> 31 #include <string.h> 32 #include <netdb.h> 33 #include <errno.h> 34 35 #include <netinet/in.h> 36 #include <arpa/inet.h> 37 #include <sys/socket.h> 38 39 static jstring InetAddress_gethostname(JNIEnv* env, jclass) 40 { 41 char name[256]; 42 int r = gethostname(name, 256); 43 if (r == 0) { 44 return env->NewStringUTF(name); 45 } else { 46 return NULL; 47 } 48 } 49 50 #if LOG_DNS 51 static void logIpString(addrinfo* ai, const char* name) 52 { 53 char ipString[INET6_ADDRSTRLEN]; 54 int result = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipString, 55 sizeof(ipString), NULL, 0, NI_NUMERICHOST); 56 if (result == 0) { 57 LOGD("%s: %s (family %d, proto %d)", name, ipString, ai->ai_family, 58 ai->ai_protocol); 59 } else { 60 LOGE("%s: getnameinfo: %s", name, gai_strerror(result)); 61 } 62 } 63 #else 64 static inline void logIpString(addrinfo*, const char*) 65 { 66 } 67 #endif 68 69 static jobjectArray InetAddress_getaddrinfo(JNIEnv* env, jclass, jstring javaName) { 70 ScopedUtfChars name(env, javaName); 71 if (name.c_str() == NULL) { 72 return NULL; 73 } 74 75 addrinfo hints; 76 memset(&hints, 0, sizeof(hints)); 77 hints.ai_family = AF_UNSPEC; 78 hints.ai_flags = AI_ADDRCONFIG; 79 /* 80 * If we don't specify a socket type, every address will appear twice, once 81 * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family 82 * anyway, just pick one. 83 */ 84 hints.ai_socktype = SOCK_STREAM; 85 86 addrinfo* addressList = NULL; 87 jobjectArray addressArray = NULL; 88 int result = getaddrinfo(name.c_str(), NULL, &hints, &addressList); 89 if (result == 0 && addressList) { 90 // Count results so we know how to size the output array. 91 int addressCount = 0; 92 for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { 93 if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { 94 addressCount++; 95 } 96 } 97 98 // Prepare output array. 99 addressArray = env->NewObjectArray(addressCount, JniConstants::byteArrayClass, NULL); 100 if (addressArray == NULL) { 101 // Appropriate exception will be thrown. 102 LOGE("getaddrinfo: could not allocate array of size %i", addressCount); 103 freeaddrinfo(addressList); 104 return NULL; 105 } 106 107 // Examine returned addresses one by one, save them in the output array. 108 int index = 0; 109 for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { 110 sockaddr* address = ai->ai_addr; 111 size_t addressLength = 0; 112 void* rawAddress; 113 114 switch (ai->ai_family) { 115 // Find the raw address length and start pointer. 116 case AF_INET6: 117 addressLength = 16; 118 rawAddress = &reinterpret_cast<sockaddr_in6*>(address)->sin6_addr.s6_addr; 119 logIpString(ai, name.c_str()); 120 break; 121 case AF_INET: 122 addressLength = 4; 123 rawAddress = &reinterpret_cast<sockaddr_in*>(address)->sin_addr.s_addr; 124 logIpString(ai, name.c_str()); 125 break; 126 default: 127 // Unknown address family. Skip this address. 128 LOGE("getaddrinfo: Unknown address family %d", ai->ai_family); 129 continue; 130 } 131 132 // Convert each IP address into a Java byte array. 133 ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(addressLength)); 134 if (byteArray.get() == NULL) { 135 // Out of memory error will be thrown on return. 136 LOGE("getaddrinfo: Can't allocate %d-byte array", addressLength); 137 addressArray = NULL; 138 break; 139 } 140 env->SetByteArrayRegion(byteArray.get(), 141 0, addressLength, reinterpret_cast<jbyte*>(rawAddress)); 142 env->SetObjectArrayElement(addressArray, index, byteArray.get()); 143 index++; 144 } 145 } else if (result == EAI_SYSTEM && errno == EACCES) { 146 /* No permission to use network */ 147 jniThrowException(env, "java/lang/SecurityException", 148 "Permission denied (maybe missing INTERNET permission)"); 149 } else { 150 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(result)); 151 } 152 153 if (addressList) { 154 freeaddrinfo(addressList); 155 } 156 157 return addressArray; 158 } 159 160 /** 161 * Looks up the name corresponding to an IP address. 162 * 163 * @param javaAddress: a byte array containing the raw IP address bytes. Must be 164 * 4 or 16 bytes long. 165 * @return the hostname. 166 * @throws UnknownHostException: the IP address has no associated hostname. 167 */ 168 static jstring InetAddress_getnameinfo(JNIEnv* env, jclass, 169 jbyteArray javaAddress) 170 { 171 if (javaAddress == NULL) { 172 jniThrowNullPointerException(env, NULL); 173 return NULL; 174 } 175 176 // Convert the raw address bytes into a socket address structure. 177 sockaddr_storage ss; 178 memset(&ss, 0, sizeof(ss)); 179 180 size_t socklen; 181 const size_t addressLength = env->GetArrayLength(javaAddress); 182 if (addressLength == 4) { 183 sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss); 184 sin->sin_family = AF_INET; 185 socklen = sizeof(sockaddr_in); 186 jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr); 187 env->GetByteArrayRegion(javaAddress, 0, 4, dst); 188 } else if (addressLength == 16) { 189 sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(&ss); 190 sin6->sin6_family = AF_INET6; 191 socklen = sizeof(sockaddr_in6); 192 jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr); 193 env->GetByteArrayRegion(javaAddress, 0, 16, dst); 194 } else { 195 // The caller already throws an exception in this case. Don't worry 196 // about it here. 197 return NULL; 198 } 199 200 // Look up the host name from the IP address. 201 char name[NI_MAXHOST]; 202 int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen, 203 name, sizeof(name), NULL, 0, NI_NAMEREQD); 204 if (ret != 0) { 205 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); 206 return NULL; 207 } 208 209 return env->NewStringUTF(name); 210 } 211 212 /** 213 * Convert a Java string representing an IP address to a Java byte array. 214 * The formats accepted are: 215 * - IPv4: 216 * - 1.2.3.4 217 * - 1.2.4 218 * - 1.4 219 * - 4 220 * - IPv6 221 * - Compressed form (2001:db8::1) 222 * - Uncompressed form (2001:db8:0:0:0:0:0:1) 223 * - IPv4-compatible (::192.0.2.0) 224 * - With an embedded IPv4 address (2001:db8::192.0.2.0). 225 * IPv6 addresses may appear in square brackets. 226 * 227 * @param addressByteArray the byte array to convert. 228 * @return a string with the textual representation of the address. 229 * @throws UnknownHostException the IP address was invalid. 230 */ 231 static jbyteArray InetAddress_ipStringToByteArray(JNIEnv* env, jobject, jstring javaString) { 232 // Convert the String to UTF-8 bytes. 233 ScopedUtfChars chars(env, javaString); 234 if (chars.c_str() == NULL) { 235 return NULL; 236 } 237 size_t byteCount = chars.size(); 238 LocalArray<INET6_ADDRSTRLEN> bytes(byteCount + 1); 239 char* ipString = &bytes[0]; 240 strcpy(ipString, chars.c_str()); 241 242 // Accept IPv6 addresses (only) in square brackets for compatibility. 243 if (ipString[0] == '[' && ipString[byteCount - 1] == ']' && strchr(ipString, ':') != NULL) { 244 memmove(ipString, ipString + 1, byteCount - 2); 245 ipString[byteCount - 2] = '\0'; 246 } 247 248 jbyteArray result = NULL; 249 addrinfo hints; 250 memset(&hints, 0, sizeof(hints)); 251 hints.ai_flags = AI_NUMERICHOST; 252 253 sockaddr_storage ss; 254 memset(&ss, 0, sizeof(ss)); 255 256 addrinfo* res = NULL; 257 int ret = getaddrinfo(ipString, NULL, &hints, &res); 258 if (ret == 0 && res) { 259 // Convert IPv4-mapped addresses to IPv4 addresses. 260 // The RI states "Java will never return an IPv4-mapped address". 261 sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(res->ai_addr); 262 if (res->ai_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 263 sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss); 264 sin->sin_family = AF_INET; 265 sin->sin_port = sin6->sin6_port; 266 memcpy(&sin->sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12], 4); 267 result = socketAddressToByteArray(env, &ss); 268 } else { 269 result = socketAddressToByteArray(env, reinterpret_cast<sockaddr_storage*>(res->ai_addr)); 270 } 271 } else { 272 // For backwards compatibility, deal with address formats that 273 // getaddrinfo does not support. For example, 1.2.3, 1.3, and even 3 are 274 // valid IPv4 addresses according to the Java API. If getaddrinfo fails, 275 // try to use inet_aton. 276 sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss); 277 if (inet_aton(ipString, &sin->sin_addr)) { 278 sin->sin_family = AF_INET; 279 sin->sin_port = 0; 280 result = socketAddressToByteArray(env, &ss); 281 } 282 } 283 284 if (res) { 285 freeaddrinfo(res); 286 } 287 288 if (!result) { 289 env->ExceptionClear(); 290 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); 291 } 292 293 return result; 294 } 295 296 static jstring InetAddress_byteArrayToIpString(JNIEnv* env, jobject, jbyteArray byteArray) { 297 if (byteArray == NULL) { 298 jniThrowNullPointerException(env, NULL); 299 return NULL; 300 } 301 sockaddr_storage ss; 302 if (!byteArrayToSocketAddress(env, NULL, byteArray, 0, &ss)) { 303 return NULL; 304 } 305 // TODO: getnameinfo seems to want its length parameter to be exactly 306 // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an 307 // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and 308 // then remove this hack. 309 int sa_size; 310 if (ss.ss_family == AF_INET) { 311 sa_size = sizeof(sockaddr_in); 312 } else if (ss.ss_family == AF_INET6) { 313 sa_size = sizeof(sockaddr_in6); 314 } else { 315 // byteArrayToSocketAddress already threw. 316 return NULL; 317 } 318 char ipString[INET6_ADDRSTRLEN]; 319 int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), sa_size, 320 ipString, sizeof(ipString), NULL, 0, NI_NUMERICHOST); 321 if (rc != 0) { 322 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(rc)); 323 return NULL; 324 } 325 return env->NewStringUTF(ipString); 326 } 327 328 static JNINativeMethod gMethods[] = { 329 NATIVE_METHOD(InetAddress, byteArrayToIpString, "([B)Ljava/lang/String;"), 330 NATIVE_METHOD(InetAddress, getaddrinfo, "(Ljava/lang/String;)[[B"), 331 NATIVE_METHOD(InetAddress, gethostname, "()Ljava/lang/String;"), 332 NATIVE_METHOD(InetAddress, getnameinfo, "([B)Ljava/lang/String;"), 333 NATIVE_METHOD(InetAddress, ipStringToByteArray, "(Ljava/lang/String;)[B"), 334 }; 335 int register_java_net_InetAddress(JNIEnv* env) { 336 return jniRegisterNativeMethods(env, "java/net/InetAddress", gMethods, NELEM(gMethods)); 337 } 338