blob: 0bfe895de7ac20b9c3b7392bf0d0cf7ec5831063 [file] [log] [blame]
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08001/*
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
Mike Lockwoodb63989a2009-05-28 10:19:17 -040019#define LOG_DNS 0
20
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080021#include "JNIHelp.h"
22#include "utils/Log.h"
23#include "jni.h"
24
25#include <stdio.h>
26#include <string.h>
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080027#include <netdb.h>
28#include <errno.h>
29
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080030#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <sys/socket.h>
33
34
35static jclass byteArrayClass = NULL;
36
Brian Carlstrom44e0e562010-05-06 23:44:16 -070037static jstring InetAddress_gethostname(JNIEnv* env, jclass)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080038{
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
Mike Lockwoodb63989a2009-05-28 10:19:17 -040048#if LOG_DNS
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080049static 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}
Mike Lockwoodb63989a2009-05-28 10:19:17 -040061#else
Brian Carlstrom44e0e562010-05-06 23:44:16 -070062static inline void logIpString(struct addrinfo*, const char*)
Mike Lockwoodb63989a2009-05-28 10:19:17 -040063{
64}
65#endif
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080066
Elliott Hughes048303b2010-01-15 18:25:27 -080067static jobjectArray InetAddress_getaddrinfoImpl(JNIEnv* env, const char* name) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080068 struct addrinfo hints, *addressList = NULL, *addrInfo;
69 jobjectArray addressArray = NULL;
70
71 memset(&hints, 0, sizeof(hints));
Lorenzo Colitti723edb32009-08-18 16:14:33 -070072 hints.ai_family = AF_UNSPEC;
73 hints.ai_flags = AI_ADDRCONFIG;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080074 /*
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) {
Elliott Hughes7ca6fd02010-03-29 20:51:48 -070086 if (addrInfo->ai_family == AF_INET || addrInfo->ai_family == AF_INET6) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080087 addressCount++;
88 }
89 }
90
91 // Prepare output array.
92 addressArray = env->NewObjectArray(addressCount, byteArrayClass, NULL);
93 if (addressArray == NULL) {
94 // Appropriate exception will be thrown.
Elliott Hughes048303b2010-01-15 18:25:27 -080095 LOGE("getaddrinfo: could not allocate array of size %i", addressCount);
Elliott Hughes7ca6fd02010-03-29 20:51:48 -070096 freeaddrinfo(addressList);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080097 return NULL;
98 }
99
100 // Examine returned addresses one by one, save them in the output array.
101 int index = 0;
102 for (addrInfo = addressList; addrInfo; addrInfo = addrInfo->ai_next) {
103 struct sockaddr* address = addrInfo->ai_addr;
104 size_t addressLength = 0;
105 void* rawAddress;
106
107 switch (addrInfo->ai_family) {
108 // Find the raw address length and start pointer.
109 case AF_INET6:
110 addressLength = 16;
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700111 rawAddress = &((struct sockaddr_in6*) address)->sin6_addr.s6_addr;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800112 logIpString(addrInfo, name);
113 break;
114 case AF_INET:
115 addressLength = 4;
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700116 rawAddress = &((struct sockaddr_in*) address)->sin_addr.s_addr;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800117 logIpString(addrInfo, name);
118 break;
119 default:
120 // Unknown address family. Skip this address.
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700121 LOGE("getaddrinfo: Unknown address family %d", addrInfo->ai_family);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800122 continue;
123 }
124
125 // Convert each IP address into a Java byte array.
126 jbyteArray bytearray = env->NewByteArray(addressLength);
127 if (bytearray == NULL) {
128 // Out of memory error will be thrown on return.
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700129 LOGE("getaddrinfo: Can't allocate %d-byte array", addressLength);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800130 addressArray = NULL;
131 break;
132 }
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700133 env->SetByteArrayRegion(bytearray, 0, addressLength, (jbyte*) rawAddress);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800134 env->SetObjectArrayElement(addressArray, index, bytearray);
135 env->DeleteLocalRef(bytearray);
136 index++;
137 }
138 } else if (result == EAI_SYSTEM && errno == EACCES) {
139 /* No permission to use network */
Elliott Hughesda4f31d2010-01-28 13:43:39 -0800140 jniThrowException(env, "java/lang/SecurityException",
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800141 "Permission denied (maybe missing INTERNET permission)");
142 } else {
Elliott Hughes7ca6fd02010-03-29 20:51:48 -0700143 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(result));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800144 }
145
146 if (addressList) {
147 freeaddrinfo(addressList);
148 }
149
150 return addressArray;
151}
152
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700153jobjectArray InetAddress_getaddrinfo(JNIEnv* env, jclass, jstring javaName) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800154 if (javaName == NULL) {
Elliott Hughesda4f31d2010-01-28 13:43:39 -0800155 jniThrowNullPointerException(env, NULL);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800156 return NULL;
157 }
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800158 const char* name = env->GetStringUTFChars(javaName, NULL);
Elliott Hughes048303b2010-01-15 18:25:27 -0800159 jobjectArray out = InetAddress_getaddrinfoImpl(env, name);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800160 env->ReleaseStringUTFChars(javaName, name);
161 return out;
162}
163
164
Lorenzo Colittiaa479382009-08-26 18:41:06 -0700165/**
166 * Looks up the name corresponding to an IP address.
167 *
168 * @param javaAddress: a byte array containing the raw IP address bytes. Must be
169 * 4 or 16 bytes long.
170 * @return the hostname.
171 * @throws UnknownHostException: the IP address has no associated hostname.
172 */
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700173static jstring InetAddress_getnameinfo(JNIEnv* env, jclass,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800174 jbyteArray javaAddress)
175{
176 if (javaAddress == NULL) {
Elliott Hughesda4f31d2010-01-28 13:43:39 -0800177 jniThrowNullPointerException(env, NULL);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800178 return NULL;
179 }
180
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800181 // Convert the raw address bytes into a socket address structure.
182 struct sockaddr_storage ss;
Lorenzo Colittiaa479382009-08-26 18:41:06 -0700183 memset(&ss, 0, sizeof(ss));
Elliott Hughes44550df2009-09-08 19:44:54 -0700184
185 size_t socklen;
186 const size_t addressLength = env->GetArrayLength(javaAddress);
187 if (addressLength == 4) {
188 struct sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss);
189 sin->sin_family = AF_INET;
190 socklen = sizeof(struct sockaddr_in);
191 jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
192 env->GetByteArrayRegion(javaAddress, 0, 4, dst);
193 } else if (addressLength == 16) {
194 struct sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(&ss);
195 sin6->sin6_family = AF_INET6;
196 socklen = sizeof(struct sockaddr_in6);
197 jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
198 env->GetByteArrayRegion(javaAddress, 0, 16, dst);
199 } else {
200 // The caller already throws an exception in this case. Don't worry
201 // about it here.
202 return NULL;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800203 }
204
Lorenzo Colittiaa479382009-08-26 18:41:06 -0700205 // Look up the host name from the IP address.
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800206 char name[NI_MAXHOST];
Elliott Hughes44550df2009-09-08 19:44:54 -0700207 int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen,
208 name, sizeof(name), NULL, 0, NI_NAMEREQD);
209 if (ret != 0) {
210 jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret));
211 return NULL;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800212 }
213
Elliott Hughes44550df2009-09-08 19:44:54 -0700214 return env->NewStringUTF(name);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800215}
216
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800217static JNINativeMethod gMethods[] = {
Elliott Hughes048303b2010-01-15 18:25:27 -0800218 { "getaddrinfo", "(Ljava/lang/String;)[[B", (void*) InetAddress_getaddrinfo },
219 { "gethostname", "()Ljava/lang/String;", (void*) InetAddress_gethostname },
220 { "getnameinfo", "([B)Ljava/lang/String;", (void*) InetAddress_getnameinfo },
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800221};
Elliott Hughesc08f9fb2010-04-16 17:44:12 -0700222int register_java_net_InetAddress(JNIEnv* env) {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800223 jclass tempClass = env->FindClass("[B");
224 if (tempClass) {
225 byteArrayClass = (jclass) env->NewGlobalRef(tempClass);
226 }
227 if (!byteArrayClass) {
228 LOGE("register_java_net_InetAddress: cannot allocate byte array class");
229 return -1;
230 }
231 return jniRegisterNativeMethods(env, "java/net/InetAddress",
232 gMethods, NELEM(gMethods));
233}