OSDN Git Service

Copy the correct number of bytes for IPv6 addresses in InetAddress_gethostbyaddr
[android-x86/dalvik.git] / libcore / luni / src / main / native / java_net_InetAddress.cpp
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 <assert.h>
28 #include <netdb.h>
29 #include <errno.h>
30
31 #include <cutils/properties.h>
32 #include <cutils/adb_networking.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <sys/socket.h>
36
37
38 static jclass byteArrayClass = NULL;
39
40 static jstring InetAddress_gethostname(JNIEnv* env, jobject obj)
41 {
42     char name[256];
43     int r = gethostname(name, 256);
44     if (r == 0) {
45         return env->NewStringUTF(name);
46     } else {
47         return NULL;
48     }
49 }
50
51 static void throwNullPointerException(JNIEnv* env)
52 {
53     const char* className = "java/lang/NullPointerException";
54
55     jclass exClass = env->FindClass(className);
56
57     if (exClass == NULL) {
58         LOGE("Unable to find class %s", className);
59     } else {
60         env->ThrowNew(exClass, NULL);
61     }
62 }
63
64 #if LOG_DNS
65 static void logIpString(struct addrinfo* ai, const char* name)
66 {
67     char ipString[INET6_ADDRSTRLEN];
68     int result = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipString,
69                              sizeof(ipString), NULL, 0, NI_NUMERICHOST);
70     if (result == 0) {
71         LOGD("%s: %s (family %d, proto %d)", name, ipString, ai->ai_family,
72              ai->ai_protocol);
73     } else {
74         LOGE("%s: getnameinfo: %s", name, gai_strerror(result));
75     }
76 }
77 #else
78 static inline void logIpString(struct addrinfo* ai, const char* name)
79 {
80 }
81 #endif
82
83 static jobjectArray getAllByNameUsingAdb(JNIEnv* env, const char* name)
84 {
85     struct in_addr outaddr;
86     jobjectArray addressArray = NULL;
87     jbyteArray byteArray;
88
89 #if 0
90     LOGI("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u",
91             err, (unsigned int)outaddr.a.s_addr,
92             outaddr.j[0],outaddr.j[1],
93             outaddr.j[2],outaddr.j[3]);
94 #endif
95
96     if (adb_networking_gethostbyname(name, &outaddr) >= 0) {
97         addressArray = env->NewObjectArray(1, byteArrayClass, NULL);
98         byteArray = env->NewByteArray(4);
99         if (addressArray && byteArray) {
100             env->SetByteArrayRegion(byteArray, 0, 4, (jbyte*) &outaddr.s_addr);
101             env->SetObjectArrayElement(addressArray, 1, byteArray);
102         }
103     }
104     return addressArray;
105 }
106
107 static jobjectArray getAllByNameUsingDns(JNIEnv* env, const char* name, 
108                                          jboolean preferIPv4Stack)
109 {
110     struct addrinfo hints, *addressList = NULL, *addrInfo;
111     jobjectArray addressArray = NULL;
112
113     memset(&hints, 0, sizeof(hints));
114     /*
115      * IPv4 only for now until the socket code supports IPv6; otherwise, the
116      * resolver will create two separate requests, one for IPv4 and one,
117      * currently unnecessary, for IPv6.
118      */
119     hints.ai_family = AF_INET;
120     /*
121      * If we don't specify a socket type, every address will appear twice, once
122      * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
123      * anyway, just pick one.
124      */
125     hints.ai_socktype = SOCK_STREAM;
126
127     int result = getaddrinfo(name, NULL, &hints, &addressList);
128     if (result == 0 && addressList) {
129         // Count results so we know how to size the output array.
130         int addressCount = 0;
131         for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) {
132             if (addrInfo->ai_family == AF_INET ||
133                 addrInfo->ai_family == AF_INET6) {
134                 addressCount++;
135             }
136         }
137
138         // Prepare output array.
139         addressArray = env->NewObjectArray(addressCount, byteArrayClass, NULL);
140         if (addressArray == NULL) {
141             // Appropriate exception will be thrown.
142             LOGE("getAllByNameUsingDns: could not allocate output array");
143             freeaddrinfo(addrInfo);
144             return NULL;
145         }
146
147         // Examine returned addresses one by one, save them in the output array.
148         int index = 0;
149         for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) {
150             struct sockaddr* address = addrInfo->ai_addr;
151             size_t addressLength = 0;
152             void* rawAddress;
153
154             switch (addrInfo->ai_family) {
155                 // Find the raw address length and start pointer.
156                 case AF_INET6:
157                     addressLength = 16;
158                     rawAddress =
159                         &((struct sockaddr_in6*) address)->sin6_addr.s6_addr;
160                     logIpString(addrInfo, name);
161                     break;
162                 case AF_INET:
163                     addressLength = 4;
164                     rawAddress =
165                         &((struct sockaddr_in*) address)->sin_addr.s_addr;
166                     logIpString(addrInfo, name);
167                     break;
168                 default:
169                     // Unknown address family. Skip this address.
170                     LOGE("getAllByNameUsingDns: Unknown address family %d",
171                          addrInfo->ai_family);
172                     continue;
173             }
174
175             // Convert each IP address into a Java byte array.
176             jbyteArray bytearray = env->NewByteArray(addressLength);
177             if (bytearray == NULL) {
178                 // Out of memory error will be thrown on return.
179                 LOGE("getAllByNameUsingDns: Can't allocate %d-byte array",
180                      addressLength);
181                 addressArray = NULL;
182                 break;
183             }
184             env->SetByteArrayRegion(bytearray, 0, addressLength,
185                                     (jbyte*) rawAddress);
186             env->SetObjectArrayElement(addressArray, index, bytearray);
187             env->DeleteLocalRef(bytearray);
188             index++;
189         }
190     } else if (result == EAI_SYSTEM && errno == EACCES) {
191         /* No permission to use network */
192         jniThrowException(
193             env, "java/lang/SecurityException",
194             "Permission denied (maybe missing INTERNET permission)");
195     } else {
196         // Do nothing. Return value will be null and the caller will throw an
197         // UnknownHostExeption.
198     }
199
200     if (addressList) {
201         freeaddrinfo(addressList);
202     }
203
204     return addressArray;
205 }
206
207 jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj,
208                                       jstring javaName,
209                                       jboolean preferIPv4Stack)
210 {
211     if (javaName == NULL) {
212         throwNullPointerException(env);
213         return NULL;
214     }
215
216     const char* name = env->GetStringUTFChars(javaName, NULL);
217     jobjectArray out = NULL;
218
219     char useAdbNetworkingProperty[PROPERTY_VALUE_MAX];
220     char adbConnected[PROPERTY_VALUE_MAX];
221     property_get("android.net.use-adb-networking",
222             useAdbNetworkingProperty, "");
223     property_get("adb.connected",
224             adbConnected, "");
225
226     // Any non-empty string value for use-adb-networking is considered "set"
227     if ((strlen(useAdbNetworkingProperty) > 0)
228             && (strlen(adbConnected) > 0) ) {
229         out = getAllByNameUsingAdb(env, name);
230     } else {
231         out = getAllByNameUsingDns(env, name, preferIPv4Stack);
232     }
233
234     if (!out) {
235         LOGI("Unknown host %s, throwing UnknownHostException", name);
236         jniThrowException(env, "java/net/UnknownHostException", name);
237     }
238     env->ReleaseStringUTFChars(javaName, name);
239     return out;
240 }
241
242
243 static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj,
244                                          jbyteArray javaAddress)
245 {
246     if (javaAddress == NULL) {
247         throwNullPointerException(env);
248         return NULL;
249     }
250
251     size_t addrlen = env->GetArrayLength(javaAddress);
252     jbyte* rawAddress = env->GetByteArrayElements(javaAddress, NULL);
253     if (rawAddress == NULL) {
254         throwNullPointerException(env);
255         return NULL;
256     }
257
258     // Convert the raw address bytes into a socket address structure.
259     struct sockaddr_storage ss;
260     struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
261     struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
262     size_t socklen;
263     switch (addrlen) {
264         case 4:
265             socklen = sizeof(struct sockaddr_in);
266             memset(sin, 0, sizeof(struct sockaddr_in));
267             sin->sin_family = AF_INET;
268             memcpy(&sin->sin_addr.s_addr, rawAddress, 4);
269             env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
270             break;
271         case 16:
272             socklen = sizeof(struct sockaddr_in6);
273             memset(sin6, 0, sizeof(struct sockaddr_in6));
274             sin6->sin6_family = AF_INET6;
275             memcpy(&sin6->sin6_addr.s6_addr, rawAddress, 16);
276             env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
277             break;
278         default:
279             env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
280             jniThrowException(env, "java/net/UnknownHostException",
281                                    "Invalid address length");
282             return NULL;
283     }
284
285
286     // Convert the socket address structure to an IP string for logging.
287     int ret;
288     char ipstr[INET6_ADDRSTRLEN];
289     ret = getnameinfo((struct sockaddr *) &ss, socklen, ipstr, sizeof(ipstr),
290                       NULL, 0, NI_NUMERICHOST);
291     if (ret) {
292         LOGE("gethostbyaddr: getnameinfo: %s", gai_strerror(ret));
293         return NULL;
294     }
295
296     // Look up the IP address from the socket address structure.
297     jstring result = NULL;
298     char name[NI_MAXHOST];
299     ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name),
300                       NULL, 0, 0);
301     if (ret == 0) {
302         LOGI("gethostbyaddr: getnameinfo: %s = %s", ipstr, name);
303         result = env->NewStringUTF(name);
304     } else {
305         LOGE("gethostbyaddr: getnameinfo: unknown host %s: %s", ipstr,
306              gai_strerror(ret));
307     }
308
309     return result;
310 }
311
312 /*
313  * JNI registration
314  */
315 static JNINativeMethod gMethods[] = {
316     /* name, signature, funcPtr */
317     { "gethostbyaddr",    "([B)Ljava/lang/String;",
318       (void*) InetAddress_gethostbyaddr },
319     { "getallbyname",     "(Ljava/lang/String;Z)[[B",
320       (void*) InetAddress_getallbyname },
321     { "gethostname",      "()Ljava/lang/String;",
322       (void*) InetAddress_gethostname  },
323 };
324
325 extern "C" int register_java_net_InetAddress(JNIEnv* env)
326 {
327     jclass tempClass = env->FindClass("[B");
328     if (tempClass) {
329         byteArrayClass = (jclass) env->NewGlobalRef(tempClass);
330     }
331     if (!byteArrayClass) {
332         LOGE("register_java_net_InetAddress: cannot allocate byte array class");
333         return -1;
334     }
335     return jniRegisterNativeMethods(env, "java/net/InetAddress",
336                 gMethods, NELEM(gMethods));
337 }