Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (c) 2000, 2012, 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 
     26 #include <errno.h>
     27 #include <sys/time.h>
     28 #include <sys/types.h>
     29 #include <sys/socket.h>
     30 #include <netinet/in.h>
     31 #include <netdb.h>
     32 #include <string.h>
     33 #include <strings.h>
     34 #include <stdlib.h>
     35 #include <ctype.h>
     36 #ifdef _ALLBSD_SOURCE
     37 #include <unistd.h> /* gethostname */
     38 #endif
     39 
     40 #include "jvm.h"
     41 #include "jni_util.h"
     42 #include "net_util.h"
     43 #ifndef IPV6_DEFS_H
     44 #include <netinet/icmp6.h>
     45 #endif
     46 
     47 #include "java_net_Inet4AddressImpl.h"
     48 #include "JNIHelp.h"
     49 
     50 #define NATIVE_METHOD(className, functionName, signature) \
     51 { #functionName, signature, (void*)(className ## _ ## functionName) }
     52 
     53 /* the initial size of our hostent buffers */
     54 #ifndef NI_MAXHOST
     55 #define NI_MAXHOST 1025
     56 #endif
     57 
     58 
     59 /************************************************************************
     60  * Inet6AddressImpl
     61  */
     62 
     63 static jclass ni_iacls;
     64 static jclass ni_ia4cls;
     65 static jclass ni_ia6cls;
     66 static jmethodID ni_ia4ctrID;
     67 static jmethodID ni_ia6ctrID;
     68 static jfieldID ni_ia6ipaddressID;
     69 static int initialized = 0;
     70 
     71 /*
     72  * Class:     java_net_Inet6AddressImpl
     73  * Method:    getHostByAddr
     74  * Signature: (I)Ljava/lang/String;
     75  */
     76 JNIEXPORT jstring JNICALL
     77 Inet6AddressImpl_getHostByAddr0(JNIEnv *env, jobject this,
     78                                              jbyteArray addrArray) {
     79 
     80     jstring ret = NULL;
     81 
     82 #ifdef AF_INET6
     83     char host[NI_MAXHOST+1];
     84     int error = 0;
     85     int len = 0;
     86     jbyte caddr[16];
     87 
     88     if (NET_addrtransAvailable()) {
     89         struct sockaddr_in him4;
     90         struct sockaddr_in6 him6;
     91         struct sockaddr *sa;
     92 
     93         /*
     94          * For IPv4 addresses construct a sockaddr_in structure.
     95          */
     96         if ((*env)->GetArrayLength(env, addrArray) == 4) {
     97             jint addr;
     98             (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
     99             addr = ((caddr[0]<<24) & 0xff000000);
    100             addr |= ((caddr[1] <<16) & 0xff0000);
    101             addr |= ((caddr[2] <<8) & 0xff00);
    102             addr |= (caddr[3] & 0xff);
    103             memset((void *) &him4, 0, sizeof(him4));
    104             him4.sin_addr.s_addr = (uint32_t) htonl(addr);
    105             him4.sin_family = AF_INET;
    106             sa = (struct sockaddr *) &him4;
    107             len = sizeof(him4);
    108         } else {
    109             /*
    110              * For IPv6 address construct a sockaddr_in6 structure.
    111              */
    112             (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
    113             memset((void *) &him6, 0, sizeof(him6));
    114             memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) );
    115             him6.sin6_family = AF_INET6;
    116             sa = (struct sockaddr *) &him6 ;
    117             len = sizeof(him6) ;
    118         }
    119 
    120         error = (*getnameinfo_ptr)(sa, len, host, NI_MAXHOST, NULL, 0,
    121                                    NI_NAMEREQD);
    122 
    123         if (!error) {
    124             ret = (*env)->NewStringUTF(env, host);
    125         }
    126     }
    127 #endif /* AF_INET6 */
    128 
    129     if (ret == NULL) {
    130         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL);
    131     }
    132 
    133     return ret;
    134 }
    135 
    136 #define SET_NONBLOCKING(fd) {           \
    137         int flags = fcntl(fd, F_GETFL); \
    138         flags |= O_NONBLOCK;            \
    139         fcntl(fd, F_SETFL, flags);      \
    140 }
    141 
    142 #ifdef AF_INET6
    143 static jboolean
    144 ping6(JNIEnv *env, jint fd, struct sockaddr_in6* him, jint timeout,
    145       struct sockaddr_in6* netif, jint ttl) {
    146     jint size;
    147     jint n;
    148     socklen_t len;
    149     char sendbuf[1500];
    150     unsigned char recvbuf[1500];
    151     struct icmp6_hdr *icmp6;
    152     struct sockaddr_in6 sa_recv;
    153     jbyte *caddr, *recv_caddr;
    154     jchar pid;
    155     jint tmout2, seq = 1;
    156     struct timeval tv;
    157     size_t plen;
    158 
    159 #ifdef __linux__
    160     {
    161     int csum_offset;
    162     /**
    163      * For some strange reason, the linux kernel won't calculate the
    164      * checksum of ICMPv6 packets unless you set this socket option
    165      */
    166     csum_offset = 2;
    167     setsockopt(fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sizeof(int));
    168     }
    169 #endif
    170 
    171     caddr = (jbyte *)&(him->sin6_addr);
    172 
    173     /* icmp_id is a 16 bit data type, therefore down cast the pid */
    174     pid = (jchar)getpid();
    175     size = 60*1024;
    176     setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
    177     if (ttl > 0) {
    178       setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
    179     }
    180     if (netif != NULL) {
    181       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) {
    182         NET_ThrowNew(env, errno, "Can't bind socket");
    183         untagSocket(env, fd);
    184         close(fd);
    185         return JNI_FALSE;
    186       }
    187     }
    188     SET_NONBLOCKING(fd);
    189 
    190     do {
    191       icmp6 = (struct icmp6_hdr *) sendbuf;
    192       icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
    193       icmp6->icmp6_code = 0;
    194       /* let's tag the ECHO packet with our pid so we can identify it */
    195       icmp6->icmp6_id = htons(pid);
    196       icmp6->icmp6_seq = htons(seq);
    197       seq++;
    198       icmp6->icmp6_cksum = 0;
    199       gettimeofday(&tv, NULL);
    200       memcpy(sendbuf + sizeof(struct icmp6_hdr), &tv, sizeof(tv));
    201       plen = sizeof(struct icmp6_hdr) + sizeof(tv);
    202       n = sendto(fd, sendbuf, plen, 0, (struct sockaddr*) him, sizeof(struct sockaddr_in6));
    203       if (n < 0 && errno != EINPROGRESS) {
    204 #ifdef __linux__
    205         if (errno != EINVAL && errno != EHOSTUNREACH)
    206           /*
    207            * On some Linuxes, when bound to the loopback interface, sendto
    208            * will fail and errno will be set to EINVAL or EHOSTUNREACH.
    209            * When that happens, don't throw an exception, just return false.
    210            */
    211 #endif /*__linux__ */
    212         NET_ThrowNew(env, errno, "Can't send ICMP packet");
    213         untagSocket(env, fd);
    214         close(fd);
    215         return JNI_FALSE;
    216       }
    217 
    218       tmout2 = timeout > 1000 ? 1000 : timeout;
    219       do {
    220         tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
    221 
    222         if (tmout2 >= 0) {
    223           len = sizeof(sa_recv);
    224           n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*) &sa_recv, &len);
    225           icmp6 = (struct icmp6_hdr *) (recvbuf);
    226           recv_caddr = (jbyte *)&(sa_recv.sin6_addr);
    227           /*
    228            * We did receive something, but is it what we were expecting?
    229            * I.E.: An ICMP6_ECHO_REPLY packet with the proper PID and
    230            *       from the host that we are trying to determine is reachable.
    231            */
    232           if (n >= 8 && icmp6->icmp6_type == ICMP6_ECHO_REPLY &&
    233               (ntohs(icmp6->icmp6_id) == pid)) {
    234             if (NET_IsEqual(caddr, recv_caddr)) {
    235               untagSocket(env, fd);
    236               close(fd);
    237               return JNI_TRUE;
    238             }
    239             if (NET_IsZeroAddr(caddr)) {
    240               untagSocket(env, fd);
    241               close(fd);
    242               return JNI_TRUE;
    243             }
    244           }
    245         }
    246       } while (tmout2 > 0);
    247       timeout -= 1000;
    248     } while (timeout > 0);
    249     untagSocket(env, fd);
    250     close(fd);
    251     return JNI_FALSE;
    252 }
    253 #endif /* AF_INET6 */
    254 
    255 /*
    256  * Class:     java_net_Inet6AddressImpl
    257  * Method:    isReachable0
    258  * Signature: ([bII[bI)Z
    259  */
    260 JNIEXPORT jboolean JNICALL
    261 Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
    262                                            jbyteArray addrArray,
    263                                            jint scope,
    264                                            jint timeout,
    265                                            jbyteArray ifArray,
    266                                            jint ttl, jint if_scope) {
    267 #ifdef AF_INET6
    268     jbyte caddr[16];
    269     jint fd, sz;
    270     struct sockaddr_in6 him6;
    271     struct sockaddr_in6 inf6;
    272     struct sockaddr_in6* netif = NULL;
    273     int len = 0;
    274     int connect_rv = -1;
    275 
    276     /*
    277      * If IPv6 is not enable, then we can't reach an IPv6 address, can we?
    278      */
    279     if (!ipv6_available()) {
    280       return JNI_FALSE;
    281     }
    282     /*
    283      * If it's an IPv4 address, ICMP won't work with IPv4 mapped address,
    284      * therefore, let's delegate to the Inet4Address method.
    285      */
    286     sz = (*env)->GetArrayLength(env, addrArray);
    287     if (sz == 4) {
    288       return Inet4AddressImpl_isReachable0(env, this,
    289                                                          addrArray,
    290                                                          timeout,
    291                                                          ifArray, ttl);
    292     }
    293 
    294     memset((void *) caddr, 0, 16);
    295     memset((void *) &him6, 0, sizeof(him6));
    296     (*env)->GetByteArrayRegion(env, addrArray, 0, 16, caddr);
    297     memcpy((void *)&(him6.sin6_addr), caddr, sizeof(struct in6_addr) );
    298     him6.sin6_family = AF_INET6;
    299 
    300     // Android-change: Don't try and figure out a default scope ID if one isn't
    301     // set. It's only useful for link local addresses anyway, and callers are
    302     // expected to call isReachable with a specific NetworkInterface if they
    303     // want to query the reachability of an address that's local to that IF.
    304     if (scope > 0)
    305       him6.sin6_scope_id = scope;
    306     len = sizeof(struct sockaddr_in6);
    307     /*
    308      * If a network interface was specified, let's create the address
    309      * for it.
    310      */
    311     if (!(IS_NULL(ifArray))) {
    312       memset((void *) caddr, 0, 16);
    313       memset((void *) &inf6, 0, sizeof(inf6));
    314       (*env)->GetByteArrayRegion(env, ifArray, 0, 16, caddr);
    315       memcpy((void *)&(inf6.sin6_addr), caddr, sizeof(struct in6_addr) );
    316       inf6.sin6_family = AF_INET6;
    317       inf6.sin6_scope_id = if_scope;
    318       netif = &inf6;
    319     }
    320     /*
    321      * If we can create a RAW socket, then when can use the ICMP ECHO_REQUEST
    322      * otherwise we'll try a tcp socket to the Echo port (7).
    323      * Note that this is empiric, and not connecting could mean it's blocked
    324      * or the echo servioe has been disabled.
    325      */
    326 
    327     fd = JVM_Socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
    328 
    329     if (fd != -1) { /* Good to go, let's do a ping */
    330         tagSocket(env, fd);
    331         return ping6(env, fd, &him6, timeout, netif, ttl);
    332     }
    333 
    334     /* No good, let's fall back on TCP */
    335     fd = JVM_Socket(AF_INET6, SOCK_STREAM, 0);
    336     if (fd == JVM_IO_ERR) {
    337         /* note: if you run out of fds, you may not be able to load
    338          * the exception class, and get a NoClassDefFoundError
    339          * instead.
    340          */
    341         NET_ThrowNew(env, errno, "Can't create socket");
    342         return JNI_FALSE;
    343     }
    344     tagSocket(env, fd);
    345 
    346     if (ttl > 0) {
    347       setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
    348     }
    349 
    350     /*
    351      * A network interface was specified, so let's bind to it.
    352      */
    353     if (netif != NULL) {
    354       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in6)) <0) {
    355         NET_ThrowNew(env, errno, "Can't bind socket");
    356         untagSocket(env, fd);
    357         close(fd);
    358         return JNI_FALSE;
    359       }
    360     }
    361     SET_NONBLOCKING(fd);
    362 
    363     /* no need to use NET_Connect as non-blocking */
    364     him6.sin6_port = htons((short) 7); /* Echo port */
    365     connect_rv = JVM_Connect(fd, (struct sockaddr *)&him6, len);
    366 
    367     /**
    368      * connection established or refused immediately, either way it means
    369      * we were able to reach the host!
    370      */
    371     if (connect_rv == 0 || errno == ECONNREFUSED) {
    372         untagSocket(env, fd);
    373         close(fd);
    374         return JNI_TRUE;
    375     } else {
    376         int optlen;
    377 
    378         switch (errno) {
    379         case ENETUNREACH: /* Network Unreachable */
    380         case EAFNOSUPPORT: /* Address Family not supported */
    381         case EADDRNOTAVAIL: /* address is not available on  the  remote machine */
    382 #ifdef __linux__
    383         case EINVAL:
    384         case EHOSTUNREACH:
    385           /*
    386            * On some Linuxes, when bound to the loopback interface, connect
    387            * will fail and errno will be set to EINVAL or EHOSTUNREACH.
    388            * When that happens, don't throw an exception, just return false.
    389            */
    390 #endif /* __linux__ */
    391           untagSocket(env, fd);
    392           close(fd);
    393           return JNI_FALSE;
    394         }
    395 
    396         if (errno != EINPROGRESS) {
    397             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
    398                                          "connect failed");
    399             untagSocket(env, fd);
    400             close(fd);
    401             return JNI_FALSE;
    402         }
    403 
    404         timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
    405 
    406         if (timeout >= 0) {
    407           /* has connection been established */
    408           optlen = sizeof(connect_rv);
    409           if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
    410                              &optlen) <0) {
    411             connect_rv = errno;
    412           }
    413           if (connect_rv == 0 || ECONNREFUSED) {
    414             untagSocket(env, fd);
    415             close(fd);
    416             return JNI_TRUE;
    417           }
    418         }
    419         untagSocket(env, fd);
    420         close(fd);
    421         return JNI_FALSE;
    422     }
    423 #else /* AF_INET6 */
    424     return JNI_FALSE;
    425 #endif /* AF_INET6 */
    426 }
    427 
    428 static JNINativeMethod gMethods[] = {
    429   NATIVE_METHOD(Inet6AddressImpl, isReachable0, "([BII[BII)Z"),
    430   NATIVE_METHOD(Inet6AddressImpl, getHostByAddr0, "([B)Ljava/lang/String;"),
    431 };
    432 
    433 void register_java_net_Inet6AddressImpl(JNIEnv* env) {
    434   jniRegisterNativeMethods(env, "java/net/Inet6AddressImpl", gMethods, NELEM(gMethods));
    435 }
    436