1 /*
   2  * Copyright 2000-2009 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any 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 #include "jvm.h"
  40 #include "jni_util.h"
  41 #include "net_util.h"
  42 
  43 #include "java_net_Inet4AddressImpl.h"
  44 
  45 /* the initial size of our hostent buffers */
  46 #define HENT_BUF_SIZE 1024
  47 #define BIG_HENT_BUF_SIZE 10240  /* a jumbo-sized one */
  48 
  49 /************************************************************************
  50  * Inet4AddressImpl
  51  */
  52 
  53 /*
  54  * Class:     java_net_Inet4AddressImpl
  55  * Method:    getLocalHostName
  56  * Signature: ()Ljava/lang/String;
  57  */
  58 JNIEXPORT jstring JNICALL
  59 Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
  60     char hostname[MAXHOSTNAMELEN+1];
  61 
  62     hostname[0] = '\0';
  63     if (JVM_GetHostName(hostname, MAXHOSTNAMELEN)) {
  64         /* Something went wrong, maybe networking is not setup? */
  65         strcpy(hostname, "localhost");
  66     } else {
  67 #ifdef __linux__
  68         /* On Linux gethostname() says "host.domain.sun.com".  On
  69          * Solaris gethostname() says "host", so extra work is needed.
  70          */
  71 #else
  72         /* Solaris doesn't want to give us a fully qualified domain name.
  73          * We do a reverse lookup to try and get one.  This works
  74          * if DNS occurs before NIS in /etc/resolv.conf, but fails
  75          * if NIS comes first (it still gets only a partial name).
  76          * We use thread-safe system calls.
  77          */
  78 #endif /* __linux__ */
  79         struct hostent res, res2, *hp;
  80         char buf[HENT_BUF_SIZE];
  81         char buf2[HENT_BUF_SIZE];
  82         int h_error=0;
  83 
  84 #ifdef __GLIBC__
  85         gethostbyname_r(hostname, &res, buf, sizeof(buf), &hp, &h_error);
  86 #else
  87         hp = gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error);
  88 #endif
  89         if (hp) {
  90 #ifdef __GLIBC__
  91             gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET,
  92                             &res2, buf2, sizeof(buf2), &hp, &h_error);
  93 #else
  94             hp = gethostbyaddr_r(hp->h_addr, hp->h_length, AF_INET,
  95                                  &res2, buf2, sizeof(buf2), &h_error);
  96 #endif
  97             if (hp) {
  98                 /*
  99                  * If gethostbyaddr_r() found a fully qualified host name,
 100                  * returns that name. Otherwise, returns the hostname
 101                  * found by gethostname().
 102                  */
 103                 char *p = hp->h_name;
 104                 if ((strlen(hp->h_name) > strlen(hostname))
 105                     && (strncmp(hostname, hp->h_name, strlen(hostname)) == 0)
 106                     && (*(p + strlen(hostname)) == '.'))
 107                     strcpy(hostname, hp->h_name);
 108             }
 109         }
 110     }
 111     return (*env)->NewStringUTF(env, hostname);
 112 }
 113 








 114 /*
 115  * Find an internet address for a given hostname.  Note that this
 116  * code only works for addresses of type INET. The translation
 117  * of %d.%d.%d.%d to an address (int) occurs in java now, so the
 118  * String "host" shouldn't *ever* be a %d.%d.%d.%d string
 119  *
 120  * Class:     java_net_Inet4AddressImpl
 121  * Method:    lookupAllHostAddr
 122  * Signature: (Ljava/lang/String;)[[B
 123  */
 124 
 125 JNIEXPORT jobjectArray JNICALL
 126 Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
 127                                                 jstring host) {
 128     const char *hostname;
 129     jobjectArray ret = 0;
 130     struct hostent res, *hp = 0;
 131     char buf[HENT_BUF_SIZE];
 132 
 133     /* temporary buffer, on the off chance we need to expand */
 134     char *tmp = NULL;
 135     int h_error=0;
 136 
 137     init(env);










 138 
 139     if (IS_NULL(host)) {
 140         JNU_ThrowNullPointerException(env, "host is null");
 141         return 0;
 142     }
 143     hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
 144     CHECK_NULL_RETURN(hostname, NULL);
 145 
 146 #ifdef __solaris__
 147     /*
 148      * Workaround for Solaris bug 4160367 - if a hostname contains a
 149      * white space then 0.0.0.0 is returned
 150      */
 151     if (isspace((unsigned char)hostname[0])) {
 152         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
 153                         (char *)hostname);
 154         JNU_ReleaseStringPlatformChars(env, host, hostname);
 155         return NULL;
 156     }
 157 #endif
 158 
 159     /* Try once, with our static buffer. */
 160 #ifdef __GLIBC__
 161     gethostbyname_r(hostname, &res, buf, sizeof(buf), &hp, &h_error);
 162 #else
 163     hp = gethostbyname_r(hostname, &res, buf, sizeof(buf), &h_error);
 164 #endif
 165 
 166     /* With the re-entrant system calls, it's possible that the buffer
 167      * we pass to it is not large enough to hold an exceptionally
 168      * large DNS entry.  This is signaled by errno->ERANGE.  We try once
 169      * more, with a very big size.
 170      */
 171     if (hp == NULL && errno == ERANGE) {
 172         if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) {
 173 #ifdef __GLIBC__
 174             gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE,
 175                             &hp, &h_error);
 176 #else
 177             hp = gethostbyname_r(hostname, &res, tmp, BIG_HENT_BUF_SIZE,
 178                                  &h_error);
 179 #endif
 180         }
 181     }
 182     if (hp != NULL) {
 183         struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
 184         int i = 0;
 185 
 186         while (*addrp != (struct in_addr *) 0) {
 187             i++;
 188             addrp++;
 189         }
 190 
 191         ret = (*env)->NewObjectArray(env, i, ia_class, NULL);
 192         if (IS_NULL(ret)) {
 193             /* we may have memory to free at the end of this */
 194             goto cleanupAndReturn;
 195         }
 196         addrp = (struct in_addr **) hp->h_addr_list;
 197         i = 0;
 198         while (*addrp) {
 199           jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
 200           if (IS_NULL(iaObj)) {
 201             ret = NULL;
 202             goto cleanupAndReturn;
 203           }
 204           (*env)->SetIntField(env, iaObj, ia_addressID,
 205                               ntohl((*addrp)->s_addr));
 206           (*env)->SetObjectField(env, iaObj, ia_hostNameID, host);
 207           (*env)->SetObjectArrayElement(env, ret, i, iaObj);
 208           addrp++;
 209           i++;
 210         }
 211     } else {
 212         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException",
 213                         (char *)hostname);
 214         ret = NULL;
 215     }
 216 
 217 cleanupAndReturn:
 218     JNU_ReleaseStringPlatformChars(env, host, hostname);
 219     if (tmp != NULL) {
 220         free(tmp);
 221     }
 222     return ret;
 223 }
 224 
 225 /*
 226  * Class:     java_net_Inet4AddressImpl
 227  * Method:    getHostByAddr
 228  * Signature: (I)Ljava/lang/String;
 229  */
 230 JNIEXPORT jstring JNICALL
 231 Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
 232                                             jbyteArray addrArray) {
 233     jstring ret = NULL;
 234     jint addr;
 235     struct hostent hent, *hp = 0;
 236     char buf[HENT_BUF_SIZE];
 237     int h_error = 0;
 238     char *tmp = NULL;
 239 
 240     /*
 241      * We are careful here to use the reentrant version of
 242      * gethostbyname because at the Java level this routine is not
 243      * protected by any synchronization.
 244      *
 245      * Still keeping the reentrant platform dependent calls temporarily
 246      * We should probably conform to one interface later.
 247      *
 248      */
 249     jbyte caddr[4];
 250     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 251     addr = ((caddr[0]<<24) & 0xff000000);
 252     addr |= ((caddr[1] <<16) & 0xff0000);
 253     addr |= ((caddr[2] <<8) & 0xff00);
 254     addr |= (caddr[3] & 0xff);
 255     addr = htonl(addr);
 256 #ifdef __GLIBC__
 257     gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent,
 258                     buf, sizeof(buf), &hp, &h_error);
 259 #else
 260     hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET, &hent,
 261                          buf, sizeof(buf), &h_error);
 262 #endif
 263     /* With the re-entrant system calls, it's possible that the buffer
 264      * we pass to it is not large enough to hold an exceptionally
 265      * large DNS entry.  This is signaled by errno->ERANGE.  We try once
 266      * more, with a very big size.
 267      */
 268     if (hp == NULL && errno == ERANGE) {
 269         if ((tmp = (char*)malloc(BIG_HENT_BUF_SIZE))) {
 270 #ifdef __GLIBC__
 271             gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET,
 272                             &hent, tmp, BIG_HENT_BUF_SIZE, &hp, &h_error);
 273 #else
 274             hp = gethostbyaddr_r((char *)&addr, sizeof(addr), AF_INET,
 275                                  &hent, tmp, BIG_HENT_BUF_SIZE, &h_error);
 276 #endif
 277         } else {
 278             JNU_ThrowOutOfMemoryError(env, "getHostByAddr");
 279         }
 280     }
 281     if (hp == NULL) {
 282         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", NULL);
 283     } else {
 284         ret = (*env)->NewStringUTF(env, hp->h_name);
 285     }
 286     if (tmp) {
 287         free(tmp);
 288     }
 289     return ret;
 290 }
 291 
 292 #define SET_NONBLOCKING(fd) {           \
 293         int flags = fcntl(fd, F_GETFL); \
 294         flags |= O_NONBLOCK;            \
 295         fcntl(fd, F_SETFL, flags);      \
 296 }
 297 
 298 /**
 299  * ping implementation.
 300  * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
 301  * expires or a answer is received.
 302  * Returns true is an ECHO_REPLY is received, otherwise, false.
 303  */
 304 static jboolean
 305 ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
 306       struct sockaddr_in* netif, jint ttl) {
 307     jint size;
 308     jint n, hlen1, icmplen;
 309     socklen_t len;
 310     char sendbuf[1500];
 311     char recvbuf[1500];
 312     struct icmp *icmp;
 313     struct ip *ip;
 314     struct sockaddr_in sa_recv;
 315     jchar pid;
 316     jint tmout2, seq = 1;
 317     struct timeval tv;
 318     size_t plen;
 319 
 320     /* icmp_id is a 16 bit data type, therefore down cast the pid */
 321     pid = (jchar)getpid();
 322     size = 60*1024;
 323     setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
 324     /*
 325      * sets the ttl (max number of hops)
 326      */
 327     if (ttl > 0) {
 328       setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
 329     }
 330     /*
 331      * a specific interface was specified, so let's bind the socket
 332      * to that interface to ensure the requests are sent only through it.
 333      */
 334     if (netif != NULL) {
 335       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 336         NET_ThrowNew(env, errno, "Can't bind socket");
 337         close(fd);
 338         return JNI_FALSE;
 339       }
 340     }
 341     /*
 342      * Make the socket non blocking so we can use select
 343      */
 344     SET_NONBLOCKING(fd);
 345     do {
 346       /*
 347        * create the ICMP request
 348        */
 349       icmp = (struct icmp *) sendbuf;
 350       icmp->icmp_type = ICMP_ECHO;
 351       icmp->icmp_code = 0;
 352       icmp->icmp_id = htons(pid);
 353       icmp->icmp_seq = htons(seq);
 354       seq++;
 355       gettimeofday(&tv, NULL);
 356       memcpy(icmp->icmp_data, &tv, sizeof(tv));
 357       plen = ICMP_ADVLENMIN + sizeof(tv);
 358       icmp->icmp_cksum = 0;
 359       icmp->icmp_cksum = in_cksum((u_short *)icmp, plen);
 360       /*
 361        * send it
 362        */
 363       n = sendto(fd, sendbuf, plen, 0, (struct sockaddr *)him,
 364                  sizeof(struct sockaddr));
 365       if (n < 0 && errno != EINPROGRESS ) {
 366         NET_ThrowNew(env, errno, "Can't send ICMP packet");
 367         close(fd);
 368         return JNI_FALSE;
 369       }
 370 
 371       tmout2 = timeout > 1000 ? 1000 : timeout;
 372       do {
 373         tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
 374         if (tmout2 >= 0) {
 375           len = sizeof(sa_recv);
 376           n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&sa_recv, &len);
 377           ip = (struct ip*) recvbuf;
 378           hlen1 = (ip->ip_hl) << 2;
 379           icmp = (struct icmp *) (recvbuf + hlen1);
 380           icmplen = n - hlen1;
 381           /*
 382            * We did receive something, but is it what we were expecting?
 383            * I.E.: A ICMP_ECHOREPLY packet with the proper PID.
 384            */
 385           if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY &&
 386                (ntohs(icmp->icmp_id) == pid) &&
 387                (him->sin_addr.s_addr == sa_recv.sin_addr.s_addr)) {
 388             close(fd);
 389             return JNI_TRUE;
 390           }
 391         }
 392       } while (tmout2 > 0);
 393       timeout -= 1000;
 394     } while (timeout >0);
 395     close(fd);
 396     return JNI_FALSE;
 397 }
 398 
 399 /*
 400  * Class:     java_net_Inet4AddressImpl
 401  * Method:    isReachable0
 402  * Signature: ([bI[bI)Z
 403  */
 404 JNIEXPORT jboolean JNICALL
 405 Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
 406                                            jbyteArray addrArray,
 407                                            jint timeout,
 408                                            jbyteArray ifArray,
 409                                            jint ttl) {
 410     jint addr;
 411     jbyte caddr[4];
 412     jint fd;
 413     struct sockaddr_in him;
 414     struct sockaddr_in* netif = NULL;
 415     struct sockaddr_in inf;
 416     int len = 0;
 417     int connect_rv = -1;
 418     int sz;
 419 
 420     memset((char *) caddr, 0, sizeof(caddr));
 421     memset((char *) &him, 0, sizeof(him));
 422     sz = (*env)->GetArrayLength(env, addrArray);
 423     if (sz != 4) {
 424       return JNI_FALSE;
 425     }
 426     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 427     addr = ((caddr[0]<<24) & 0xff000000);
 428     addr |= ((caddr[1] <<16) & 0xff0000);
 429     addr |= ((caddr[2] <<8) & 0xff00);
 430     addr |= (caddr[3] & 0xff);
 431     addr = htonl(addr);
 432     him.sin_addr.s_addr = addr;
 433     him.sin_family = AF_INET;
 434     len = sizeof(him);
 435     /*
 436      * If a network interface was specified, let's create the address
 437      * for it.
 438      */
 439     if (!(IS_NULL(ifArray))) {
 440       memset((char *) caddr, 0, sizeof(caddr));
 441       (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
 442       addr = ((caddr[0]<<24) & 0xff000000);
 443       addr |= ((caddr[1] <<16) & 0xff0000);
 444       addr |= ((caddr[2] <<8) & 0xff00);
 445       addr |= (caddr[3] & 0xff);
 446       addr = htonl(addr);
 447       inf.sin_addr.s_addr = addr;
 448       inf.sin_family = AF_INET;
 449       inf.sin_port = 0;
 450       netif = &inf;
 451     }
 452 
 453     /*
 454      * Let's try to create a RAW socket to send ICMP packets
 455      * This usually requires "root" privileges, so it's likely to fail.
 456      */
 457     fd = JVM_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 458     if (fd != -1) {
 459       /*
 460        * It didn't fail, so we can use ICMP_ECHO requests.
 461        */
 462       return ping4(env, fd, &him, timeout, netif, ttl);
 463     }
 464 
 465     /*
 466      * Can't create a raw socket, so let's try a TCP socket
 467      */
 468     fd = JVM_Socket(AF_INET, SOCK_STREAM, 0);
 469     if (fd == JVM_IO_ERR) {
 470         /* note: if you run out of fds, you may not be able to load
 471          * the exception class, and get a NoClassDefFoundError
 472          * instead.
 473          */
 474         NET_ThrowNew(env, errno, "Can't create socket");
 475         return JNI_FALSE;
 476     }
 477     if (ttl > 0) {
 478       setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
 479     }
 480 
 481     /*
 482      * A network interface was specified, so let's bind to it.
 483      */
 484     if (netif != NULL) {
 485       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 486         NET_ThrowNew(env, errno, "Can't bind socket");
 487         close(fd);
 488         return JNI_FALSE;
 489       }
 490     }
 491 
 492     /*
 493      * Make the socket non blocking so we can use select/poll.
 494      */
 495     SET_NONBLOCKING(fd);
 496 
 497     /* no need to use NET_Connect as non-blocking */
 498     him.sin_port = htons(7);    /* Echo */
 499     connect_rv = JVM_Connect(fd, (struct sockaddr *)&him, len);
 500 
 501     /**
 502      * connection established or refused immediately, either way it means
 503      * we were able to reach the host!
 504      */
 505     if (connect_rv == 0 || errno == ECONNREFUSED) {
 506         close(fd);
 507         return JNI_TRUE;
 508     } else {
 509         int optlen;
 510 
 511         switch (errno) {
 512         case ENETUNREACH: /* Network Unreachable */
 513         case EAFNOSUPPORT: /* Address Family not supported */
 514         case EADDRNOTAVAIL: /* address is not available on  the  remote machine */
 515 #ifdef __linux__
 516         case EINVAL:
 517           /*
 518            * On some Linuxes, when bound to the loopback interface, connect
 519            * will fail and errno will be set to EINVAL. When that happens,
 520            * don't throw an exception, just return false.
 521            */
 522 #endif /* __linux__ */
 523           close(fd);
 524           return JNI_FALSE;
 525         }
 526 
 527         if (errno != EINPROGRESS) {
 528           NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
 529                                        "connect failed");
 530           close(fd);
 531           return JNI_FALSE;
 532         }
 533 
 534         timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
 535         if (timeout >= 0) {
 536           /* has connection been established? */
 537           optlen = sizeof(connect_rv);
 538           if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
 539                              &optlen) <0) {
 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 }
--- EOF ---