1/*
2 * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25#include <ctype.h>
26#include <errno.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/time.h>
30#include <sys/types.h>
31#include <netinet/in.h>
32#include <netinet/icmp6.h>
33
34#if defined(_ALLBSD_SOURCE)
35#include <ifaddrs.h>
36#include <net/if.h>
37#endif
38
39#include "net_util.h"
40
41#include "java_net_InetAddress.h"
42#include "java_net_Inet4AddressImpl.h"
43#include "java_net_Inet6AddressImpl.h"
44
45#define SET_NONBLOCKING(fd) { \
46 int flags = fcntl(fd, F_GETFL); \
47 flags |= O_NONBLOCK; \
48 fcntl(fd, F_SETFL, flags); \
49}
50
51/*
52 * Inet6AddressImpl
53 */
54
55/*
56 * Class: java_net_Inet6AddressImpl
57 * Method: getLocalHostName
58 * Signature: ()Ljava/lang/String;
59 */
60JNIEXPORT jstring JNICALL
61Java_java_net_Inet6AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
62 char hostname[NI_MAXHOST + 1];
63
64 hostname[0] = '\0';
65 if (gethostname(hostname, sizeof(hostname)) != 0) {
66 strcpy(hostname, "localhost");
67 } else {
68#if defined(__solaris__)
69 // try to resolve hostname via nameservice
70 // if it is known but getnameinfo fails, hostname will still be the
71 // value from gethostname
72 struct addrinfo hints, *res;
73
74 // make sure string is null-terminated
75 hostname[NI_MAXHOST] = '\0';
76 memset(&hints, 0, sizeof(hints));
77 hints.ai_flags = AI_CANONNAME;
78 hints.ai_family = AF_UNSPEC;
79
80 if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
81 getnameinfo(res->ai_addr, res->ai_addrlen, hostname, sizeof(hostname),
82 NULL, 0, NI_NAMEREQD);
83 freeaddrinfo(res);
84 }
85#else
86 // make sure string is null-terminated
87 hostname[NI_MAXHOST] = '\0';
88#endif
89 }
90 return (*env)->NewStringUTF(env, hostname);
91}
92
93#if defined(MACOSX)
94/* also called from Inet4AddressImpl.c */
95__private_extern__ jobjectArray
96lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6)
97{
98 jobjectArray result = NULL;
99 char myhostname[NI_MAXHOST + 1];
100 struct ifaddrs *ifa = NULL;
101 int familyOrder = 0;
102 int count = 0, i, j;
103 int addrs4 = 0, addrs6 = 0, numV4Loopbacks = 0, numV6Loopbacks = 0;
104 jboolean includeLoopback = JNI_FALSE;
105 jobject name;
106
107 initInetAddressIDs(env);
108 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
109
110 /* If the requested name matches this host's hostname, return IP addresses
111 * from all attached interfaces. (#2844683 et al) This prevents undesired
112 * PPP dialup, but may return addresses that don't actually correspond to
113 * the name (if the name actually matches something in DNS etc.
114 */
115 myhostname[0] = '\0';
116 if (gethostname(myhostname, sizeof(myhostname)) == -1) {
117 /* Something went wrong, maybe networking is not setup? */
118 return NULL;
119 }
120 myhostname[NI_MAXHOST] = '\0';
121
122 if (strcmp(myhostname, hostname) != 0) {
123 // Non-self lookup
124 return NULL;
125 }
126
127 if (getifaddrs(&ifa) != 0) {
128 NET_ThrowNew(env, errno, "Can't get local interface addresses");
129 return NULL;
130 }
131
132 name = (*env)->NewStringUTF(env, hostname);
133 if (name == NULL) {
134 freeifaddrs(ifa);
135 return NULL;
136 }
137
138 /* Iterate over the interfaces, and total up the number of IPv4 and IPv6
139 * addresses we have. Also keep a count of loopback addresses. We need to
140 * exclude them in the normal case, but return them if we don't get an IP
141 * address.
142 */
143 struct ifaddrs *iter = ifa;
144 while (iter) {
145 if (iter->ifa_addr != NULL) {
146 int family = iter->ifa_addr->sa_family;
147 if (iter->ifa_name[0] != '\0') {
148 jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK;
149 if (family == AF_INET) {
150 addrs4++;
151 if (isLoopback) numV4Loopbacks++;
152 } else if (family == AF_INET6 && includeV6) {
153 addrs6++;
154 if (isLoopback) numV6Loopbacks++;
155 } // else we don't care, e.g. AF_LINK
156 }
157 }
158 iter = iter->ifa_next;
159 }
160
161 if (addrs4 == numV4Loopbacks && addrs6 == numV6Loopbacks) {
162 // We don't have a real IP address, just loopback. We need to include
163 // loopback in our results.
164 includeLoopback = JNI_TRUE;
165 }
166
167 /* Create and fill the Java array. */
168 int arraySize = addrs4 + addrs6 -
169 (includeLoopback ? 0 : (numV4Loopbacks + numV6Loopbacks));
170 result = (*env)->NewObjectArray(env, arraySize, ia_class, NULL);
171 if (!result) goto done;
172
173 if ((*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID)) {
174 i = includeLoopback ? addrs6 : (addrs6 - numV6Loopbacks);
175 j = 0;
176 } else {
177 i = 0;
178 j = includeLoopback ? addrs4 : (addrs4 - numV4Loopbacks);
179 }
180
181 // Now loop around the ifaddrs
182 iter = ifa;
183 while (iter != NULL) {
184 if (iter->ifa_addr != NULL) {
185 jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK;
186 int family = iter->ifa_addr->sa_family;
187
188 if (iter->ifa_name[0] != '\0' &&
189 (family == AF_INET || (family == AF_INET6 && includeV6)) &&
190 (!isLoopback || includeLoopback))
191 {
192 int port;
193 int index = (family == AF_INET) ? i++ : j++;
194 jobject o = NET_SockaddrToInetAddress(env,
195 (SOCKETADDRESS *)iter->ifa_addr, &port);
196 if (!o) {
197 freeifaddrs(ifa);
198 if (!(*env)->ExceptionCheck(env))
199 JNU_ThrowOutOfMemoryError(env, "Object allocation failed");
200 return NULL;
201 }
202 setInetAddress_hostName(env, o, name);
203 if ((*env)->ExceptionCheck(env))
204 goto done;
205 (*env)->SetObjectArrayElement(env, result, index, o);
206 (*env)->DeleteLocalRef(env, o);
207 }
208 }
209 iter = iter->ifa_next;
210 }
211
212 done:
213 freeifaddrs(ifa);
214
215 return result;
216}
217#endif
218
219/*
220 * Class: java_net_Inet6AddressImpl
221 * Method: lookupAllHostAddr
222 * Signature: (Ljava/lang/String;)[[B
223 */
224JNIEXPORT jobjectArray JNICALL
225Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
226 jstring host) {
227 jobjectArray ret = NULL;
228 const char *hostname;
229 int error = 0;
230 struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,
231 *iterator;
232
233 initInetAddressIDs(env);
234 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
235
236 if (IS_NULL(host)) {
237 JNU_ThrowNullPointerException(env, "host argument is null");
238 return NULL;
239 }
240 hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
241 CHECK_NULL_RETURN(hostname, NULL);
242
243 // try once, with our static buffer
244 memset(&hints, 0, sizeof(hints));
245 hints.ai_flags = AI_CANONNAME;
246 hints.ai_family = AF_UNSPEC;
247
248 error = getaddrinfo(hostname, NULL, &hints, &res);
249
250 if (error) {
251#if defined(MACOSX)
252 // if getaddrinfo fails try getifaddrs
253 ret = lookupIfLocalhost(env, hostname, JNI_TRUE);
254 if (ret != NULL || (*env)->ExceptionCheck(env)) {
255 goto cleanupAndReturn;
256 }
257#endif
258 // report error
259 NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error);
260 goto cleanupAndReturn;
261 } else {
262 int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0,
263 inet6Index = 0, originalIndex = 0;
264 int addressPreference =
265 (*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);;
266 iterator = res;
267 while (iterator != NULL) {
268 // skip duplicates
269 int skip = 0;
270 struct addrinfo *iteratorNew = resNew;
271 while (iteratorNew != NULL) {
272 if (iterator->ai_family == iteratorNew->ai_family &&
273 iterator->ai_addrlen == iteratorNew->ai_addrlen) {
274 if (iteratorNew->ai_family == AF_INET) { /* AF_INET */
275 struct sockaddr_in *addr1, *addr2;
276 addr1 = (struct sockaddr_in *)iterator->ai_addr;
277 addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;
278 if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {
279 skip = 1;
280 break;
281 }
282 } else {
283 int t;
284 struct sockaddr_in6 *addr1, *addr2;
285 addr1 = (struct sockaddr_in6 *)iterator->ai_addr;
286 addr2 = (struct sockaddr_in6 *)iteratorNew->ai_addr;
287
288 for (t = 0; t < 16; t++) {
289 if (addr1->sin6_addr.s6_addr[t] !=
290 addr2->sin6_addr.s6_addr[t]) {
291 break;
292 }
293 }
294 if (t < 16) {
295 iteratorNew = iteratorNew->ai_next;
296 continue;
297 } else {
298 skip = 1;
299 break;
300 }
301 }
302 } else if (iterator->ai_family != AF_INET &&
303 iterator->ai_family != AF_INET6) {
304 // we can't handle other family types
305 skip = 1;
306 break;
307 }
308 iteratorNew = iteratorNew->ai_next;
309 }
310
311 if (!skip) {
312 struct addrinfo *next
313 = (struct addrinfo *)malloc(sizeof(struct addrinfo));
314 if (!next) {
315 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
316 ret = NULL;
317 goto cleanupAndReturn;
318 }
319 memcpy(next, iterator, sizeof(struct addrinfo));
320 next->ai_next = NULL;
321 if (resNew == NULL) {
322 resNew = next;
323 } else {
324 last->ai_next = next;
325 }
326 last = next;
327 i++;
328 if (iterator->ai_family == AF_INET) {
329 inetCount++;
330 } else if (iterator->ai_family == AF_INET6) {
331 inet6Count++;
332 }
333 }
334 iterator = iterator->ai_next;
335 }
336
337 // allocate array - at this point i contains the number of addresses
338 ret = (*env)->NewObjectArray(env, i, ia_class, NULL);
339 if (IS_NULL(ret)) {
340 /* we may have memory to free at the end of this */
341 goto cleanupAndReturn;
342 }
343
344 if (addressPreference == java_net_InetAddress_PREFER_IPV6_VALUE) {
345 inetIndex = inet6Count;
346 inet6Index = 0;
347 } else if (addressPreference == java_net_InetAddress_PREFER_IPV4_VALUE) {
348 inetIndex = 0;
349 inet6Index = inetCount;
350 } else if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
351 inetIndex = inet6Index = originalIndex = 0;
352 }
353
354 iterator = resNew;
355 while (iterator != NULL) {
356 if (iterator->ai_family == AF_INET) {
357 jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
358 if (IS_NULL(iaObj)) {
359 ret = NULL;
360 goto cleanupAndReturn;
361 }
362 setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
363 if ((*env)->ExceptionCheck(env))
364 goto cleanupAndReturn;
365 setInetAddress_hostName(env, iaObj, host);
366 if ((*env)->ExceptionCheck(env))
367 goto cleanupAndReturn;
368 (*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);
369 inetIndex++;
370 } else if (iterator->ai_family == AF_INET6) {
371 jint scope = 0;
372 jboolean ret1;
373 jobject iaObj = (*env)->NewObject(env, ia6_class, ia6_ctrID);
374 if (IS_NULL(iaObj)) {
375 ret = NULL;
376 goto cleanupAndReturn;
377 }
378 ret1 = setInet6Address_ipaddress(env, iaObj, (char *)&(((struct sockaddr_in6*)iterator->ai_addr)->sin6_addr));
379 if (ret1 == JNI_FALSE) {
380 ret = NULL;
381 goto cleanupAndReturn;
382 }
383 scope = ((struct sockaddr_in6 *)iterator->ai_addr)->sin6_scope_id;
384 if (scope != 0) { // zero is default value, no need to set
385 setInet6Address_scopeid(env, iaObj, scope);
386 }
387 setInetAddress_hostName(env, iaObj, host);
388 if ((*env)->ExceptionCheck(env))
389 goto cleanupAndReturn;
390 (*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);
391 inet6Index++;
392 }
393 if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
394 originalIndex++;
395 inetIndex = inet6Index = 0;
396 }
397 iterator = iterator->ai_next;
398 }
399 }
400cleanupAndReturn:
401 JNU_ReleaseStringPlatformChars(env, host, hostname);
402 while (resNew != NULL) {
403 last = resNew;
404 resNew = resNew->ai_next;
405 free(last);
406 }
407 if (res != NULL) {
408 freeaddrinfo(res);
409 }
410 return ret;
411}
412
413/*
414 * Class: java_net_Inet6AddressImpl
415 * Method: getHostByAddr
416 * Signature: (I)Ljava/lang/String;
417 *
418 * Theoretically the UnknownHostException could be enriched with gai error
419 * information. But as it is silently ignored anyway, there's no need for this.
420 * It's only important that either a valid hostname is returned or an
421 * UnknownHostException is thrown.
422 */
423JNIEXPORT jstring JNICALL
424Java_java_net_Inet6AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
425 jbyteArray addrArray) {
426 jstring ret = NULL;
427 char host[NI_MAXHOST + 1];
428 int len = 0;
429 jbyte caddr[16];
430 SOCKETADDRESS sa;
431
432 memset((void *)&sa, 0, sizeof(SOCKETADDRESS));
433
434 // construct a sockaddr_in structure (AF_INET or AF_INET6)
435 if ((*env)->GetArrayLength(env, addrArray) == 4) {
436 jint addr;
437 (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
438 addr = ((caddr[0] << 24) & 0xff000000);
439 addr |= ((caddr[1] << 16) & 0xff0000);
440 addr |= ((caddr[2] << 8) & 0xff00);
441 addr |= (caddr[3] & 0xff);
442 sa.sa4.sin_addr.s_addr = htonl(addr);
443 sa.sa4.sin_family = AF_INET;
444 len = sizeof(struct sockaddr_in);
445 } else {
446 (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
447 memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr));
448 sa.sa6.sin6_family = AF_INET6;
449 len = sizeof(struct sockaddr_in6);
450 }
451
452 if (getnameinfo(&sa.sa, len, host, sizeof(host), NULL, 0, NI_NAMEREQD)) {
453 JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
454 } else {
455 ret = (*env)->NewStringUTF(env, host);
456 if (ret == NULL) {
457 JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
458 }
459 }
460
461 return ret;
462}
463
464/**
465 * ping implementation using tcp port 7 (echo)
466 */
467static jboolean
468tcp_ping6(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout,
469 jint ttl)
470{
471 jint fd;
472 int connect_rv = -1;
473
474 // open a TCP socket
475 fd = socket(AF_INET6, SOCK_STREAM, 0);
476 if (fd == -1) {
477 // note: if you run out of fds, you may not be able to load
478 // the exception class, and get a NoClassDefFoundError instead.
479 NET_ThrowNew(env, errno, "Can't create socket");
480 return JNI_FALSE;
481 }
482
483 // set TTL
484 if (ttl > 0) {
485 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
486 }
487
488 // A network interface was specified, so let's bind to it.
489 if (netif != NULL) {
490 if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) {
491 NET_ThrowNew(env, errno, "Can't bind socket");
492 close(fd);
493 return JNI_FALSE;
494 }
495 }
496
497 // Make the socket non blocking so we can use select/poll.
498 SET_NONBLOCKING(fd);
499
500 sa->sa6.sin6_port = htons(7); // echo port
501 connect_rv = NET_Connect(fd, &sa->sa, sizeof(struct sockaddr_in6));
502
503 // connection established or refused immediately, either way it means
504 // we were able to reach the host!
505 if (connect_rv == 0 || errno == ECONNREFUSED) {
506 close(fd);
507 return JNI_TRUE;
508 }
509
510 switch (errno) {
511 case ENETUNREACH: // Network Unreachable
512 case EAFNOSUPPORT: // Address Family not supported
513 case EADDRNOTAVAIL: // address is not available on the remote machine
514#if defined(__linux__) || defined(_AIX)
515 // On some Linux versions, when a socket is bound to the loopback
516 // interface, connect will fail and errno will be set to EINVAL
517 // or EHOSTUNREACH. When that happens, don't throw an exception,
518 // just return false.
519 case EINVAL:
520 case EHOSTUNREACH: // No route to host
521#endif
522 close(fd);
523 return JNI_FALSE;
524 case EINPROGRESS: // this is expected as we'll probably have to wait
525 break;
526 default:
527 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
528 "connect failed");
529 close(fd);
530 return JNI_FALSE;
531 }
532
533 timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
534 if (timeout >= 0) {
535 // connection has been established, check for error condition
536 socklen_t optlen = (socklen_t)sizeof(connect_rv);
537 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
538 &optlen) <0)
539 {
540 connect_rv = errno;
541 }
542 if (connect_rv == 0 || connect_rv == ECONNREFUSED) {
543 close(fd);
544 return JNI_TRUE;
545 }
546 }
547 close(fd);
548 return JNI_FALSE;
549}
550
551/**
552 * ping implementation.
553 * Send an ICMP_ECHO_REQUEST packet every second until either the timeout
554 * expires or an answer is received.
555 * Returns true if an ECHO_REPLY is received, false otherwise.
556 */
557static jboolean
558ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,
559 jint timeout, jint ttl)
560{
561 jint n, size = 60 * 1024, tmout2, seq = 1;
562 socklen_t len;
563 unsigned char sendbuf[1500], recvbuf[1500];
564 struct icmp6_hdr *icmp6;
565 struct sockaddr_in6 sa_recv;
566 jchar pid;
567 struct timeval tv;
568 size_t plen = sizeof(struct icmp6_hdr) + sizeof(tv);
569
570#if defined(__linux__)
571 /**
572 * For some strange reason, the linux kernel won't calculate the
573 * checksum of ICMPv6 packets unless you set this socket option
574 */
575 int csum_offset = 2;
576 setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int));
577#endif
578
579 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
580
581 // sets the ttl (max number of hops)
582 if (ttl > 0) {
583 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
584 }
585
586 // a specific interface was specified, so let's bind the socket
587 // to that interface to ensure the requests are sent only through it.
588 if (netif != NULL) {
589 if (bind(fd, &netif->sa, sizeof(struct sockaddr_in6)) <0) {
590 NET_ThrowNew(env, errno, "Can't bind socket");
591 close(fd);
592 return JNI_FALSE;
593 }
594 }
595
596 // icmp_id is a 16 bit data type, therefore down cast the pid
597 pid = (jchar)getpid();
598
599 // Make the socket non blocking so we can use select
600 SET_NONBLOCKING(fd);
601 do {
602 // create the ICMP request
603 icmp6 = (struct icmp6_hdr *)sendbuf;
604 icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
605 icmp6->icmp6_code = 0;
606 // let's tag the ECHO packet with our pid so we can identify it
607 icmp6->icmp6_id = htons(pid);
608 icmp6->icmp6_seq = htons(seq);
609 seq++;
610 gettimeofday(&tv, NULL);
611 memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv));
612 icmp6->icmp6_cksum = 0;
613 // send it
614 n = sendto(fd, sendbuf, plen, 0, &sa->sa, sizeof(struct sockaddr_in6));
615 if (n < 0 && errno != EINPROGRESS) {
616#if defined(__linux__)
617 /*
618 * On some Linux versions, when a socket is bound to the loopback
619 * interface, sendto will fail and errno will be set to
620 * EINVAL or EHOSTUNREACH. When that happens, don't throw an
621 * exception, just return false.
622 */
623 if (errno != EINVAL && errno != EHOSTUNREACH) {
624 NET_ThrowNew(env, errno, "Can't send ICMP packet");
625 }
626#else
627 NET_ThrowNew(env, errno, "Can't send ICMP packet");
628#endif
629 close(fd);
630 return JNI_FALSE;
631 }
632
633 tmout2 = timeout > 1000 ? 1000 : timeout;
634 do {
635 tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
636 if (tmout2 >= 0) {
637 len = sizeof(sa_recv);
638 n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0,
639 (struct sockaddr *)&sa_recv, &len);
640 // check if we received enough data
641 if (n < (jint)sizeof(struct icmp6_hdr)) {
642 continue;
643 }
644 icmp6 = (struct icmp6_hdr *)recvbuf;
645 // We did receive something, but is it what we were expecting?
646 // I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and
647 // from the host that we are trying to determine is reachable.
648 if (icmp6->icmp6_type == ICMP6_ECHO_REPLY &&
649 (ntohs(icmp6->icmp6_id) == pid))
650 {
651 if (NET_IsEqual((jbyte *)&sa->sa6.sin6_addr,
652 (jbyte *)&sa_recv.sin6_addr)) {
653 close(fd);
654 return JNI_TRUE;
655 } else if (NET_IsZeroAddr((jbyte *)&sa->sa6.sin6_addr)) {
656 close(fd);
657 return JNI_TRUE;
658 }
659 }
660 }
661 } while (tmout2 > 0);
662 timeout -= 1000;
663 } while (timeout > 0);
664 close(fd);
665 return JNI_FALSE;
666}
667
668/*
669 * Class: java_net_Inet6AddressImpl
670 * Method: isReachable0
671 * Signature: ([bII[bI)Z
672 */
673JNIEXPORT jboolean JNICALL
674Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
675 jbyteArray addrArray, jint scope,
676 jint timeout, jbyteArray ifArray,
677 jint ttl, jint if_scope)
678{
679 jbyte caddr[16];
680 jint sz, fd;
681 SOCKETADDRESS sa, inf, *netif = NULL;
682
683 // If IPv6 is not enabled, then we can't reach an IPv6 address, can we?
684 // Actually, we probably shouldn't even get here.
685 if (!ipv6_available()) {
686 return JNI_FALSE;
687 }
688
689 // If it's an IPv4 address, ICMP won't work with IPv4 mapped address,
690 // therefore, let's delegate to the Inet4Address method.
691 sz = (*env)->GetArrayLength(env, addrArray);
692 if (sz == 4) {
693 return Java_java_net_Inet4AddressImpl_isReachable0(env, this,
694 addrArray, timeout,
695 ifArray, ttl);
696 }
697
698 // load address to SOCKETADDRESS
699 memset((char *)caddr, 0, 16);
700 (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
701 memset((char *)&sa, 0, sizeof(SOCKETADDRESS));
702 memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr));
703 sa.sa6.sin6_family = AF_INET6;
704 if (scope > 0) {
705 sa.sa6.sin6_scope_id = scope;
706 }
707
708 // load network interface address to SOCKETADDRESS, if specified
709 if (!(IS_NULL(ifArray))) {
710 memset((char *)caddr, 0, 16);
711 (*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr);
712 memset((char *)&inf, 0, sizeof(SOCKETADDRESS));
713 memcpy((void *)&inf.sa6.sin6_addr, caddr, sizeof(struct in6_addr));
714 inf.sa6.sin6_family = AF_INET6;
715 inf.sa6.sin6_scope_id = if_scope;
716 netif = &inf;
717 }
718
719 // Let's try to create a RAW socket to send ICMP packets.
720 // This usually requires "root" privileges, so it's likely to fail.
721 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
722 if (fd == -1) {
723 return tcp_ping6(env, &sa, netif, timeout, ttl);
724 } else {
725 // It didn't fail, so we can use ICMP_ECHO requests.
726 return ping6(env, fd, &sa, netif, timeout, ttl);
727 }
728}
729