author | chegar |
Mon, 28 Jul 2008 13:02:11 +0100 | |
changeset 910 | 1f53246fb014 |
parent 715 | f16baef3a20e |
child 2056 | 115e09b7a004 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
715 | 2 |
* Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. Sun designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Sun in the LICENSE file that accompanied this code. |
|
10 |
* |
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
21 |
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 |
* CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 |
* have any questions. |
|
24 |
*/ |
|
25 |
||
26 |
#include <errno.h> |
|
27 |
#include <sys/types.h> |
|
28 |
#include <sys/socket.h> |
|
29 |
#include <netinet/in.h> |
|
30 |
#include <netdb.h> |
|
31 |
#include <string.h> |
|
32 |
#include <strings.h> |
|
33 |
#include <stdlib.h> |
|
34 |
#include <ctype.h> |
|
35 |
||
36 |
#include "jvm.h" |
|
37 |
#include "jni_util.h" |
|
38 |
#include "net_util.h" |
|
39 |
#ifndef IPV6_DEFS_H |
|
40 |
#include <netinet/icmp6.h> |
|
41 |
#endif |
|
42 |
||
43 |
#include "java_net_Inet4AddressImpl.h" |
|
44 |
#include "java_net_Inet6AddressImpl.h" |
|
45 |
||
46 |
/* the initial size of our hostent buffers */ |
|
47 |
#ifndef NI_MAXHOST |
|
48 |
#define NI_MAXHOST 1025 |
|
49 |
#endif |
|
50 |
||
51 |
#ifndef __GLIBC__ |
|
52 |
/* gethostname() is in libc.so but I can't find a header file for it */ |
|
53 |
extern int gethostname(char *buf, int buf_len); |
|
54 |
#endif |
|
55 |
||
56 |
/************************************************************************ |
|
57 |
* Inet6AddressImpl |
|
58 |
*/ |
|
59 |
||
60 |
/* |
|
61 |
* Class: java_net_Inet6AddressImpl |
|
62 |
* Method: getLocalHostName |
|
63 |
* Signature: ()Ljava/lang/String; |
|
64 |
*/ |
|
65 |
JNIEXPORT jstring JNICALL |
|
66 |
Java_java_net_Inet6AddressImpl_getLocalHostName(JNIEnv *env, jobject this) { |
|
67 |
char hostname[NI_MAXHOST+1]; |
|
68 |
||
69 |
hostname[0] = '\0'; |
|
70 |
if (JVM_GetHostName(hostname, MAXHOSTNAMELEN)) { |
|
71 |
/* Something went wrong, maybe networking is not setup? */ |
|
72 |
strcpy(hostname, "localhost"); |
|
73 |
} else { |
|
74 |
#ifdef __linux__ |
|
75 |
/* On Linux gethostname() says "host.domain.sun.com". On |
|
76 |
* Solaris gethostname() says "host", so extra work is needed. |
|
77 |
*/ |
|
78 |
#else |
|
79 |
/* Solaris doesn't want to give us a fully qualified domain name. |
|
80 |
* We do a reverse lookup to try and get one. This works |
|
81 |
* if DNS occurs before NIS in /etc/resolv.conf, but fails |
|
82 |
* if NIS comes first (it still gets only a partial name). |
|
83 |
* We use thread-safe system calls. |
|
84 |
*/ |
|
85 |
#ifdef AF_INET6 |
|
86 |
if (NET_addrtransAvailable()) { |
|
87 |
struct addrinfo hints, *res; |
|
88 |
int error; |
|
89 |
||
90 |
bzero(&hints, sizeof(hints)); |
|
91 |
hints.ai_flags = AI_CANONNAME; |
|
92 |
hints.ai_family = AF_UNSPEC; |
|
93 |
||
94 |
error = (*getaddrinfo_ptr)(hostname, NULL, &hints, &res); |
|
95 |
||
96 |
if (error == 0) { |
|
97 |
/* host is known to name service */ |
|
98 |
error = (*getnameinfo_ptr)(res->ai_addr, |
|
99 |
res->ai_addrlen, |
|
100 |
hostname, |
|
101 |
NI_MAXHOST, |
|
102 |
NULL, |
|
103 |
0, |
|
104 |
NI_NAMEREQD); |
|
105 |
||
106 |
/* if getnameinfo fails hostname is still the value |
|
107 |
from gethostname */ |
|
108 |
||
109 |
(*freeaddrinfo_ptr)(res); |
|
110 |
} |
|
111 |
} |
|
112 |
#endif /* AF_INET6 */ |
|
113 |
#endif /* __linux__ */ |
|
114 |
} |
|
115 |
return (*env)->NewStringUTF(env, hostname); |
|
116 |
} |
|
117 |
||
118 |
static jclass ni_iacls; |
|
119 |
static jclass ni_ia4cls; |
|
120 |
static jclass ni_ia6cls; |
|
121 |
static jmethodID ni_ia4ctrID; |
|
122 |
static jmethodID ni_ia6ctrID; |
|
123 |
static jfieldID ni_iaaddressID; |
|
124 |
static jfieldID ni_iahostID; |
|
125 |
static jfieldID ni_iafamilyID; |
|
126 |
static jfieldID ni_ia6ipaddressID; |
|
127 |
static int initialized = 0; |
|
128 |
||
129 |
/* |
|
130 |
* Find an internet address for a given hostname. Not this this |
|
131 |
* code only works for addresses of type INET. The translation |
|
132 |
* of %d.%d.%d.%d to an address (int) occurs in java now, so the |
|
133 |
* String "host" shouldn't *ever* be a %d.%d.%d.%d string |
|
134 |
* |
|
135 |
* Class: java_net_Inet6AddressImpl |
|
136 |
* Method: lookupAllHostAddr |
|
137 |
* Signature: (Ljava/lang/String;)[[B |
|
138 |
*/ |
|
139 |
||
140 |
JNIEXPORT jobjectArray JNICALL |
|
141 |
Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, |
|
142 |
jstring host) { |
|
143 |
const char *hostname; |
|
144 |
jobjectArray ret = 0; |
|
145 |
int retLen = 0; |
|
146 |
jboolean preferIPv6Address; |
|
147 |
||
148 |
int error=0; |
|
149 |
#ifdef AF_INET6 |
|
150 |
struct addrinfo hints, *res, *resNew = NULL; |
|
151 |
#endif /* AF_INET6 */ |
|
152 |
||
153 |
if (!initialized) { |
|
154 |
ni_iacls = (*env)->FindClass(env, "java/net/InetAddress"); |
|
155 |
ni_iacls = (*env)->NewGlobalRef(env, ni_iacls); |
|
156 |
ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address"); |
|
157 |
ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls); |
|
158 |
ni_ia6cls = (*env)->FindClass(env, "java/net/Inet6Address"); |
|
159 |
ni_ia6cls = (*env)->NewGlobalRef(env, ni_ia6cls); |
|
160 |
ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V"); |
|
161 |
ni_ia6ctrID = (*env)->GetMethodID(env, ni_ia6cls, "<init>", "()V"); |
|
162 |
ni_iaaddressID = (*env)->GetFieldID(env, ni_iacls, "address", "I"); |
|
163 |
ni_iafamilyID = (*env)->GetFieldID(env, ni_iacls, "family", "I"); |
|
164 |
ni_iahostID = (*env)->GetFieldID(env, ni_iacls, "hostName", "Ljava/lang/String;"); |
|
165 |
ni_ia6ipaddressID = (*env)->GetFieldID(env, ni_ia6cls, "ipaddress", "[B"); |
|
166 |
initialized = 1; |
|
167 |
} |
|
168 |
||
169 |
if (IS_NULL(host)) { |
|
170 |
JNU_ThrowNullPointerException(env, "host is null"); |
|
171 |
return 0; |
|
172 |
} |
|
173 |
hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); |
|
174 |
CHECK_NULL_RETURN(hostname, NULL); |
|
175 |
||
176 |
#ifdef AF_INET6 |
|
177 |
if (NET_addrtransAvailable()) { |
|
178 |
static jfieldID ia_preferIPv6AddressID; |
|
179 |
if (ia_preferIPv6AddressID == NULL) { |
|
180 |
jclass c = (*env)->FindClass(env,"java/net/InetAddress"); |
|
181 |
if (c) { |
|
182 |
ia_preferIPv6AddressID = |
|
183 |
(*env)->GetStaticFieldID(env, c, "preferIPv6Address", "Z"); |
|
184 |
} |
|
185 |
if (ia_preferIPv6AddressID == NULL) { |
|
186 |
JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
187 |
return NULL; |
|
188 |
} |
|
189 |
} |
|
190 |
/* get the address preference */ |
|
191 |
preferIPv6Address |
|
192 |
= (*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID); |
|
193 |
||
194 |
/* Try once, with our static buffer. */ |
|
195 |
bzero(&hints, sizeof(hints)); |
|
196 |
hints.ai_flags = AI_CANONNAME; |
|
197 |
hints.ai_family = AF_UNSPEC; |
|
198 |
||
199 |
/* |
|
200 |
* Workaround for Solaris bug 4160367 - if a hostname contains a |
|
201 |
* white space then 0.0.0.0 is returned |
|
202 |
*/ |
|
203 |
if (isspace(hostname[0])) { |
|
204 |
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", |
|
205 |
(char *)hostname); |
|
206 |
JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
207 |
return NULL; |
|
208 |
} |
|
209 |
||
210 |
error = (*getaddrinfo_ptr)(hostname, NULL, &hints, &res); |
|
211 |
||
212 |
if (error) { |
|
213 |
/* report error */ |
|
214 |
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", |
|
215 |
(char *)hostname); |
|
216 |
JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
217 |
return NULL; |
|
218 |
} else { |
|
219 |
int i = 0; |
|
220 |
int inetCount = 0, inet6Count = 0, inetIndex, inet6Index; |
|
910 | 221 |
struct addrinfo *itr, *last = NULL, *iterator = res; |
2 | 222 |
while (iterator != NULL) { |
223 |
int skip = 0; |
|
224 |
itr = resNew; |
|
225 |
while (itr != NULL) { |
|
226 |
if (iterator->ai_family == itr->ai_family && |
|
227 |
iterator->ai_addrlen == itr->ai_addrlen) { |
|
228 |
if (itr->ai_family == AF_INET) { /* AF_INET */ |
|
229 |
struct sockaddr_in *addr1, *addr2; |
|
230 |
addr1 = (struct sockaddr_in *)iterator->ai_addr; |
|
231 |
addr2 = (struct sockaddr_in *)itr->ai_addr; |
|
232 |
if (addr1->sin_addr.s_addr == |
|
233 |
addr2->sin_addr.s_addr) { |
|
234 |
skip = 1; |
|
235 |
break; |
|
236 |
} |
|
237 |
} else { |
|
238 |
int t; |
|
239 |
struct sockaddr_in6 *addr1, *addr2; |
|
240 |
addr1 = (struct sockaddr_in6 *)iterator->ai_addr; |
|
241 |
addr2 = (struct sockaddr_in6 *)itr->ai_addr; |
|
242 |
||
243 |
for (t = 0; t < 16; t++) { |
|
244 |
if (addr1->sin6_addr.s6_addr[t] != |
|
245 |
addr2->sin6_addr.s6_addr[t]) { |
|
246 |
break; |
|
247 |
} |
|
248 |
} |
|
249 |
if (t < 16) { |
|
250 |
itr = itr->ai_next; |
|
251 |
continue; |
|
252 |
} else { |
|
253 |
skip = 1; |
|
254 |
break; |
|
255 |
} |
|
256 |
} |
|
257 |
} else if (iterator->ai_family != AF_INET && |
|
258 |
iterator->ai_family != AF_INET6) { |
|
259 |
/* we can't handle other family types */ |
|
260 |
skip = 1; |
|
261 |
break; |
|
262 |
} |
|
263 |
itr = itr->ai_next; |
|
264 |
} |
|
265 |
||
266 |
if (!skip) { |
|
267 |
struct addrinfo *next |
|
268 |
= (struct addrinfo*) malloc(sizeof(struct addrinfo)); |
|
269 |
if (!next) { |
|
270 |
JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); |
|
271 |
ret = NULL; |
|
272 |
goto cleanupAndReturn; |
|
273 |
} |
|
274 |
memcpy(next, iterator, sizeof(struct addrinfo)); |
|
275 |
next->ai_next = NULL; |
|
276 |
if (resNew == NULL) { |
|
277 |
resNew = next; |
|
278 |
} else { |
|
279 |
last->ai_next = next; |
|
280 |
} |
|
281 |
last = next; |
|
282 |
i++; |
|
283 |
if (iterator->ai_family == AF_INET) { |
|
284 |
inetCount ++; |
|
285 |
} else if (iterator->ai_family == AF_INET6) { |
|
286 |
inet6Count ++; |
|
287 |
} |
|
288 |
} |
|
289 |
iterator = iterator->ai_next; |
|
290 |
} |
|
291 |
retLen = i; |
|
292 |
iterator = resNew; |
|
293 |
||
294 |
ret = (*env)->NewObjectArray(env, retLen, ni_iacls, NULL); |
|
295 |
||
296 |
if (IS_NULL(ret)) { |
|
297 |
/* we may have memory to free at the end of this */ |
|
298 |
goto cleanupAndReturn; |
|
299 |
} |
|
300 |
||
301 |
if (preferIPv6Address) { |
|
302 |
/* AF_INET addresses will be offset by inet6Count */ |
|
303 |
inetIndex = inet6Count; |
|
304 |
inet6Index = 0; |
|
305 |
} else { |
|
306 |
/* AF_INET6 addresses will be offset by inetCount */ |
|
307 |
inetIndex = 0; |
|
308 |
inet6Index = inetCount; |
|
309 |
} |
|
310 |
||
311 |
while (iterator != NULL) { |
|
312 |
if (iterator->ai_family == AF_INET) { |
|
313 |
jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID); |
|
314 |
if (IS_NULL(iaObj)) { |
|
315 |
ret = NULL; |
|
316 |
goto cleanupAndReturn; |
|
317 |
} |
|
318 |
(*env)->SetIntField(env, iaObj, ni_iaaddressID, |
|
319 |
ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr)); |
|
707
4e060643fb54
6698625: InetAddress.getLocalHost() failed in returning chinese local host name
chegar
parents:
2
diff
changeset
|
320 |
(*env)->SetObjectField(env, iaObj, ni_iahostID, host); |
2 | 321 |
(*env)->SetObjectArrayElement(env, ret, inetIndex, iaObj); |
322 |
inetIndex++; |
|
323 |
} else if (iterator->ai_family == AF_INET6) { |
|
324 |
jint scope = 0; |
|
325 |
jbyteArray ipaddress; |
|
326 |
||
327 |
jobject iaObj = (*env)->NewObject(env, ni_ia6cls, ni_ia6ctrID); |
|
328 |
if (IS_NULL(iaObj)) { |
|
329 |
ret = NULL; |
|
330 |
goto cleanupAndReturn; |
|
331 |
} |
|
332 |
ipaddress = (*env)->NewByteArray(env, 16); |
|
333 |
if (IS_NULL(ipaddress)) { |
|
334 |
ret = NULL; |
|
335 |
goto cleanupAndReturn; |
|
336 |
} |
|
337 |
(*env)->SetByteArrayRegion(env, ipaddress, 0, 16, |
|
338 |
(jbyte *)&(((struct sockaddr_in6*)iterator->ai_addr)->sin6_addr)); |
|
339 |
#ifdef __linux__ |
|
340 |
if (!kernelIsV22()) { |
|
341 |
scope = ((struct sockaddr_in6*)iterator->ai_addr)->sin6_scope_id; |
|
342 |
} |
|
343 |
#else |
|
344 |
scope = ((struct sockaddr_in6*)iterator->ai_addr)->sin6_scope_id; |
|
345 |
#endif |
|
346 |
if (scope != 0) { /* zero is default value, no need to set */ |
|
347 |
(*env)->SetIntField(env, iaObj, ia6_scopeidID, scope); |
|
348 |
(*env)->SetBooleanField(env, iaObj, ia6_scopeidsetID, JNI_TRUE); |
|
349 |
} |
|
350 |
(*env)->SetObjectField(env, iaObj, ni_ia6ipaddressID, ipaddress); |
|
707
4e060643fb54
6698625: InetAddress.getLocalHost() failed in returning chinese local host name
chegar
parents:
2
diff
changeset
|
351 |
(*env)->SetObjectField(env, iaObj, ni_iahostID, host); |
2 | 352 |
(*env)->SetObjectArrayElement(env, ret, inet6Index, iaObj); |
353 |
inet6Index++; |
|
354 |
} |
|
355 |
iterator = iterator->ai_next; |
|
356 |
} |
|
357 |
} |
|
358 |
} |
|
359 |
||
360 |
#endif /* AF_INET6 */ |
|
361 |
||
362 |
cleanupAndReturn: |
|
363 |
{ |
|
364 |
struct addrinfo *iterator, *tmp; |
|
365 |
iterator = resNew; |
|
366 |
while (iterator != NULL) { |
|
367 |
tmp = iterator; |
|
368 |
iterator = iterator->ai_next; |
|
369 |
free(tmp); |
|
370 |
} |
|
371 |
JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
372 |
} |
|
373 |
||
374 |
#ifdef AF_INET6 |
|
375 |
if (NET_addrtransAvailable()) |
|
376 |
(*freeaddrinfo_ptr)(res); |
|
377 |
#endif /* AF_INET6 */ |
|
378 |
||
379 |
return ret; |
|
380 |
} |
|
381 |
||
382 |
/* |
|
383 |
* Class: java_net_Inet6AddressImpl |
|
384 |
* Method: getHostByAddr |
|
385 |
* Signature: (I)Ljava/lang/String; |
|
386 |
*/ |
|
387 |
JNIEXPORT jstring JNICALL |
|
388 |
Java_java_net_Inet6AddressImpl_getHostByAddr(JNIEnv *env, jobject this, |
|
389 |
jbyteArray addrArray) { |
|
390 |
||
391 |
jstring ret = NULL; |
|
392 |
||
393 |
#ifdef AF_INET6 |
|
394 |
char host[NI_MAXHOST+1]; |
|
395 |
int error = 0; |
|
396 |
int len = 0; |
|
397 |
jbyte caddr[16]; |
|
398 |
||
399 |
if (NET_addrtransAvailable()) { |
|
400 |
struct sockaddr_in him4; |
|
401 |
struct sockaddr_in6 him6; |
|
402 |
struct sockaddr *sa; |
|
403 |
||
404 |
/* |
|
405 |
* For IPv4 addresses construct a sockaddr_in structure. |
|
406 |
*/ |
|
407 |
if ((*env)->GetArrayLength(env, addrArray) == 4) { |
|
408 |
jint addr; |
|
409 |
(*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr); |
|
410 |
addr = ((caddr[0]<<24) & 0xff000000); |
|
411 |
addr |= ((caddr[1] <<16) & 0xff0000); |
|
412 |
addr |= ((caddr[2] <<8) & 0xff00); |
|
413 |
addr |= (caddr[3] & 0xff); |
|
414 |
memset((char *) &him4, 0, sizeof(him4)); |
|
415 |
him4.sin_addr.s_addr = (uint32_t) htonl(addr); |
|
416 |
him4.sin_family = AF_INET; |
|
417 |
sa = (struct sockaddr *) &him4; |
|
418 |
len = sizeof(him4); |
|
419 |
} else { |
|
420 |
/* |
|
421 |
* For IPv6 address construct a sockaddr_in6 structure. |
|
422 |
*/ |
|
423 |
(*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr); |
|
424 |
memset((char *) &him6, 0, sizeof(him6)); |
|
425 |
memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) ); |
|
426 |
him6.sin6_family = AF_INET6; |
|
427 |
sa = (struct sockaddr *) &him6 ; |
|
428 |
len = sizeof(him6) ; |
|
429 |
} |
|
430 |
||
431 |
error = (*getnameinfo_ptr)(sa, len, host, NI_MAXHOST, NULL, 0, |
|
432 |
NI_NAMEREQD); |
|
433 |
||
434 |
if (!error) { |
|
435 |
ret = (*env)->NewStringUTF(env, host); |
|
436 |
} |
|
437 |
} |
|
438 |
#endif /* AF_INET6 */ |
|
439 |
||
440 |
if (ret == NULL) { |
|
441 |
JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL); |
|
442 |
} |
|
443 |
||
444 |
return ret; |
|
445 |
} |
|
446 |
||
447 |
#define SET_NONBLOCKING(fd) { \ |
|
448 |
int flags = fcntl(fd, F_GETFL); \ |
|
449 |
flags |= O_NONBLOCK; \ |
|
450 |
fcntl(fd, F_SETFL, flags); \ |
|
451 |
} |
|
452 |
||
453 |
#ifdef AF_INET6 |
|
454 |
static jboolean |
|
455 |
ping6(JNIEnv *env, jint fd, struct sockaddr_in6* him, jint timeout, |
|
456 |
struct sockaddr_in6* netif, jint ttl) { |
|
457 |
jint size; |
|
910 | 458 |
jint n, len; |
2 | 459 |
char sendbuf[1500]; |
460 |
unsigned char recvbuf[1500]; |
|
461 |
struct icmp6_hdr *icmp6; |
|
462 |
struct sockaddr_in6 sa_recv; |
|
463 |
jbyte *caddr, *recv_caddr; |
|
464 |
jchar pid; |
|
465 |
jint tmout2, seq = 1; |
|
466 |
struct timeval tv; |
|
467 |
size_t plen; |
|
468 |
||
469 |
#ifdef __linux__ |
|
470 |
{ |
|
471 |
int csum_offset; |
|
472 |
/** |
|
473 |
* For some strange reason, the linux kernel won't calculate the |
|
474 |
* checksum of ICMPv6 packets unless you set this socket option |
|
475 |
*/ |
|
476 |
csum_offset = 2; |
|
477 |
setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int)); |
|
478 |
} |
|
479 |
#endif |
|
480 |
||
481 |
caddr = (jbyte *)&(him->sin6_addr); |
|
482 |
||
483 |
/* icmp_id is a 16 bit data type, therefore down cast the pid */ |
|
484 |
pid = (jchar)getpid(); |
|
485 |
size = 60*1024; |
|
486 |
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); |
|
487 |
if (ttl > 0) { |
|
488 |
setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
|
489 |
} |
|
490 |
if (netif != NULL) { |
|
491 |
if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) { |
|
492 |
NET_ThrowNew(env, errno, "Can't bind socket"); |
|
493 |
close(fd); |
|
494 |
return JNI_FALSE; |
|
495 |
} |
|
496 |
} |
|
497 |
SET_NONBLOCKING(fd); |
|
498 |
||
499 |
do { |
|
500 |
icmp6 = (struct icmp6_hdr *) sendbuf; |
|
501 |
icmp6->icmp6_type = ICMP6_ECHO_REQUEST; |
|
502 |
icmp6->icmp6_code = 0; |
|
503 |
/* let's tag the ECHO packet with our pid so we can identify it */ |
|
504 |
icmp6->icmp6_id = htons(pid); |
|
505 |
icmp6->icmp6_seq = htons(seq); |
|
506 |
seq++; |
|
507 |
icmp6->icmp6_cksum = 0; |
|
508 |
gettimeofday(&tv, NULL); |
|
509 |
memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv)); |
|
510 |
plen = sizeof(struct icmp6_hdr) + sizeof(tv); |
|
511 |
n = sendto(fd, sendbuf, plen, 0, (struct sockaddr*) him, sizeof(struct sockaddr_in6)); |
|
512 |
if (n < 0 && errno != EINPROGRESS) { |
|
513 |
NET_ThrowNew(env, errno, "Can't send ICMP packet"); |
|
514 |
return JNI_FALSE; |
|
515 |
} |
|
516 |
||
517 |
tmout2 = timeout > 1000 ? 1000 : timeout; |
|
518 |
do { |
|
519 |
tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2); |
|
520 |
||
521 |
if (tmout2 >= 0) { |
|
522 |
len = sizeof(sa_recv); |
|
523 |
n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) &sa_recv, &len); |
|
524 |
icmp6 = (struct icmp6_hdr *) (recvbuf); |
|
525 |
recv_caddr = (jbyte *)&(sa_recv.sin6_addr); |
|
526 |
/* |
|
527 |
* We did receive something, but is it what we were expecting? |
|
528 |
* I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and |
|
529 |
* from the host that we are trying to determine is reachable. |
|
530 |
*/ |
|
531 |
if (n >= 8 && icmp6->icmp6_type == ICMP6_ECHO_REPLY && |
|
532 |
(ntohs(icmp6->icmp6_id) == pid) && |
|
533 |
NET_IsEqual(caddr, recv_caddr)) { |
|
534 |
close(fd); |
|
535 |
return JNI_TRUE; |
|
536 |
} |
|
537 |
} |
|
538 |
} while (tmout2 > 0); |
|
539 |
timeout -= 1000; |
|
540 |
} while (timeout > 0); |
|
541 |
close(fd); |
|
542 |
return JNI_FALSE; |
|
543 |
} |
|
544 |
#endif /* AF_INET6 */ |
|
545 |
||
546 |
/* |
|
547 |
* Class: java_net_Inet6AddressImpl |
|
548 |
* Method: isReachable0 |
|
549 |
* Signature: ([bII[bI)Z |
|
550 |
*/ |
|
551 |
JNIEXPORT jboolean JNICALL |
|
552 |
Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this, |
|
553 |
jbyteArray addrArray, |
|
554 |
jint scope, |
|
555 |
jint timeout, |
|
556 |
jbyteArray ifArray, |
|
557 |
jint ttl, jint if_scope) { |
|
558 |
#ifdef AF_INET6 |
|
559 |
jbyte caddr[16]; |
|
560 |
jint fd, sz; |
|
561 |
struct sockaddr_in6 him6; |
|
562 |
struct sockaddr_in6 inf6; |
|
563 |
struct sockaddr_in6* netif = NULL; |
|
564 |
int len = 0; |
|
565 |
int connect_rv = -1; |
|
566 |
||
567 |
/* |
|
568 |
* If IPv6 is not enable, then we can't reach an IPv6 address, can we? |
|
569 |
*/ |
|
570 |
if (!ipv6_available()) { |
|
571 |
return JNI_FALSE; |
|
572 |
} |
|
573 |
/* |
|
574 |
* If it's an IPv4 address, ICMP won't work with IPv4 mapped address, |
|
575 |
* therefore, let's delegate to the Inet4Address method. |
|
576 |
*/ |
|
577 |
sz = (*env)->GetArrayLength(env, addrArray); |
|
578 |
if (sz == 4) { |
|
579 |
return Java_java_net_Inet4AddressImpl_isReachable0(env, this, |
|
580 |
addrArray, |
|
581 |
timeout, |
|
582 |
ifArray, ttl); |
|
583 |
} |
|
584 |
||
585 |
memset((char *) caddr, 0, 16); |
|
586 |
memset((char *) &him6, 0, sizeof(him6)); |
|
587 |
(*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr); |
|
588 |
memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) ); |
|
589 |
him6.sin6_family = AF_INET6; |
|
590 |
#ifdef __linux__ |
|
591 |
if (scope > 0) |
|
592 |
him6.sin6_scope_id = scope; |
|
593 |
else |
|
594 |
him6.sin6_scope_id = getDefaultIPv6Interface( &(him6.sin6_addr)); |
|
595 |
len = sizeof(struct sockaddr_in6); |
|
596 |
#else |
|
597 |
if (scope > 0) |
|
598 |
him6.sin6_scope_id = scope; |
|
599 |
len = sizeof(struct sockaddr_in6); |
|
600 |
#endif |
|
601 |
/* |
|
602 |
* If a network interface was specified, let's create the address |
|
603 |
* for it. |
|
604 |
*/ |
|
605 |
if (!(IS_NULL(ifArray))) { |
|
606 |
memset((char *) caddr, 0, 16); |
|
607 |
memset((char *) &inf6, 0, sizeof(inf6)); |
|
608 |
(*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr); |
|
609 |
memcpy((void *)&(inf6.sin6_addr), caddr, sizeof(struct in6_addr) ); |
|
610 |
inf6.sin6_family = AF_INET6; |
|
611 |
inf6.sin6_scope_id = if_scope; |
|
612 |
netif = &inf6; |
|
613 |
} |
|
614 |
/* |
|
615 |
* If we can create a RAW socket, then when can use the ICMP ECHO_REQUEST |
|
616 |
* otherwise we'll try a tcp socket to the Echo port (7). |
|
617 |
* Note that this is empiric, and not connecting could mean it's blocked |
|
618 |
* or the echo servioe has been disabled. |
|
619 |
*/ |
|
620 |
||
621 |
fd = JVM_Socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
|
622 |
||
623 |
if (fd != -1) { /* Good to go, let's do a ping */ |
|
624 |
return ping6(env, fd, &him6, timeout, netif, ttl); |
|
625 |
} |
|
626 |
||
627 |
/* No good, let's fall back on TCP */ |
|
628 |
fd = JVM_Socket(AF_INET6, SOCK_STREAM, 0); |
|
629 |
if (fd == JVM_IO_ERR) { |
|
630 |
/* note: if you run out of fds, you may not be able to load |
|
631 |
* the exception class, and get a NoClassDefFoundError |
|
632 |
* instead. |
|
633 |
*/ |
|
634 |
NET_ThrowNew(env, errno, "Can't create socket"); |
|
635 |
return JNI_FALSE; |
|
636 |
} |
|
637 |
if (ttl > 0) { |
|
638 |
setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
|
639 |
} |
|
640 |
||
641 |
/* |
|
642 |
* A network interface was specified, so let's bind to it. |
|
643 |
*/ |
|
644 |
if (netif != NULL) { |
|
645 |
if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) { |
|
646 |
NET_ThrowNew(env, errno, "Can't bind socket"); |
|
647 |
close(fd); |
|
648 |
return JNI_FALSE; |
|
649 |
} |
|
650 |
} |
|
651 |
SET_NONBLOCKING(fd); |
|
652 |
||
653 |
/* no need to use NET_Connect as non-blocking */ |
|
654 |
him6.sin6_port = htons((short) 7); /* Echo port */ |
|
655 |
connect_rv = JVM_Connect(fd, (struct sockaddr *)&him6, len); |
|
656 |
||
657 |
/** |
|
658 |
* connection established or refused immediately, either way it means |
|
659 |
* we were able to reach the host! |
|
660 |
*/ |
|
661 |
if (connect_rv == 0 || errno == ECONNREFUSED) { |
|
662 |
close(fd); |
|
663 |
return JNI_TRUE; |
|
664 |
} else { |
|
665 |
int optlen; |
|
666 |
||
667 |
switch (errno) { |
|
668 |
case ENETUNREACH: /* Network Unreachable */ |
|
669 |
case EAFNOSUPPORT: /* Address Family not supported */ |
|
670 |
case EADDRNOTAVAIL: /* address is not available on the remote machine */ |
|
671 |
#ifdef __linux__ |
|
672 |
case EINVAL: |
|
673 |
/* |
|
674 |
* On some Linuxes, when bound to the loopback interface, connect |
|
675 |
* will fail and errno will be set to EINVAL. When that happens, |
|
676 |
* don't throw an exception, just return false. |
|
677 |
*/ |
|
678 |
#endif /* __linux__ */ |
|
679 |
close(fd); |
|
680 |
return JNI_FALSE; |
|
681 |
} |
|
682 |
||
683 |
if (errno != EINPROGRESS) { |
|
684 |
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", |
|
685 |
"connect failed"); |
|
686 |
close(fd); |
|
687 |
return JNI_FALSE; |
|
688 |
} |
|
689 |
||
690 |
timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout); |
|
691 |
||
692 |
if (timeout >= 0) { |
|
693 |
/* has connection been established */ |
|
694 |
optlen = sizeof(connect_rv); |
|
695 |
if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, |
|
696 |
&optlen) <0) { |
|
697 |
connect_rv = errno; |
|
698 |
} |
|
699 |
if (connect_rv == 0 || ECONNREFUSED) { |
|
700 |
close(fd); |
|
701 |
return JNI_TRUE; |
|
702 |
} |
|
703 |
} |
|
704 |
close(fd); |
|
705 |
return JNI_FALSE; |
|
706 |
} |
|
707 |
#else /* AF_INET6 */ |
|
708 |
return JNI_FALSE; |
|
709 |
#endif /* AF_INET6 */ |
|
710 |
} |