1/*
2 * Copyright (c) 2000, 2016, 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/* the initial size of our hostent buffers */
46#ifndef NI_MAXHOST
47#define NI_MAXHOST 1025
48#endif
49
50#define SET_NONBLOCKING(fd) {       \
51    int flags = fcntl(fd, F_GETFL); \
52    flags |= O_NONBLOCK;            \
53    fcntl(fd, F_SETFL, flags);      \
54}
55
56/*
57 * Inet6AddressImpl
58 */
59
60/*
61 * Class:     java_net_Inet6AddressImpl
62 * Method:    getLocalHostName
63 * Signature: ()Ljava/lang/String;
64 */
65JNIEXPORT jstring JNICALL
66Java_java_net_Inet6AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
67    char hostname[NI_MAXHOST + 1];
68
69    hostname[0] = '\0';
70    if (gethostname(hostname, NI_MAXHOST) != 0) {
71        strcpy(hostname, "localhost");
72#if defined(__solaris__)
73    } else {
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
80        hostname[NI_MAXHOST] = '\0';
81        memset(&hints, 0, sizeof(hints));
82        hints.ai_flags = AI_CANONNAME;
83        hints.ai_family = AF_UNSPEC;
84
85        if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
86            getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST,
87                        NULL, 0, NI_NAMEREQD);
88            freeaddrinfo(res);
89        }
90    }
91#else
92    } else {
93        // make sure string is null-terminated
94        hostname[NI_MAXHOST] = '\0';
95    }
96#endif
97    return (*env)->NewStringUTF(env, hostname);
98}
99
100#if defined(MACOSX)
101/* also called from Inet4AddressImpl.c */
102__private_extern__ jobjectArray
103lookupIfLocalhost(JNIEnv *env, const char *hostname, jboolean includeV6)
104{
105    jobjectArray result = NULL;
106    char myhostname[NI_MAXHOST+1];
107    struct ifaddrs *ifa = NULL;
108    int familyOrder = 0;
109    int count = 0, i, j;
110    int addrs4 = 0, addrs6 = 0, numV4Loopbacks = 0, numV6Loopbacks = 0;
111    jboolean includeLoopback = JNI_FALSE;
112    jobject name;
113
114    initInetAddressIDs(env);
115    JNU_CHECK_EXCEPTION_RETURN(env, NULL);
116
117    /* If the requested name matches this host's hostname, return IP addresses
118     * from all attached interfaces. (#2844683 et al) This prevents undesired
119     * PPP dialup, but may return addresses that don't actually correspond to
120     * the name (if the name actually matches something in DNS etc.
121     */
122    myhostname[0] = '\0';
123    if (gethostname(myhostname, NI_MAXHOST) == -1) {
124        /* Something went wrong, maybe networking is not setup? */
125        return NULL;
126    }
127    myhostname[NI_MAXHOST] = '\0';
128
129    if (strcmp(myhostname, hostname) != 0) {
130        // Non-self lookup
131        return NULL;
132    }
133
134    if (getifaddrs(&ifa) != 0) {
135        NET_ThrowNew(env, errno, "Can't get local interface addresses");
136        return NULL;
137    }
138
139    name = (*env)->NewStringUTF(env, hostname);
140    if (name == NULL) {
141        freeifaddrs(ifa);
142        return NULL;
143    }
144
145    /* Iterate over the interfaces, and total up the number of IPv4 and IPv6
146     * addresses we have. Also keep a count of loopback addresses. We need to
147     * exclude them in the normal case, but return them if we don't get an IP
148     * address.
149     */
150    struct ifaddrs *iter = ifa;
151    while (iter) {
152        int family = iter->ifa_addr->sa_family;
153        if (iter->ifa_name[0] != '\0' && iter->ifa_addr)
154        {
155            jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK;
156            if (family == AF_INET) {
157                addrs4++;
158                if (isLoopback) numV4Loopbacks++;
159            } else if (family == AF_INET6 && includeV6) {
160                addrs6++;
161                if (isLoopback) numV6Loopbacks++;
162            } // else we don't care, e.g. AF_LINK
163        }
164        iter = iter->ifa_next;
165    }
166
167    if (addrs4 == numV4Loopbacks && addrs6 == numV6Loopbacks) {
168        // We don't have a real IP address, just loopback. We need to include
169        // loopback in our results.
170        includeLoopback = JNI_TRUE;
171    }
172
173    /* Create and fill the Java array. */
174    int arraySize = addrs4 + addrs6 -
175        (includeLoopback ? 0 : (numV4Loopbacks + numV6Loopbacks));
176    result = (*env)->NewObjectArray(env, arraySize, ia_class, NULL);
177    if (!result) goto done;
178
179    if ((*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID)) {
180        i = includeLoopback ? addrs6 : (addrs6 - numV6Loopbacks);
181        j = 0;
182    } else {
183        i = 0;
184        j = includeLoopback ? addrs4 : (addrs4 - numV4Loopbacks);
185    }
186
187    // Now loop around the ifaddrs
188    iter = ifa;
189    while (iter != NULL) {
190        jboolean isLoopback = iter->ifa_flags & IFF_LOOPBACK;
191        int family = iter->ifa_addr->sa_family;
192
193        if (iter->ifa_name[0] != '\0' && iter->ifa_addr &&
194            (family == AF_INET || (family == AF_INET6 && includeV6)) &&
195            (!isLoopback || includeLoopback))
196        {
197            int port;
198            int index = (family == AF_INET) ? i++ : j++;
199            jobject o = NET_SockaddrToInetAddress(env,
200                            (SOCKETADDRESS *)iter->ifa_addr, &port);
201            if (!o) {
202                freeifaddrs(ifa);
203                if (!(*env)->ExceptionCheck(env))
204                    JNU_ThrowOutOfMemoryError(env, "Object allocation failed");
205                return NULL;
206            }
207            setInetAddress_hostName(env, o, name);
208            (*env)->SetObjectArrayElement(env, result, index, o);
209            (*env)->DeleteLocalRef(env, o);
210        }
211        iter = iter->ifa_next;
212    }
213
214  done:
215    freeifaddrs(ifa);
216
217    return result;
218}
219#endif
220
221/*
222 * Class:     java_net_Inet6AddressImpl
223 * Method:    lookupAllHostAddr
224 * Signature: (Ljava/lang/String;)[[B
225 */
226JNIEXPORT jobjectArray JNICALL
227Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
228                                                 jstring host) {
229    jobjectArray ret = NULL;
230    const char *hostname;
231    int error = 0;
232    struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,
233        *iterator;
234
235    initInetAddressIDs(env);
236    JNU_CHECK_EXCEPTION_RETURN(env, NULL);
237
238    if (IS_NULL(host)) {
239        JNU_ThrowNullPointerException(env, "host argument is null");
240        return NULL;
241    }
242    hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
243    CHECK_NULL_RETURN(hostname, NULL);
244
245    // try once, with our static buffer
246    memset(&hints, 0, sizeof(hints));
247    hints.ai_flags = AI_CANONNAME;
248    hints.ai_family = AF_UNSPEC;
249
250    error = getaddrinfo(hostname, NULL, &hints, &res);
251
252    if (error) {
253#if defined(MACOSX)
254        // if getaddrinfo fails try getifaddrs
255        ret = lookupIfLocalhost(env, hostname, JNI_TRUE);
256        if (ret != NULL || (*env)->ExceptionCheck(env)) {
257            goto cleanupAndReturn;
258        }
259#endif
260        // report error
261        NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error);
262        goto cleanupAndReturn;
263    } else {
264        int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0,
265            inet6Index = 0, originalIndex = 0;
266        int addressPreference =
267            (*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);;
268        iterator = res;
269        while (iterator != NULL) {
270            // skip duplicates
271            int skip = 0;
272            struct addrinfo *iteratorNew = resNew;
273            while (iteratorNew != NULL) {
274                if (iterator->ai_family == iteratorNew->ai_family &&
275                    iterator->ai_addrlen == iteratorNew->ai_addrlen) {
276                    if (iteratorNew->ai_family == AF_INET) { /* AF_INET */
277                        struct sockaddr_in *addr1, *addr2;
278                        addr1 = (struct sockaddr_in *)iterator->ai_addr;
279                        addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;
280                        if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {
281                            skip = 1;
282                            break;
283                        }
284                    } else {
285                        int t;
286                        struct sockaddr_in6 *addr1, *addr2;
287                        addr1 = (struct sockaddr_in6 *)iterator->ai_addr;
288                        addr2 = (struct sockaddr_in6 *)iteratorNew->ai_addr;
289
290                        for (t = 0; t < 16; t++) {
291                            if (addr1->sin6_addr.s6_addr[t] !=
292                                addr2->sin6_addr.s6_addr[t]) {
293                                break;
294                            }
295                        }
296                        if (t < 16) {
297                            iteratorNew = iteratorNew->ai_next;
298                            continue;
299                        } else {
300                            skip = 1;
301                            break;
302                        }
303                    }
304                } else if (iterator->ai_family != AF_INET &&
305                           iterator->ai_family != AF_INET6) {
306                    // we can't handle other family types
307                    skip = 1;
308                    break;
309                }
310                iteratorNew = iteratorNew->ai_next;
311            }
312
313            if (!skip) {
314                struct addrinfo *next
315                    = (struct addrinfo *)malloc(sizeof(struct addrinfo));
316                if (!next) {
317                    JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
318                    ret = NULL;
319                    goto cleanupAndReturn;
320                }
321                memcpy(next, iterator, sizeof(struct addrinfo));
322                next->ai_next = NULL;
323                if (resNew == NULL) {
324                    resNew = next;
325                } else {
326                    last->ai_next = next;
327                }
328                last = next;
329                i++;
330                if (iterator->ai_family == AF_INET) {
331                    inetCount++;
332                } else if (iterator->ai_family == AF_INET6) {
333                    inet6Count++;
334                }
335            }
336            iterator = iterator->ai_next;
337        }
338
339        // allocate array - at this point i contains the number of addresses
340        ret = (*env)->NewObjectArray(env, i, ia_class, NULL);
341        if (IS_NULL(ret)) {
342            /* we may have memory to free at the end of this */
343            goto cleanupAndReturn;
344        }
345
346        if (addressPreference == java_net_InetAddress_PREFER_IPV6_VALUE) {
347            inetIndex = inet6Count;
348            inet6Index = 0;
349        } else if (addressPreference == java_net_InetAddress_PREFER_IPV4_VALUE) {
350            inetIndex = 0;
351            inet6Index = inetCount;
352        } else if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
353            inetIndex = inet6Index = originalIndex = 0;
354        }
355
356        iterator = resNew;
357        while (iterator != NULL) {
358            if (iterator->ai_family == AF_INET) {
359                jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
360                if (IS_NULL(iaObj)) {
361                    ret = NULL;
362                    goto cleanupAndReturn;
363                }
364                setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
365                setInetAddress_hostName(env, iaObj, host);
366                (*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);
367                inetIndex++;
368            } else if (iterator->ai_family == AF_INET6) {
369                jint scope = 0;
370                jboolean ret1;
371                jobject iaObj = (*env)->NewObject(env, ia6_class, ia6_ctrID);
372                if (IS_NULL(iaObj)) {
373                    ret = NULL;
374                    goto cleanupAndReturn;
375                }
376                ret1 = setInet6Address_ipaddress(env, iaObj, (char *)&(((struct sockaddr_in6*)iterator->ai_addr)->sin6_addr));
377                if (ret1 == JNI_FALSE) {
378                    ret = NULL;
379                    goto cleanupAndReturn;
380                }
381                scope = ((struct sockaddr_in6 *)iterator->ai_addr)->sin6_scope_id;
382                if (scope != 0) { // zero is default value, no need to set
383                    setInet6Address_scopeid(env, iaObj, scope);
384                }
385                setInetAddress_hostName(env, iaObj, host);
386                (*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);
387                inet6Index++;
388            }
389            if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
390                originalIndex++;
391                inetIndex = inet6Index = 0;
392            }
393            iterator = iterator->ai_next;
394        }
395    }
396cleanupAndReturn:
397    JNU_ReleaseStringPlatformChars(env, host, hostname);
398    while (resNew != NULL) {
399        last = resNew;
400        resNew = resNew->ai_next;
401        free(last);
402    }
403    if (res != NULL) {
404        freeaddrinfo(res);
405    }
406    return ret;
407}
408
409/*
410 * Class:     java_net_Inet6AddressImpl
411 * Method:    getHostByAddr
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.
418 */
419JNIEXPORT jstring JNICALL
420Java_java_net_Inet6AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
421                                             jbyteArray addrArray) {
422    jstring ret = NULL;
423    char host[NI_MAXHOST + 1];
424    int len = 0;
425    jbyte caddr[16];
426    SOCKETADDRESS sa;
427
428    memset((void *)&sa, 0, sizeof(SOCKETADDRESS));
429
430    // construct a sockaddr_in structure (AF_INET or AF_INET6)
431    if ((*env)->GetArrayLength(env, addrArray) == 4) {
432        jint addr;
433        (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
434        addr = ((caddr[0] << 24) & 0xff000000);
435        addr |= ((caddr[1] << 16) & 0xff0000);
436        addr |= ((caddr[2] << 8) & 0xff00);
437        addr |= (caddr[3] & 0xff);
438        sa.sa4.sin_addr.s_addr = htonl(addr);
439        sa.sa4.sin_family = AF_INET;
440        len = sizeof(struct sockaddr_in);
441    } else {
442        (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
443        memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr));
444        sa.sa6.sin6_family = AF_INET6;
445        len = sizeof(struct sockaddr_in6);
446    }
447
448    if (getnameinfo(&sa.sa, len, host, NI_MAXHOST, NULL, 0, NI_NAMEREQD)) {
449        JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
450    } else {
451        ret = (*env)->NewStringUTF(env, host);
452        if (ret == NULL) {
453            JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
454        }
455    }
456
457    return ret;
458}
459
460/**
461 * ping implementation using tcp port 7 (echo)
462 */
463static jboolean
464tcp_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;
545}
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 */
553static jboolean
554ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif,
555      jint timeout, jint ttl)
556{
557    jint n, size = 60 * 1024, tmout2, seq = 1;
558    socklen_t len;
559    unsigned char sendbuf[1500], recvbuf[1500];
560    struct icmp6_hdr *icmp6;
561    struct sockaddr_in6 sa_recv;
562    jchar pid;
563    struct timeval tv;
564    size_t plen = sizeof(struct icmp6_hdr) + sizeof(tv);
565
566#if defined(__linux__)
567    /**
568     * For some strange reason, the linux kernel won't calculate the
569     * checksum of ICMPv6 packets unless you set this socket option
570     */
571    int csum_offset = 2;
572    setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int));
573#endif
574
575    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
576
577    // sets the ttl (max number of hops)
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
593    pid = (jchar)getpid();
594
595    // Make the socket non blocking so we can use select
596    SET_NONBLOCKING(fd);
597    do {
598        // create the ICMP request
599        icmp6 = (struct icmp6_hdr *)sendbuf;
600        icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
601        icmp6->icmp6_code = 0;
602        // let's tag the ECHO packet with our pid so we can identify it
603        icmp6->icmp6_id = htons(pid);
604        icmp6->icmp6_seq = htons(seq);
605        seq++;
606        gettimeofday(&tv, NULL);
607        memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv));
608        icmp6->icmp6_cksum = 0;
609        // send it
610        n = sendto(fd, sendbuf, plen, 0, &sa->sa, sizeof(struct sockaddr_in6));
611        if (n < 0 && errno != EINPROGRESS) {
612#if defined(__linux__)
613            /*
614             * On some Linux versions, when a socket is bound to the loopback
615             * interface, sendto will fail and errno will be set to
616             * EINVAL or EHOSTUNREACH. When that happens, don't throw an
617             * exception, just return false.
618             */
619            if (errno != EINVAL && errno != EHOSTUNREACH) {
620                NET_ThrowNew(env, errno, "Can't send ICMP packet");
621            }
622#else
623            NET_ThrowNew(env, errno, "Can't send ICMP packet");
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                }
656            }
657        } while (tmout2 > 0);
658        timeout -= 1000;
659    } while (timeout > 0);
660    close(fd);
661    return JNI_FALSE;
662}
663
664/*
665 * Class:     java_net_Inet6AddressImpl
666 * Method:    isReachable0
667 * Signature: ([bII[bI)Z
668 */
669JNIEXPORT jboolean JNICALL
670Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
671                                            jbyteArray addrArray, jint scope,
672                                            jint timeout, jbyteArray ifArray,
673                                            jint ttl, jint if_scope)
674{
675    jbyte caddr[16];
676    jint sz, fd;
677    SOCKETADDRESS sa, inf, *netif = NULL;
678
679    // If IPv6 is not enabled, then we can't reach an IPv6 address, can we?
680    // Actually, we probably shouldn't even get here.
681    if (!ipv6_available()) {
682        return JNI_FALSE;
683    }
684
685    // If it's an IPv4 address, ICMP won't work with IPv4 mapped address,
686    // therefore, let's delegate to the Inet4Address method.
687    sz = (*env)->GetArrayLength(env, addrArray);
688    if (sz == 4) {
689        return Java_java_net_Inet4AddressImpl_isReachable0(env, this,
690                                                           addrArray, timeout,
691                                                           ifArray, ttl);
692    }
693
694    // load address to SOCKETADDRESS
695    memset((char *)caddr, 0, 16);
696    (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
697    memset((char *)&sa, 0, sizeof(SOCKETADDRESS));
698    memcpy((void *)&sa.sa6.sin6_addr, caddr, sizeof(struct in6_addr));
699    sa.sa6.sin6_family = AF_INET6;
700    if (scope > 0) {
701        sa.sa6.sin6_scope_id = scope;
702#if defined(__linux__)
703    } else {
704        sa.sa6.sin6_scope_id = getDefaultIPv6Interface(&sa.sa6.sin6_addr);
705#endif
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