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 "utils/Log.h" 23#include "jni.h" 24 25#include <stdio.h> 26#include <string.h> 27#include <netdb.h> 28#include <errno.h> 29 30#include <netinet/in.h> 31#include <arpa/inet.h> 32#include <sys/socket.h> 33 34 35static jclass byteArrayClass = NULL; 36 37static jstring InetAddress_gethostname(JNIEnv* env, jobject obj) 38{ 39 char name[256]; 40 int r = gethostname(name, 256); 41 if (r == 0) { 42 return env->NewStringUTF(name); 43 } else { 44 return NULL; 45 } 46} 47 48#if LOG_DNS 49static void logIpString(struct addrinfo* ai, const char* name) 50{ 51 char ipString[INET6_ADDRSTRLEN]; 52 int result = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipString, 53 sizeof(ipString), NULL, 0, NI_NUMERICHOST); 54 if (result == 0) { 55 LOGD("%s: %s (family %d, proto %d)", name, ipString, ai->ai_family, 56 ai->ai_protocol); 57 } else { 58 LOGE("%s: getnameinfo: %s", name, gai_strerror(result)); 59 } 60} 61#else 62static inline void logIpString(struct addrinfo* ai, const char* name) 63{ 64} 65#endif 66 67static jobjectArray InetAddress_getaddrinfoImpl(JNIEnv* env, const char* name) { 68 struct addrinfo hints, *addressList = NULL, *addrInfo; 69 jobjectArray addressArray = NULL; 70 71 memset(&hints, 0, sizeof(hints)); 72 hints.ai_family = AF_UNSPEC; 73 hints.ai_flags = AI_ADDRCONFIG; 74 /* 75 * If we don't specify a socket type, every address will appear twice, once 76 * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family 77 * anyway, just pick one. 78 */ 79 hints.ai_socktype = SOCK_STREAM; 80 81 int result = getaddrinfo(name, NULL, &hints, &addressList); 82 if (result == 0 && addressList) { 83 // Count results so we know how to size the output array. 84 int addressCount = 0; 85 for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) { 86 if (addrInfo->ai_family == AF_INET || 87 addrInfo->ai_family == AF_INET6) { 88 addressCount++; 89 } 90 } 91 92 // Prepare output array. 93 addressArray = env->NewObjectArray(addressCount, byteArrayClass, NULL); 94 if (addressArray == NULL) { 95 // Appropriate exception will be thrown. 96 LOGE("getaddrinfo: could not allocate array of size %i", addressCount); 97 freeaddrinfo(addrInfo); 98 return NULL; 99 } 100 101 // Examine returned addresses one by one, save them in the output array. 102 int index = 0; 103 for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) { 104 struct sockaddr* address = addrInfo->ai_addr; 105 size_t addressLength = 0; 106 void* rawAddress; 107 108 switch (addrInfo->ai_family) { 109 // Find the raw address length and start pointer. 110 case AF_INET6: 111 addressLength = 16; 112 rawAddress = 113 &((struct sockaddr_in6*) address)->sin6_addr.s6_addr; 114 logIpString(addrInfo, name); 115 break; 116 case AF_INET: 117 addressLength = 4; 118 rawAddress = 119 &((struct sockaddr_in*) address)->sin_addr.s_addr; 120 logIpString(addrInfo, name); 121 break; 122 default: 123 // Unknown address family. Skip this address. 124 LOGE("getaddrinfo: Unknown address family %d", 125 addrInfo->ai_family); 126 continue; 127 } 128 129 // Convert each IP address into a Java byte array. 130 jbyteArray bytearray = env->NewByteArray(addressLength); 131 if (bytearray == NULL) { 132 // Out of memory error will be thrown on return. 133 LOGE("getaddrinfo: Can't allocate %d-byte array", 134 addressLength); 135 addressArray = NULL; 136 break; 137 } 138 env->SetByteArrayRegion(bytearray, 0, addressLength, 139 (jbyte*) rawAddress); 140 env->SetObjectArrayElement(addressArray, index, bytearray); 141 env->DeleteLocalRef(bytearray); 142 index++; 143 } 144 } else if (result == EAI_SYSTEM && errno == EACCES) { 145 /* No permission to use network */ 146 jniThrowException(env, "java/lang/SecurityException", 147 "Permission denied (maybe missing INTERNET permission)"); 148 } else { 149 jniThrowException(env, "java/net/UnknownHostException", 150 gai_strerror(result)); 151 } 152 153 if (addressList) { 154 freeaddrinfo(addressList); 155 } 156 157 return addressArray; 158} 159 160jobjectArray InetAddress_getaddrinfo(JNIEnv* env, jobject obj, jstring javaName) { 161 if (javaName == NULL) { 162 jniThrowNullPointerException(env, NULL); 163 return NULL; 164 } 165 const char* name = env->GetStringUTFChars(javaName, NULL); 166 jobjectArray out = InetAddress_getaddrinfoImpl(env, name); 167 env->ReleaseStringUTFChars(javaName, name); 168 return out; 169} 170 171 172/** 173 * Looks up the name corresponding to an IP address. 174 * 175 * @param javaAddress: a byte array containing the raw IP address bytes. Must be 176 * 4 or 16 bytes long. 177 * @return the hostname. 178 * @throws UnknownHostException: the IP address has no associated hostname. 179 */ 180static jstring InetAddress_getnameinfo(JNIEnv* env, jobject obj, 181 jbyteArray javaAddress) 182{ 183 if (javaAddress == NULL) { 184 jniThrowNullPointerException(env, NULL); 185 return NULL; 186 } 187 188 // Convert the raw address bytes into a socket address structure. 189 struct sockaddr_storage ss; 190 memset(&ss, 0, sizeof(ss)); 191 192 size_t socklen; 193 const size_t addressLength = env->GetArrayLength(javaAddress); 194 if (addressLength == 4) { 195 struct sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss); 196 sin->sin_family = AF_INET; 197 socklen = sizeof(struct sockaddr_in); 198 jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr); 199 env->GetByteArrayRegion(javaAddress, 0, 4, dst); 200 } else if (addressLength == 16) { 201 struct sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(&ss); 202 sin6->sin6_family = AF_INET6; 203 socklen = sizeof(struct sockaddr_in6); 204 jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr); 205 env->GetByteArrayRegion(javaAddress, 0, 16, dst); 206 } else { 207 // The caller already throws an exception in this case. Don't worry 208 // about it here. 209 return NULL; 210 } 211 212 // Look up the host name from the IP address. 213 char name[NI_MAXHOST]; 214 int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen, 215 name, sizeof(name), NULL, 0, NI_NAMEREQD); 216 if (ret != 0) { 217 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); 218 return NULL; 219 } 220 221 return env->NewStringUTF(name); 222} 223 224/* 225 * JNI registration 226 */ 227static JNINativeMethod gMethods[] = { 228 /* name, signature, funcPtr */ 229 { "getaddrinfo", "(Ljava/lang/String;)[[B", (void*) InetAddress_getaddrinfo }, 230 { "gethostname", "()Ljava/lang/String;", (void*) InetAddress_gethostname }, 231 { "getnameinfo", "([B)Ljava/lang/String;", (void*) InetAddress_getnameinfo }, 232}; 233 234extern "C" int register_java_net_InetAddress(JNIEnv* env) { 235 jclass tempClass = env->FindClass("[B"); 236 if (tempClass) { 237 byteArrayClass = (jclass) env->NewGlobalRef(tempClass); 238 } 239 if (!byteArrayClass) { 240 LOGE("register_java_net_InetAddress: cannot allocate byte array class"); 241 return -1; 242 } 243 return jniRegisterNativeMethods(env, "java/net/InetAddress", 244 gMethods, NELEM(gMethods)); 245} 246