36 #include <net/if.h> |
36 #include <net/if.h> |
37 #endif |
37 #endif |
38 |
38 |
39 #include "net_util.h" |
39 #include "net_util.h" |
40 |
40 |
|
41 #include "java_net_InetAddress.h" |
41 #include "java_net_Inet4AddressImpl.h" |
42 #include "java_net_Inet4AddressImpl.h" |
42 #include "java_net_Inet6AddressImpl.h" |
43 #include "java_net_Inet6AddressImpl.h" |
43 #include "java_net_InetAddress.h" |
|
44 |
44 |
45 /* the initial size of our hostent buffers */ |
45 /* the initial size of our hostent buffers */ |
46 #ifndef NI_MAXHOST |
46 #ifndef NI_MAXHOST |
47 #define NI_MAXHOST 1025 |
47 #define NI_MAXHOST 1025 |
48 #endif |
48 #endif |
49 |
49 |
50 |
50 #define SET_NONBLOCKING(fd) { \ |
51 /************************************************************************ |
51 int flags = fcntl(fd, F_GETFL); \ |
|
52 flags |= O_NONBLOCK; \ |
|
53 fcntl(fd, F_SETFL, flags); \ |
|
54 } |
|
55 |
|
56 /* |
52 * Inet6AddressImpl |
57 * Inet6AddressImpl |
53 */ |
58 */ |
54 |
59 |
55 /* |
60 /* |
56 * Class: java_net_Inet6AddressImpl |
61 * Class: java_net_Inet6AddressImpl |
57 * Method: getLocalHostName |
62 * Method: getLocalHostName |
58 * Signature: ()Ljava/lang/String; |
63 * Signature: ()Ljava/lang/String; |
59 */ |
64 */ |
60 JNIEXPORT jstring JNICALL |
65 JNIEXPORT jstring JNICALL |
61 Java_java_net_Inet6AddressImpl_getLocalHostName(JNIEnv *env, jobject this) { |
66 Java_java_net_Inet6AddressImpl_getLocalHostName(JNIEnv *env, jobject this) { |
62 int ret; |
67 char hostname[NI_MAXHOST + 1]; |
63 char hostname[NI_MAXHOST+1]; |
|
64 |
68 |
65 hostname[0] = '\0'; |
69 hostname[0] = '\0'; |
66 ret = gethostname(hostname, NI_MAXHOST); |
70 if (gethostname(hostname, NI_MAXHOST) != 0) { |
67 if (ret == -1) { |
|
68 /* Something went wrong, maybe networking is not setup? */ |
|
69 strcpy(hostname, "localhost"); |
71 strcpy(hostname, "localhost"); |
|
72 #if defined(__solaris__) |
70 } else { |
73 } else { |
71 // ensure null-terminated |
74 // try to resolve hostname via nameservice |
|
75 // if it is known but getnameinfo fails, hostname will still be the |
|
76 // value from gethostname |
|
77 struct addrinfo hints, *res; |
|
78 |
|
79 // make sure string is null-terminated |
72 hostname[NI_MAXHOST] = '\0'; |
80 hostname[NI_MAXHOST] = '\0'; |
73 } |
|
74 |
|
75 #if defined(__solaris__) |
|
76 if (ret == 0) { |
|
77 /* Solaris doesn't want to give us a fully qualified domain name. |
|
78 * We do a reverse lookup to try and get one. This works |
|
79 * if DNS occurs before NIS in /etc/resolv.conf, but fails |
|
80 * if NIS comes first (it still gets only a partial name). |
|
81 * We use thread-safe system calls. |
|
82 */ |
|
83 struct addrinfo hints, *res; |
|
84 int error; |
|
85 |
|
86 memset(&hints, 0, sizeof(hints)); |
81 memset(&hints, 0, sizeof(hints)); |
87 hints.ai_flags = AI_CANONNAME; |
82 hints.ai_flags = AI_CANONNAME; |
88 hints.ai_family = AF_UNSPEC; |
83 hints.ai_family = AF_UNSPEC; |
89 |
84 |
90 error = getaddrinfo(hostname, NULL, &hints, &res); |
85 if (getaddrinfo(hostname, NULL, &hints, &res) == 0) { |
91 |
86 getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, |
92 if (error == 0) { |
87 NULL, 0, NI_NAMEREQD); |
93 /* host is known to name service */ |
|
94 error = getnameinfo(res->ai_addr, |
|
95 res->ai_addrlen, |
|
96 hostname, |
|
97 NI_MAXHOST, |
|
98 NULL, |
|
99 0, |
|
100 NI_NAMEREQD); |
|
101 |
|
102 /* if getnameinfo fails hostname is still the value |
|
103 from gethostname */ |
|
104 |
|
105 freeaddrinfo(res); |
88 freeaddrinfo(res); |
106 } |
89 } |
107 } |
90 } |
108 #endif |
91 #else |
109 |
92 } else { |
|
93 // make sure string is null-terminated |
|
94 hostname[NI_MAXHOST] = '\0'; |
|
95 } |
|
96 #endif |
110 return (*env)->NewStringUTF(env, hostname); |
97 return (*env)->NewStringUTF(env, hostname); |
111 } |
98 } |
112 |
99 |
113 #ifdef MACOSX |
100 #if defined(MACOSX) |
114 /* also called from Inet4AddressImpl.c */ |
101 /* also called from Inet4AddressImpl.c */ |
115 __private_extern__ jobjectArray |
102 __private_extern__ jobjectArray |
116 lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6) |
103 lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6) |
117 { |
104 { |
118 jobjectArray result = NULL; |
105 jobjectArray result = NULL; |
232 return result; |
217 return result; |
233 } |
218 } |
234 #endif |
219 #endif |
235 |
220 |
236 /* |
221 /* |
237 * Find an internet address for a given hostname. Note that this |
|
238 * code only works for addresses of type INET. The translation |
|
239 * of %d.%d.%d.%d to an address (int) occurs in java now, so the |
|
240 * String "host" shouldn't *ever* be a %d.%d.%d.%d string |
|
241 * |
|
242 * Class: java_net_Inet6AddressImpl |
222 * Class: java_net_Inet6AddressImpl |
243 * Method: lookupAllHostAddr |
223 * Method: lookupAllHostAddr |
244 * Signature: (Ljava/lang/String;)[[B |
224 * Signature: (Ljava/lang/String;)[[B |
245 */ |
225 */ |
246 |
|
247 JNIEXPORT jobjectArray JNICALL |
226 JNIEXPORT jobjectArray JNICALL |
248 Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, |
227 Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, |
249 jstring host) { |
228 jstring host) { |
|
229 jobjectArray ret = NULL; |
250 const char *hostname; |
230 const char *hostname; |
251 jobjectArray ret = 0; |
231 int error = 0; |
252 int retLen = 0; |
232 struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL, |
253 |
233 *iterator; |
254 int getaddrinfo_error=0; |
|
255 struct addrinfo hints, *res, *resNew = NULL; |
|
256 |
234 |
257 initInetAddressIDs(env); |
235 initInetAddressIDs(env); |
258 JNU_CHECK_EXCEPTION_RETURN(env, NULL); |
236 JNU_CHECK_EXCEPTION_RETURN(env, NULL); |
259 |
237 |
260 if (IS_NULL(host)) { |
238 if (IS_NULL(host)) { |
261 JNU_ThrowNullPointerException(env, "host is null"); |
239 JNU_ThrowNullPointerException(env, "host argument is null"); |
262 return 0; |
240 return NULL; |
263 } |
241 } |
264 hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); |
242 hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE); |
265 CHECK_NULL_RETURN(hostname, NULL); |
243 CHECK_NULL_RETURN(hostname, NULL); |
266 |
244 |
267 /* Try once, with our static buffer. */ |
245 // try once, with our static buffer |
268 memset(&hints, 0, sizeof(hints)); |
246 memset(&hints, 0, sizeof(hints)); |
269 hints.ai_flags = AI_CANONNAME; |
247 hints.ai_flags = AI_CANONNAME; |
270 hints.ai_family = AF_UNSPEC; |
248 hints.ai_family = AF_UNSPEC; |
271 |
249 |
272 #ifdef __solaris__ |
250 error = getaddrinfo(hostname, NULL, &hints, &res); |
273 /* |
251 |
274 * Workaround for Solaris bug 4160367 - if a hostname contains a |
252 if (error) { |
275 * white space then 0.0.0.0 is returned |
253 #if defined(MACOSX) |
276 */ |
254 // if getaddrinfo fails try getifaddrs |
277 if (isspace((unsigned char)hostname[0])) { |
|
278 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", |
|
279 hostname); |
|
280 JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
281 return NULL; |
|
282 } |
|
283 #endif |
|
284 |
|
285 getaddrinfo_error = getaddrinfo(hostname, NULL, &hints, &res); |
|
286 |
|
287 #ifdef MACOSX |
|
288 if (getaddrinfo_error) { |
|
289 /* |
|
290 * If getaddrinfo fails looking up the local machine, attempt to get the |
|
291 * address from getifaddrs. This ensures we get an IPv6 address for the |
|
292 * local machine. |
|
293 */ |
|
294 ret = lookupIfLocalhost(env, hostname, JNI_TRUE); |
255 ret = lookupIfLocalhost(env, hostname, JNI_TRUE); |
295 if (ret != NULL || (*env)->ExceptionCheck(env)) { |
256 if (ret != NULL || (*env)->ExceptionCheck(env)) { |
296 JNU_ReleaseStringPlatformChars(env, host, hostname); |
257 goto cleanupAndReturn; |
297 return ret; |
258 } |
298 } |
259 #endif |
299 } |
260 // report error |
300 #endif |
261 NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error); |
301 |
262 goto cleanupAndReturn; |
302 if (getaddrinfo_error) { |
|
303 /* report error */ |
|
304 NET_ThrowUnknownHostExceptionWithGaiError( |
|
305 env, hostname, getaddrinfo_error); |
|
306 JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
307 return NULL; |
|
308 } else { |
263 } else { |
309 int i = 0, addressPreference = -1; |
264 int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0, |
310 int inetCount = 0, inet6Count = 0, inetIndex = 0, inet6Index = 0, originalIndex = 0; |
265 inet6Index = 0, originalIndex = 0; |
311 struct addrinfo *itr, *last = NULL, *iterator = res; |
266 int addressPreference = |
|
267 (*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);; |
|
268 iterator = res; |
312 while (iterator != NULL) { |
269 while (iterator != NULL) { |
|
270 // skip duplicates |
313 int skip = 0; |
271 int skip = 0; |
314 itr = resNew; |
272 struct addrinfo *iteratorNew = resNew; |
315 while (itr != NULL) { |
273 while (iteratorNew != NULL) { |
316 if (iterator->ai_family == itr->ai_family && |
274 if (iterator->ai_family == iteratorNew->ai_family && |
317 iterator->ai_addrlen == itr->ai_addrlen) { |
275 iterator->ai_addrlen == iteratorNew->ai_addrlen) { |
318 if (itr->ai_family == AF_INET) { /* AF_INET */ |
276 if (iteratorNew->ai_family == AF_INET) { /* AF_INET */ |
319 struct sockaddr_in *addr1, *addr2; |
277 struct sockaddr_in *addr1, *addr2; |
320 addr1 = (struct sockaddr_in *)iterator->ai_addr; |
278 addr1 = (struct sockaddr_in *)iterator->ai_addr; |
321 addr2 = (struct sockaddr_in *)itr->ai_addr; |
279 addr2 = (struct sockaddr_in *)iteratorNew->ai_addr; |
322 if (addr1->sin_addr.s_addr == |
280 if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) { |
323 addr2->sin_addr.s_addr) { |
|
324 skip = 1; |
281 skip = 1; |
325 break; |
282 break; |
326 } |
283 } |
327 } else { |
284 } else { |
328 int t; |
285 int t; |
329 struct sockaddr_in6 *addr1, *addr2; |
286 struct sockaddr_in6 *addr1, *addr2; |
330 addr1 = (struct sockaddr_in6 *)iterator->ai_addr; |
287 addr1 = (struct sockaddr_in6 *)iterator->ai_addr; |
331 addr2 = (struct sockaddr_in6 *)itr->ai_addr; |
288 addr2 = (struct sockaddr_in6 *)iteratorNew->ai_addr; |
332 |
289 |
333 for (t = 0; t < 16; t++) { |
290 for (t = 0; t < 16; t++) { |
334 if (addr1->sin6_addr.s6_addr[t] != |
291 if (addr1->sin6_addr.s6_addr[t] != |
335 addr2->sin6_addr.s6_addr[t]) { |
292 addr2->sin6_addr.s6_addr[t]) { |
336 break; |
293 break; |
337 } |
294 } |
338 } |
295 } |
339 if (t < 16) { |
296 if (t < 16) { |
340 itr = itr->ai_next; |
297 iteratorNew = iteratorNew->ai_next; |
341 continue; |
298 continue; |
342 } else { |
299 } else { |
343 skip = 1; |
300 skip = 1; |
344 break; |
301 break; |
345 } |
302 } |
346 } |
303 } |
347 } else if (iterator->ai_family != AF_INET && |
304 } else if (iterator->ai_family != AF_INET && |
348 iterator->ai_family != AF_INET6) { |
305 iterator->ai_family != AF_INET6) { |
349 /* we can't handle other family types */ |
306 // we can't handle other family types |
350 skip = 1; |
307 skip = 1; |
351 break; |
308 break; |
352 } |
309 } |
353 itr = itr->ai_next; |
310 iteratorNew = iteratorNew->ai_next; |
354 } |
311 } |
355 |
312 |
356 if (!skip) { |
313 if (!skip) { |
357 struct addrinfo *next |
314 struct addrinfo *next |
358 = (struct addrinfo*) malloc(sizeof(struct addrinfo)); |
315 = (struct addrinfo *)malloc(sizeof(struct addrinfo)); |
359 if (!next) { |
316 if (!next) { |
360 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); |
317 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); |
361 ret = NULL; |
318 ret = NULL; |
362 goto cleanupAndReturn; |
319 goto cleanupAndReturn; |
363 } |
320 } |
441 inetIndex = inet6Index = 0; |
391 inetIndex = inet6Index = 0; |
442 } |
392 } |
443 iterator = iterator->ai_next; |
393 iterator = iterator->ai_next; |
444 } |
394 } |
445 } |
395 } |
446 |
396 cleanupAndReturn: |
447 cleanupAndReturn: |
397 JNU_ReleaseStringPlatformChars(env, host, hostname); |
448 { |
398 while (resNew != NULL) { |
449 struct addrinfo *iterator, *tmp; |
399 last = resNew; |
450 iterator = resNew; |
400 resNew = resNew->ai_next; |
451 while (iterator != NULL) { |
401 free(last); |
452 tmp = iterator; |
402 } |
453 iterator = iterator->ai_next; |
403 if (res != NULL) { |
454 free(tmp); |
404 freeaddrinfo(res); |
455 } |
405 } |
456 JNU_ReleaseStringPlatformChars(env, host, hostname); |
|
457 } |
|
458 |
|
459 freeaddrinfo(res); |
|
460 |
|
461 return ret; |
406 return ret; |
462 } |
407 } |
463 |
408 |
464 /* |
409 /* |
465 * Class: java_net_Inet6AddressImpl |
410 * Class: java_net_Inet6AddressImpl |
466 * Method: getHostByAddr |
411 * Method: getHostByAddr |
467 * Signature: (I)Ljava/lang/String; |
412 * Signature: (I)Ljava/lang/String; |
|
413 * |
|
414 * Theoretically the UnknownHostException could be enriched with gai error |
|
415 * information. But as it is silently ignored anyway, there's no need for this. |
|
416 * It's only important that either a valid hostname is returned or an |
|
417 * UnknownHostException is thrown. |
468 */ |
418 */ |
469 JNIEXPORT jstring JNICALL |
419 JNIEXPORT jstring JNICALL |
470 Java_java_net_Inet6AddressImpl_getHostByAddr(JNIEnv *env, jobject this, |
420 Java_java_net_Inet6AddressImpl_getHostByAddr(JNIEnv *env, jobject this, |
471 jbyteArray addrArray) { |
421 jbyteArray addrArray) { |
472 |
|
473 jstring ret = NULL; |
422 jstring ret = NULL; |
474 |
423 char host[NI_MAXHOST + 1]; |
475 char host[NI_MAXHOST+1]; |
|
476 int error = 0; |
|
477 int len = 0; |
424 int len = 0; |
478 jbyte caddr[16]; |
425 jbyte caddr[16]; |
479 |
426 SOCKETADDRESS sa; |
480 struct sockaddr_in him4; |
427 |
481 struct sockaddr_in6 him6; |
428 memset((void *)&sa, 0, sizeof(SOCKETADDRESS)); |
482 struct sockaddr *sa; |
429 |
483 |
430 // construct a sockaddr_in structure (AF_INET or AF_INET6) |
484 /* |
|
485 * For IPv4 addresses construct a sockaddr_in structure. |
|
486 */ |
|
487 if ((*env)->GetArrayLength(env, addrArray) == 4) { |
431 if ((*env)->GetArrayLength(env, addrArray) == 4) { |
488 jint addr; |
432 jint addr; |
489 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr); |
433 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr); |
490 addr = ((caddr[0]<<24) & 0xff000000); |
434 addr = ((caddr[0] << 24) & 0xff000000); |
491 addr |= ((caddr[1] <<16) & 0xff0000); |
435 addr |= ((caddr[1] << 16) & 0xff0000); |
492 addr |= ((caddr[2] <<8) & 0xff00); |
436 addr |= ((caddr[2] << 8) & 0xff00); |
493 addr |= (caddr[3] & 0xff); |
437 addr |= (caddr[3] & 0xff); |
494 memset((void *) &him4, 0, sizeof(him4)); |
438 sa.sa4.sin_addr.s_addr = htonl(addr); |
495 him4.sin_addr.s_addr = htonl(addr); |
439 sa.sa4.sin_family = AF_INET; |
496 him4.sin_family = AF_INET; |
440 len = sizeof(struct sockaddr_in); |
497 sa = (struct sockaddr *)&him4; |
|
498 len = sizeof(him4); |
|
499 } else { |
441 } else { |
500 /* |
|
501 * For IPv6 address construct a sockaddr_in6 structure. |
|
502 */ |
|
503 (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr); |
442 (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr); |
504 memset((void *)&him6, 0, sizeof(him6)); |
443 memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr)); |
505 memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr)); |
444 sa.sa6.sin6_family = AF_INET6; |
506 him6.sin6_family = AF_INET6; |
445 len = sizeof(struct sockaddr_in6); |
507 sa = (struct sockaddr *)&him6; |
446 } |
508 len = sizeof(him6); |
447 |
509 } |
448 if (getnameinfo(&sa.sa, len, host, NI_MAXHOST, NULL, 0, NI_NAMEREQD)) { |
510 |
449 JNU_ThrowByName(env, "java/net/UnknownHostException", NULL); |
511 error = getnameinfo(sa, len, host, NI_MAXHOST, NULL, 0, NI_NAMEREQD); |
450 } else { |
512 |
|
513 if (!error) { |
|
514 ret = (*env)->NewStringUTF(env, host); |
451 ret = (*env)->NewStringUTF(env, host); |
515 CHECK_NULL_RETURN(ret, NULL); |
452 if (ret == NULL) { |
516 } |
453 JNU_ThrowByName(env, "java/net/UnknownHostException", NULL); |
517 |
454 } |
518 if (ret == NULL) { |
|
519 JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL); |
|
520 } |
455 } |
521 |
456 |
522 return ret; |
457 return ret; |
523 } |
458 } |
524 |
459 |
525 #define SET_NONBLOCKING(fd) { \ |
460 /** |
526 int flags = fcntl(fd, F_GETFL); \ |
461 * ping implementation using tcp port 7 (echo) |
527 flags |= O_NONBLOCK; \ |
462 */ |
528 fcntl(fd, F_SETFL, flags); \ |
463 static jboolean |
|
464 tcp_ping6(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout, |
|
465 jint ttl) |
|
466 { |
|
467 jint fd; |
|
468 int connect_rv = -1; |
|
469 |
|
470 // open a TCP socket |
|
471 fd = socket(AF_INET6, SOCK_STREAM, 0); |
|
472 if (fd == -1) { |
|
473 // note: if you run out of fds, you may not be able to load |
|
474 // the exception class, and get a NoClassDefFoundError instead. |
|
475 NET_ThrowNew(env, errno, "Can't create socket"); |
|
476 return JNI_FALSE; |
|
477 } |
|
478 |
|
479 // set TTL |
|
480 if (ttl > 0) { |
|
481 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
|
482 } |
|
483 |
|
484 // A network interface was specified, so let's bind to it. |
|
485 if (netif != NULL) { |
|
486 if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) { |
|
487 NET_ThrowNew(env, errno, "Can't bind socket"); |
|
488 close(fd); |
|
489 return JNI_FALSE; |
|
490 } |
|
491 } |
|
492 |
|
493 // Make the socket non blocking so we can use select/poll. |
|
494 SET_NONBLOCKING(fd); |
|
495 |
|
496 sa->sa6.sin6_port = htons(7); // echo port |
|
497 connect_rv = NET_Connect(fd, &sa->sa, sizeof(struct sockaddr_in6)); |
|
498 |
|
499 // connection established or refused immediately, either way it means |
|
500 // we were able to reach the host! |
|
501 if (connect_rv == 0 || errno == ECONNREFUSED) { |
|
502 close(fd); |
|
503 return JNI_TRUE; |
|
504 } |
|
505 |
|
506 switch (errno) { |
|
507 case ENETUNREACH: // Network Unreachable |
|
508 case EAFNOSUPPORT: // Address Family not supported |
|
509 case EADDRNOTAVAIL: // address is not available on the remote machine |
|
510 #if defined(__linux__) || defined(_AIX) |
|
511 // On some Linux versions, when a socket is bound to the loopback |
|
512 // interface, connect will fail and errno will be set to EINVAL |
|
513 // or EHOSTUNREACH. When that happens, don't throw an exception, |
|
514 // just return false. |
|
515 case EINVAL: |
|
516 case EHOSTUNREACH: // No route to host |
|
517 #endif |
|
518 close(fd); |
|
519 return JNI_FALSE; |
|
520 case EINPROGRESS: // this is expected as we'll probably have to wait |
|
521 break; |
|
522 default: |
|
523 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", |
|
524 "connect failed"); |
|
525 close(fd); |
|
526 return JNI_FALSE; |
|
527 } |
|
528 |
|
529 timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout); |
|
530 if (timeout >= 0) { |
|
531 // connection has been established, check for error condition |
|
532 socklen_t optlen = (socklen_t)sizeof(connect_rv); |
|
533 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, |
|
534 &optlen) <0) |
|
535 { |
|
536 connect_rv = errno; |
|
537 } |
|
538 if (connect_rv == 0 || connect_rv == ECONNREFUSED) { |
|
539 close(fd); |
|
540 return JNI_TRUE; |
|
541 } |
|
542 } |
|
543 close(fd); |
|
544 return JNI_FALSE; |
529 } |
545 } |
530 |
546 |
|
547 /** |
|
548 * ping implementation. |
|
549 * Send an ICMP_ECHO_REQUEST packet every second until either the timeout |
|
550 * expires or an answer is received. |
|
551 * Returns true if an ECHO_REPLY is received, false otherwise. |
|
552 */ |
531 static jboolean |
553 static jboolean |
532 ping6(JNIEnv *env, jint fd, struct sockaddr_in6* him, jint timeout, |
554 ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif, |
533 struct sockaddr_in6* netif, jint ttl) { |
555 jint timeout, jint ttl) |
534 jint size; |
556 { |
535 jint n; |
557 jint n, size = 60 * 1024, tmout2, seq = 1; |
536 socklen_t len; |
558 socklen_t len; |
537 char sendbuf[1500]; |
559 unsigned char sendbuf[1500], recvbuf[1500]; |
538 unsigned char recvbuf[1500]; |
|
539 struct icmp6_hdr *icmp6; |
560 struct icmp6_hdr *icmp6; |
540 struct sockaddr_in6 sa_recv; |
561 struct sockaddr_in6 sa_recv; |
541 jbyte *caddr, *recv_caddr; |
|
542 jchar pid; |
562 jchar pid; |
543 jint tmout2, seq = 1; |
|
544 struct timeval tv; |
563 struct timeval tv; |
545 size_t plen; |
564 size_t plen = sizeof(struct icmp6_hdr) + sizeof(tv); |
546 |
565 |
547 #ifdef __linux__ |
566 #if defined(__linux__) |
548 { |
|
549 int csum_offset; |
|
550 /** |
567 /** |
551 * For some strange reason, the linux kernel won't calculate the |
568 * For some strange reason, the linux kernel won't calculate the |
552 * checksum of ICMPv6 packets unless you set this socket option |
569 * checksum of ICMPv6 packets unless you set this socket option |
553 */ |
570 */ |
554 csum_offset = 2; |
571 int csum_offset = 2; |
555 setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int)); |
572 setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int)); |
556 } |
573 #endif |
557 #endif |
574 |
558 |
575 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); |
559 caddr = (jbyte *)&(him->sin6_addr); |
576 |
560 |
577 // sets the ttl (max number of hops) |
561 /* icmp_id is a 16 bit data type, therefore down cast the pid */ |
578 if (ttl > 0) { |
|
579 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
|
580 } |
|
581 |
|
582 // a specific interface was specified, so let's bind the socket |
|
583 // to that interface to ensure the requests are sent only through it. |
|
584 if (netif != NULL) { |
|
585 if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) { |
|
586 NET_ThrowNew(env, errno, "Can't bind socket"); |
|
587 close(fd); |
|
588 return JNI_FALSE; |
|
589 } |
|
590 } |
|
591 |
|
592 // icmp_id is a 16 bit data type, therefore down cast the pid |
562 pid = (jchar)getpid(); |
593 pid = (jchar)getpid(); |
563 size = 60*1024; |
594 |
564 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); |
595 // Make the socket non blocking so we can use select |
565 if (ttl > 0) { |
|
566 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
|
567 } |
|
568 if (netif != NULL) { |
|
569 if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) { |
|
570 NET_ThrowNew(env, errno, "Can't bind socket"); |
|
571 close(fd); |
|
572 return JNI_FALSE; |
|
573 } |
|
574 } |
|
575 SET_NONBLOCKING(fd); |
596 SET_NONBLOCKING(fd); |
576 |
|
577 do { |
597 do { |
578 icmp6 = (struct icmp6_hdr *) sendbuf; |
598 // create the ICMP request |
579 icmp6->icmp6_type = ICMP6_ECHO_REQUEST; |
599 icmp6 = (struct icmp6_hdr *)sendbuf; |
580 icmp6->icmp6_code = 0; |
600 icmp6->icmp6_type = ICMP6_ECHO_REQUEST; |
581 /* let's tag the ECHO packet with our pid so we can identify it */ |
601 icmp6->icmp6_code = 0; |
582 icmp6->icmp6_id = htons(pid); |
602 // let's tag the ECHO packet with our pid so we can identify it |
583 icmp6->icmp6_seq = htons(seq); |
603 icmp6->icmp6_id = htons(pid); |
584 seq++; |
604 icmp6->icmp6_seq = htons(seq); |
585 icmp6->icmp6_cksum = 0; |
605 seq++; |
586 gettimeofday(&tv, NULL); |
606 gettimeofday(&tv, NULL); |
587 memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv)); |
607 memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv)); |
588 plen = sizeof(struct icmp6_hdr) + sizeof(tv); |
608 icmp6->icmp6_cksum = 0; |
589 n = sendto(fd, sendbuf, plen, 0, (struct sockaddr*) him, sizeof(struct sockaddr_in6)); |
609 // send it |
590 if (n < 0 && errno != EINPROGRESS) { |
610 n = sendto(fd, sendbuf, plen, 0, &sa->sa, sizeof(struct sockaddr_in6)); |
591 #ifdef __linux__ |
611 if (n < 0 && errno != EINPROGRESS) { |
592 if (errno != EINVAL && errno != EHOSTUNREACH) |
612 #if defined(__linux__) |
593 /* |
613 /* |
594 * On some Linux versions, when a socket is bound to the |
614 * On some Linux versions, when a socket is bound to the loopback |
595 * loopback interface, sendto will fail and errno will be |
615 * interface, sendto will fail and errno will be set to |
596 * set to EINVAL or EHOSTUNREACH. |
616 * EINVAL or EHOSTUNREACH. When that happens, don't throw an |
597 * When that happens, don't throw an exception, just return false. |
617 * exception, just return false. |
598 */ |
618 */ |
599 #endif /*__linux__ */ |
619 if (errno != EINVAL && errno != EHOSTUNREACH) { |
600 NET_ThrowNew(env, errno, "Can't send ICMP packet"); |
620 NET_ThrowNew(env, errno, "Can't send ICMP packet"); |
601 close(fd); |
|
602 return JNI_FALSE; |
|
603 } |
|
604 |
|
605 tmout2 = timeout > 1000 ? 1000 : timeout; |
|
606 do { |
|
607 tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2); |
|
608 |
|
609 if (tmout2 >= 0) { |
|
610 len = sizeof(sa_recv); |
|
611 n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) &sa_recv, &len); |
|
612 icmp6 = (struct icmp6_hdr *) (recvbuf); |
|
613 recv_caddr = (jbyte *)&(sa_recv.sin6_addr); |
|
614 /* |
|
615 * We did receive something, but is it what we were expecting? |
|
616 * I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and |
|
617 * from the host that we are trying to determine is reachable. |
|
618 */ |
|
619 if (n >= 8 && icmp6->icmp6_type == ICMP6_ECHO_REPLY && |
|
620 (ntohs(icmp6->icmp6_id) == pid)) { |
|
621 if (NET_IsEqual(caddr, recv_caddr)) { |
|
622 close(fd); |
|
623 return JNI_TRUE; |
|
624 } |
621 } |
625 if (NET_IsZeroAddr(caddr)) { |
622 #else |
626 close(fd); |
623 NET_ThrowNew(env, errno, "Can't send ICMP packet"); |
627 return JNI_TRUE; |
624 #endif |
|
625 close(fd); |
|
626 return JNI_FALSE; |
|
627 } |
|
628 |
|
629 tmout2 = timeout > 1000 ? 1000 : timeout; |
|
630 do { |
|
631 tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2); |
|
632 if (tmout2 >= 0) { |
|
633 len = sizeof(sa_recv); |
|
634 n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, |
|
635 (struct sockaddr *)&sa_recv, &len); |
|
636 // check if we received enough data |
|
637 if (n < (jint)sizeof(struct icmp6_hdr)) { |
|
638 continue; |
|
639 } |
|
640 icmp6 = (struct icmp6_hdr *)recvbuf; |
|
641 // We did receive something, but is it what we were expecting? |
|
642 // I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and |
|
643 // from the host that we are trying to determine is reachable. |
|
644 if (icmp6->icmp6_type == ICMP6_ECHO_REPLY && |
|
645 (ntohs(icmp6->icmp6_id) == pid)) |
|
646 { |
|
647 if (NET_IsEqual((jbyte *)&sa->sa6.sin6_addr, |
|
648 (jbyte *)&sa_recv.sin6_addr)) { |
|
649 close(fd); |
|
650 return JNI_TRUE; |
|
651 } else if (NET_IsZeroAddr((jbyte *)&sa->sa6.sin6_addr)) { |
|
652 close(fd); |
|
653 return JNI_TRUE; |
|
654 } |
|
655 } |
628 } |
656 } |
629 } |
657 } while (tmout2 > 0); |
630 } |
658 timeout -= 1000; |
631 } while (tmout2 > 0); |
|
632 timeout -= 1000; |
|
633 } while (timeout > 0); |
659 } while (timeout > 0); |
634 close(fd); |
660 close(fd); |
635 return JNI_FALSE; |
661 return JNI_FALSE; |
636 } |
662 } |
637 |
663 |
640 * Method: isReachable0 |
666 * Method: isReachable0 |
641 * Signature: ([bII[bI)Z |
667 * Signature: ([bII[bI)Z |
642 */ |
668 */ |
643 JNIEXPORT jboolean JNICALL |
669 JNIEXPORT jboolean JNICALL |
644 Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this, |
670 Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this, |
645 jbyteArray addrArray, |
671 jbyteArray addrArray, jint scope, |
646 jint scope, |
672 jint timeout, jbyteArray ifArray, |
647 jint timeout, |
673 jint ttl, jint if_scope) |
648 jbyteArray ifArray, |
674 { |
649 jint ttl, jint if_scope) { |
|
650 jbyte caddr[16]; |
675 jbyte caddr[16]; |
651 jint fd, sz; |
676 jint sz, fd; |
652 struct sockaddr_in6 him6; |
677 SOCKETADDRESS sa, inf, *netif = NULL; |
653 struct sockaddr_in6 inf6; |
678 |
654 struct sockaddr_in6* netif = NULL; |
679 // If IPv6 is not enabled, then we can't reach an IPv6 address, can we? |
655 int len = 0; |
680 // Actually, we probably shouldn't even get here. |
656 int connect_rv = -1; |
|
657 |
|
658 /* |
|
659 * If IPv6 is not enable, then we can't reach an IPv6 address, can we? |
|
660 */ |
|
661 if (!ipv6_available()) { |
681 if (!ipv6_available()) { |
662 return JNI_FALSE; |
682 return JNI_FALSE; |
663 } |
683 } |
664 /* |
684 |
665 * If it's an IPv4 address, ICMP won't work with IPv4 mapped address, |
685 // If it's an IPv4 address, ICMP won't work with IPv4 mapped address, |
666 * therefore, let's delegate to the Inet4Address method. |
686 // therefore, let's delegate to the Inet4Address method. |
667 */ |
|
668 sz = (*env)->GetArrayLength(env, addrArray); |
687 sz = (*env)->GetArrayLength(env, addrArray); |
669 if (sz == 4) { |
688 if (sz == 4) { |
670 return Java_java_net_Inet4AddressImpl_isReachable0(env, this, |
689 return Java_java_net_Inet4AddressImpl_isReachable0(env, this, |
671 addrArray, |
690 addrArray, timeout, |
672 timeout, |
691 ifArray, ttl); |
673 ifArray, ttl); |
692 } |
674 } |
693 |
675 |
694 // load address to SOCKETADDRESS |
676 memset((void *) caddr, 0, 16); |
695 memset((char *)caddr, 0, 16); |
677 memset((void *) &him6, 0, sizeof(him6)); |
|
678 (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr); |
696 (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr); |
679 memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) ); |
697 memset((char *)&sa, 0, sizeof(SOCKETADDRESS)); |
680 him6.sin6_family = AF_INET6; |
698 memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr)); |
681 #ifdef __linux__ |
699 sa.sa6.sin6_family = AF_INET6; |
682 if (scope > 0) |
700 if (scope > 0) { |
683 him6.sin6_scope_id = scope; |
701 sa.sa6.sin6_scope_id = scope; |
684 else |
702 #if defined(__linux__) |
685 him6.sin6_scope_id = getDefaultIPv6Interface( &(him6.sin6_addr)); |
703 } else { |
686 len = sizeof(struct sockaddr_in6); |
704 sa.sa6.sin6_scope_id = getDefaultIPv6Interface(&sa.sa6.sin6_addr); |
687 #else |
705 #endif |
688 if (scope > 0) |
706 } |
689 him6.sin6_scope_id = scope; |
707 |
690 len = sizeof(struct sockaddr_in6); |
708 // load network interface address to SOCKETADDRESS, if specified |
691 #endif |
|
692 /* |
|
693 * If a network interface was specified, let's create the address |
|
694 * for it. |
|
695 */ |
|
696 if (!(IS_NULL(ifArray))) { |
709 if (!(IS_NULL(ifArray))) { |
697 memset((void *) caddr, 0, 16); |
710 memset((char *)caddr, 0, 16); |
698 memset((void *) &inf6, 0, sizeof(inf6)); |
711 (*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr); |
699 (*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr); |
712 memset((char *)&inf, 0, sizeof(SOCKETADDRESS)); |
700 memcpy((void *)&(inf6.sin6_addr), caddr, sizeof(struct in6_addr) ); |
713 memcpy((void *)&inf.sa6.sin6_addr, caddr, sizeof(struct in6_addr)); |
701 inf6.sin6_family = AF_INET6; |
714 inf.sa6.sin6_family = AF_INET6; |
702 inf6.sin6_scope_id = if_scope; |
715 inf.sa6.sin6_scope_id = if_scope; |
703 netif = &inf6; |
716 netif = &inf; |
704 } |
717 } |
705 /* |
718 |
706 * If we can create a RAW socket, then when can use the ICMP ECHO_REQUEST |
719 // Let's try to create a RAW socket to send ICMP packets. |
707 * otherwise we'll try a tcp socket to the Echo port (7). |
720 // This usually requires "root" privileges, so it's likely to fail. |
708 * Note that this is empiric, and not connecting could mean it's blocked |
|
709 * or the echo service has been disabled. |
|
710 */ |
|
711 |
|
712 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
721 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
713 |
|
714 if (fd != -1) { /* Good to go, let's do a ping */ |
|
715 return ping6(env, fd, &him6, timeout, netif, ttl); |
|
716 } |
|
717 |
|
718 /* No good, let's fall back on TCP */ |
|
719 fd = socket(AF_INET6, SOCK_STREAM, 0); |
|
720 if (fd == -1) { |
722 if (fd == -1) { |
721 /* note: if you run out of fds, you may not be able to load |
723 return tcp_ping6(env, &sa, netif, timeout, ttl); |
722 * the exception class, and get a NoClassDefFoundError |
|
723 * instead. |
|
724 */ |
|
725 NET_ThrowNew(env, errno, "Can't create socket"); |
|
726 return JNI_FALSE; |
|
727 } |
|
728 if (ttl > 0) { |
|
729 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); |
|
730 } |
|
731 |
|
732 /* |
|
733 * A network interface was specified, so let's bind to it. |
|
734 */ |
|
735 if (netif != NULL) { |
|
736 if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) { |
|
737 NET_ThrowNew(env, errno, "Can't bind socket"); |
|
738 close(fd); |
|
739 return JNI_FALSE; |
|
740 } |
|
741 } |
|
742 SET_NONBLOCKING(fd); |
|
743 |
|
744 him6.sin6_port = htons((short) 7); /* Echo port */ |
|
745 connect_rv = NET_Connect(fd, (struct sockaddr *)&him6, len); |
|
746 |
|
747 /** |
|
748 * connection established or refused immediately, either way it means |
|
749 * we were able to reach the host! |
|
750 */ |
|
751 if (connect_rv == 0 || errno == ECONNREFUSED) { |
|
752 close(fd); |
|
753 return JNI_TRUE; |
|
754 } else { |
724 } else { |
755 socklen_t optlen = (socklen_t)sizeof(connect_rv); |
725 // It didn't fail, so we can use ICMP_ECHO requests. |
756 |
726 return ping6(env, fd, &sa, netif, timeout, ttl); |
757 switch (errno) { |
|
758 case ENETUNREACH: /* Network Unreachable */ |
|
759 case EAFNOSUPPORT: /* Address Family not supported */ |
|
760 case EADDRNOTAVAIL: /* address is not available on the remote machine */ |
|
761 #if defined(__linux__) || defined(_AIX) |
|
762 case EINVAL: |
|
763 case EHOSTUNREACH: /* No route to host */ |
|
764 /* |
|
765 * On some Linux versions, when a socket is bound to the |
|
766 * loopback interface, connect will fail and errno will |
|
767 * be set to EINVAL or EHOSTUNREACH. When that happens, |
|
768 * don't throw an exception, just return false. |
|
769 */ |
|
770 #endif /* __linux__ */ |
|
771 close(fd); |
|
772 return JNI_FALSE; |
|
773 } |
|
774 |
|
775 if (errno != EINPROGRESS) { |
|
776 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", |
|
777 "connect failed"); |
|
778 close(fd); |
|
779 return JNI_FALSE; |
|
780 } |
|
781 |
|
782 timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout); |
|
783 |
|
784 if (timeout >= 0) { |
|
785 /* has connection been established */ |
|
786 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, |
|
787 &optlen) <0) { |
|
788 connect_rv = errno; |
|
789 } |
|
790 if (connect_rv == 0 || connect_rv == ECONNREFUSED) { |
|
791 close(fd); |
|
792 return JNI_TRUE; |
|
793 } |
|
794 } |
|
795 close(fd); |
|
796 return JNI_FALSE; |
|
797 } |
727 } |
798 } |
728 } |