Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (c) 2000, 2011, 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_systm.h>
     31 #include <netinet/in.h>
     32 #include <netinet/ip.h>
     33 #include <netinet/ip_icmp.h>
     34 #include <netdb.h>
     35 #include <string.h>
     36 #include <stdlib.h>
     37 #include <ctype.h>
     38 
     39 #ifdef _ALLBSD_SOURCE
     40 #include <unistd.h>
     41 #include <sys/param.h>
     42 #endif
     43 
     44 #include "jvm.h"
     45 #include "jni_util.h"
     46 #include "net_util.h"
     47 
     48 #include "JNIHelp.h"
     49 
     50 #define NATIVE_METHOD(className, functionName, signature) \
     51 { #functionName, signature, (void*)(className ## _ ## functionName) }
     52 
     53 //#if defined(__GLIBC__) || (defined(__FreeBSD__) && (__FreeBSD_version >= 601104))
     54 #define HAS_GLIBC_GETHOSTBY_R   1
     55 //#endif
     56 
     57 #define SET_NONBLOCKING(fd) {           \
     58         int flags = fcntl(fd, F_GETFL); \
     59         flags |= O_NONBLOCK;            \
     60         fcntl(fd, F_SETFL, flags);      \
     61 }
     62 
     63 /**
     64  * ping implementation.
     65  * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
     66  * expires or a answer is received.
     67  * Returns true is an ECHO_REPLY is received, otherwise, false.
     68  */
     69 static jboolean
     70 ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
     71       struct sockaddr_in* netif, jint ttl) {
     72     jint size;
     73     jint n, hlen1, icmplen;
     74     socklen_t len;
     75     char sendbuf[1500];
     76     char recvbuf[1500];
     77     struct icmp *icmp;
     78     struct ip *ip;
     79     struct sockaddr_in sa_recv;
     80     jchar pid;
     81     jint tmout2, seq = 1;
     82     struct timeval tv;
     83     size_t plen;
     84 
     85     /* icmp_id is a 16 bit data type, therefore down cast the pid */
     86     pid = (jchar)getpid();
     87     size = 60*1024;
     88     setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
     89     /*
     90      * sets the ttl (max number of hops)
     91      */
     92     if (ttl > 0) {
     93       setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
     94     }
     95     /*
     96      * a specific interface was specified, so let's bind the socket
     97      * to that interface to ensure the requests are sent only through it.
     98      */
     99     if (netif != NULL) {
    100       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
    101         NET_ThrowNew(env, errno, "Can't bind socket");
    102         untagSocket(env, fd);
    103         close(fd);
    104         return JNI_FALSE;
    105       }
    106     }
    107     /*
    108      * Make the socket non blocking so we can use select
    109      */
    110     SET_NONBLOCKING(fd);
    111     do {
    112       /*
    113        * create the ICMP request
    114        */
    115       icmp = (struct icmp *) sendbuf;
    116       icmp->icmp_type = ICMP_ECHO;
    117       icmp->icmp_code = 0;
    118       icmp->icmp_id = htons(pid);
    119       icmp->icmp_seq = htons(seq);
    120       seq++;
    121       gettimeofday(&tv, NULL);
    122       memcpy(icmp->icmp_data, &tv, sizeof(tv));
    123       plen = ICMP_ADVLENMIN + sizeof(tv);
    124       icmp->icmp_cksum = 0;
    125       icmp->icmp_cksum = in_cksum((u_short *)icmp, plen);
    126       /*
    127        * send it
    128        */
    129       n = sendto(fd, sendbuf, plen, 0, (struct sockaddr *)him,
    130                  sizeof(struct sockaddr));
    131       if (n < 0 && errno != EINPROGRESS ) {
    132 #ifdef __linux__
    133         if (errno != EINVAL && errno != EHOSTUNREACH)
    134           /*
    135            * On some Linuxes, when bound to the loopback interface, sendto
    136            * will fail and errno will be set to EINVAL or EHOSTUNREACH.
    137            * When that happens, don't throw an exception, just return false.
    138            */
    139 #endif /*__linux__ */
    140           NET_ThrowNew(env, errno, "Can't send ICMP packet");
    141         untagSocket(env, fd);
    142         close(fd);
    143         return JNI_FALSE;
    144       }
    145 
    146       tmout2 = timeout > 1000 ? 1000 : timeout;
    147       do {
    148         tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
    149         if (tmout2 >= 0) {
    150           len = sizeof(sa_recv);
    151           n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&sa_recv, &len);
    152           ip = (struct ip*) recvbuf;
    153           hlen1 = (ip->ip_hl) << 2;
    154           icmp = (struct icmp *) (recvbuf + hlen1);
    155           icmplen = n - hlen1;
    156           /*
    157            * We did receive something, but is it what we were expecting?
    158            * I.E.: A ICMP_ECHOREPLY packet with the proper PID.
    159            */
    160           if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY
    161                && (ntohs(icmp->icmp_id) == pid)) {
    162             if ((him->sin_addr.s_addr == sa_recv.sin_addr.s_addr)) {
    163               untagSocket(env, fd);
    164               close(fd);
    165               return JNI_TRUE;
    166             }
    167 
    168             if (him->sin_addr.s_addr == 0) {
    169               untagSocket(env, fd);
    170               close(fd);
    171               return JNI_TRUE;
    172             }
    173          }
    174 
    175         }
    176       } while (tmout2 > 0);
    177       timeout -= 1000;
    178     } while (timeout >0);
    179     untagSocket(env, fd);
    180     close(fd);
    181     return JNI_FALSE;
    182 }
    183 
    184 /*
    185  * Class:     java_net_Inet4AddressImpl
    186  * Method:    isReachable0
    187  * Signature: ([bI[bI)Z
    188  */
    189 JNIEXPORT jboolean JNICALL
    190 Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
    191                                            jbyteArray addrArray,
    192                                            jint timeout,
    193                                            jbyteArray ifArray,
    194                                            jint ttl) {
    195     jint addr;
    196     jbyte caddr[4];
    197     jint fd;
    198     struct sockaddr_in him;
    199     struct sockaddr_in* netif = NULL;
    200     struct sockaddr_in inf;
    201     int len = 0;
    202     int connect_rv = -1;
    203     int sz;
    204 
    205     memset((char *) caddr, 0, sizeof(caddr));
    206     memset((char *) &him, 0, sizeof(him));
    207     memset((char *) &inf, 0, sizeof(inf));
    208     sz = (*env)->GetArrayLength(env, addrArray);
    209     if (sz != 4) {
    210       return JNI_FALSE;
    211     }
    212     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
    213     addr = ((caddr[0]<<24) & 0xff000000);
    214     addr |= ((caddr[1] <<16) & 0xff0000);
    215     addr |= ((caddr[2] <<8) & 0xff00);
    216     addr |= (caddr[3] & 0xff);
    217     addr = htonl(addr);
    218     him.sin_addr.s_addr = addr;
    219     him.sin_family = AF_INET;
    220     len = sizeof(him);
    221     /*
    222      * If a network interface was specified, let's create the address
    223      * for it.
    224      */
    225     if (!(IS_NULL(ifArray))) {
    226       memset((char *) caddr, 0, sizeof(caddr));
    227       (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
    228       addr = ((caddr[0]<<24) & 0xff000000);
    229       addr |= ((caddr[1] <<16) & 0xff0000);
    230       addr |= ((caddr[2] <<8) & 0xff00);
    231       addr |= (caddr[3] & 0xff);
    232       addr = htonl(addr);
    233       inf.sin_addr.s_addr = addr;
    234       inf.sin_family = AF_INET;
    235       inf.sin_port = 0;
    236       netif = &inf;
    237     }
    238 
    239     /*
    240      * Let's try to create a RAW socket to send ICMP packets
    241      * This usually requires "root" privileges, so it's likely to fail.
    242      */
    243     fd = JVM_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    244     if (fd != -1) {
    245       /*
    246        * It didn't fail, so we can use ICMP_ECHO requests.
    247        */
    248       tagSocket(env, fd);
    249       return ping4(env, fd, &him, timeout, netif, ttl);
    250     }
    251 
    252     /*
    253      * Can't create a raw socket, so let's try a TCP socket
    254      */
    255     fd = JVM_Socket(AF_INET, SOCK_STREAM, 0);
    256     if (fd == JVM_IO_ERR) {
    257         /* note: if you run out of fds, you may not be able to load
    258          * the exception class, and get a NoClassDefFoundError
    259          * instead.
    260          */
    261         NET_ThrowNew(env, errno, "Can't create socket");
    262         return JNI_FALSE;
    263     }
    264     tagSocket(env, fd);
    265 
    266     if (ttl > 0) {
    267       setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
    268     }
    269 
    270     /*
    271      * A network interface was specified, so let's bind to it.
    272      */
    273     if (netif != NULL) {
    274       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
    275         NET_ThrowNew(env, errno, "Can't bind socket");
    276         untagSocket(env, fd);
    277         close(fd);
    278         return JNI_FALSE;
    279       }
    280     }
    281 
    282     /*
    283      * Make the socket non blocking so we can use select/poll.
    284      */
    285     SET_NONBLOCKING(fd);
    286 
    287     /* no need to use NET_Connect as non-blocking */
    288     him.sin_port = htons(7);    /* Echo */
    289     connect_rv = JVM_Connect(fd, (struct sockaddr *)&him, len);
    290 
    291     /**
    292      * connection established or refused immediately, either way it means
    293      * we were able to reach the host!
    294      */
    295     if (connect_rv == 0 || errno == ECONNREFUSED) {
    296         untagSocket(env, fd);
    297         close(fd);
    298         return JNI_TRUE;
    299     } else {
    300         int optlen;
    301 
    302         switch (errno) {
    303         case ENETUNREACH: /* Network Unreachable */
    304         case EAFNOSUPPORT: /* Address Family not supported */
    305         case EADDRNOTAVAIL: /* address is not available on  the  remote machine */
    306 #ifdef __linux__
    307         case EINVAL:
    308         case EHOSTUNREACH:
    309           /*
    310            * On some Linuxes, when bound to the loopback interface, connect
    311            * will fail and errno will be set to EINVAL or EHOSTUNREACH.
    312            * When that happens, don't throw an exception, just return false.
    313            */
    314 #endif /* __linux__ */
    315           untagSocket(env, fd);
    316           close(fd);
    317           return JNI_FALSE;
    318         }
    319 
    320         if (errno != EINPROGRESS) {
    321           NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
    322                                        "connect failed");
    323           untagSocket(env, fd);
    324           close(fd);
    325           return JNI_FALSE;
    326         }
    327 
    328         timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
    329         if (timeout >= 0) {
    330           /* has connection been established? */
    331           optlen = sizeof(connect_rv);
    332           if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
    333                              &optlen) <0) {
    334             connect_rv = errno;
    335           }
    336           if (connect_rv == 0 || connect_rv == ECONNREFUSED) {
    337             untagSocket(env, fd);
    338             close(fd);
    339             return JNI_TRUE;
    340           }
    341         }
    342         untagSocket(env, fd);
    343         close(fd);
    344         return JNI_FALSE;
    345     }
    346 }
    347