OSDN Git Service

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