1 /*
   2  * Copyright (c) 2000, 2008, 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 <windows.h>
  27 #include <winsock2.h>
  28 #include <ctype.h>
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <malloc.h>
  32 #include <sys/types.h>
  33 #include <process.h>
  34 
  35 #include "java_net_InetAddress.h"
  36 #include "java_net_Inet4AddressImpl.h"
  37 #include "net_util.h"
  38 #include "icmp.h"
  39 
  40 
  41 /*
  42  * Returns true if hostname is in dotted IP address format. Note that this
  43  * function performs a syntax check only. For each octet it just checks that
  44  * the octet is at most 3 digits.
  45  */
  46 jboolean isDottedIPAddress(const char *hostname, unsigned int *addrp) {
  47     char *c = (char *)hostname;
  48     int octets = 0;
  49     unsigned int cur = 0;
  50     int digit_cnt = 0;
  51 
  52     while (*c) {
  53         if (*c == '.') {
  54             if (digit_cnt == 0) {
  55                 return JNI_FALSE;
  56             } else {
  57                 if (octets < 4) {
  58                     addrp[octets++] = cur;
  59                     cur = 0;
  60                     digit_cnt = 0;
  61                 } else {
  62                     return JNI_FALSE;
  63                 }
  64             }
  65             c++;
  66             continue;
  67         }
  68 
  69         if ((*c < '0') || (*c > '9')) {
  70             return JNI_FALSE;
  71         }
  72 
  73         digit_cnt++;
  74         if (digit_cnt > 3) {
  75             return JNI_FALSE;
  76         }
  77 
  78         /* don't check if current octet > 255 */
  79         cur = cur*10 + (*c - '0');
  80 
  81         /* Move onto next character and check for EOF */
  82         c++;
  83         if (*c == '\0') {
  84             if (octets < 4) {
  85                 addrp[octets++] = cur;
  86             } else {
  87                 return JNI_FALSE;
  88             }
  89         }
  90     }
  91 
  92     return (jboolean)(octets == 4);
  93 }
  94 
  95 /*
  96  * Inet4AddressImpl
  97  */
  98 
  99 /*
 100  * Class:     java_net_Inet4AddressImpl
 101  * Method:    getLocalHostName
 102  * Signature: ()Ljava/lang/String;
 103  */
 104 JNIEXPORT jstring JNICALL
 105 Java_java_net_Inet4AddressImpl_getLocalHostName (JNIEnv *env, jobject this) {
 106     char hostname[256];
 107 
 108     if (gethostname(hostname, sizeof hostname) == -1) {
 109         strcpy(hostname, "localhost");
 110     }
 111     return JNU_NewStringPlatform(env, hostname);
 112 }
 113 
 114 static jclass ni_iacls;
 115 static jclass ni_ia4cls;
 116 static jmethodID ni_ia4ctrID;
 117 static jfieldID ni_iaaddressID;
 118 static jfieldID ni_iahostID;
 119 static jfieldID ni_iafamilyID;
 120 static int initialized = 0;
 121 
 122 /*
 123  * Find an internet address for a given hostname.  Not this this
 124  * code only works for addresses of type INET. The translation
 125  * of %d.%d.%d.%d to an address (int) occurs in java now, so the
 126  * String "host" shouldn't be a %d.%d.%d.%d string. The only
 127  * exception should be when any of the %d are out of range and
 128  * we fallback to a lookup.
 129  *
 130  * Class:     java_net_Inet4AddressImpl
 131  * Method:    lookupAllHostAddr
 132  * Signature: (Ljava/lang/String;)[[B
 133  *
 134  * This is almost shared code
 135  */
 136 
 137 JNIEXPORT jobjectArray JNICALL
 138 Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
 139                                                 jstring host) {
 140     const char *hostname;
 141     struct hostent *hp;
 142     unsigned int addr[4];
 143 
 144     jobjectArray ret = NULL;
 145 
 146     if (!initialized) {
 147       ni_iacls = (*env)->FindClass(env, "java/net/InetAddress");
 148       ni_iacls = (*env)->NewGlobalRef(env, ni_iacls);
 149       ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address");
 150       ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls);
 151       ni_ia4ctrID = (*env)->GetMethodID(env, ni_ia4cls, "<init>", "()V");
 152       ni_iaaddressID = (*env)->GetFieldID(env, ni_iacls, "address", "I");
 153       ni_iafamilyID = (*env)->GetFieldID(env, ni_iacls, "family", "I");
 154       ni_iahostID = (*env)->GetFieldID(env, ni_iacls, "hostName", "Ljava/lang/String;");
 155       initialized = 1;
 156     }
 157 
 158     if (IS_NULL(host)) {
 159         JNU_ThrowNullPointerException(env, "host argument");
 160         return NULL;
 161     }
 162     hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
 163     CHECK_NULL_RETURN(hostname, NULL);
 164 
 165     /*
 166      * The NT/2000 resolver tolerates a space in front of localhost. This
 167      * is not consistent with other implementations of gethostbyname.
 168      * In addition we must do a white space check on Solaris to avoid a
 169      * bug whereby 0.0.0.0 is returned if any host name has a white space.
 170      */
 171     if (isspace(hostname[0])) {
 172         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
 173         goto cleanupAndReturn;
 174     }
 175 
 176     /*
 177      * If the format is x.x.x.x then don't use gethostbyname as Windows
 178      * is unable to handle octets which are out of range.
 179      */
 180     if (isDottedIPAddress(hostname, &addr[0])) {
 181         unsigned int address;
 182         jobject iaObj;
 183 
 184         /*
 185          * Are any of the octets out of range?
 186          */
 187         if (addr[0] > 255 || addr[1] > 255 || addr[2] > 255 || addr[3] > 255) {
 188             JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
 189             goto cleanupAndReturn;
 190         }
 191 
 192         /*
 193          * Return an byte array with the populated address.
 194          */
 195         address = (addr[3]<<24) & 0xff000000;
 196         address |= (addr[2]<<16) & 0xff0000;
 197         address |= (addr[1]<<8) & 0xff00;
 198         address |= addr[0];
 199 
 200         ret = (*env)->NewObjectArray(env, 1, ni_iacls, NULL);
 201 
 202         if (IS_NULL(ret)) {
 203             goto cleanupAndReturn;
 204         }
 205 
 206         iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
 207         if (IS_NULL(iaObj)) {
 208           ret = NULL;
 209           goto cleanupAndReturn;
 210         }
 211         (*env)->SetIntField(env, iaObj, ni_iaaddressID,
 212                             ntohl(address));
 213         (*env)->SetObjectArrayElement(env, ret, 0, iaObj);
 214         JNU_ReleaseStringPlatformChars(env, host, hostname);
 215         return ret;
 216     }
 217 
 218     /*
 219      * Perform the lookup
 220      */
 221     if ((hp = gethostbyname((char*)hostname)) != NULL) {
 222         struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
 223         int len = sizeof(struct in_addr);
 224         int i = 0;
 225 
 226         while (*addrp != (struct in_addr *) 0) {
 227             i++;
 228             addrp++;
 229         }
 230 
 231         ret = (*env)->NewObjectArray(env, i, ni_iacls, NULL);
 232 
 233         if (IS_NULL(ret)) {
 234             goto cleanupAndReturn;
 235         }
 236 
 237         addrp = (struct in_addr **) hp->h_addr_list;
 238         i = 0;
 239         while (*addrp != (struct in_addr *) 0) {
 240           jobject iaObj = (*env)->NewObject(env, ni_ia4cls, ni_ia4ctrID);
 241           if (IS_NULL(iaObj)) {
 242             ret = NULL;
 243             goto cleanupAndReturn;
 244           }
 245           (*env)->SetIntField(env, iaObj, ni_iaaddressID,
 246                               ntohl((*addrp)->s_addr));
 247           (*env)->SetObjectField(env, iaObj, ni_iahostID, host);
 248           (*env)->SetObjectArrayElement(env, ret, i, iaObj);
 249           addrp++;
 250           i++;
 251         }
 252     } else {
 253         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
 254     }
 255 
 256 cleanupAndReturn:
 257     JNU_ReleaseStringPlatformChars(env, host, hostname);
 258     return ret;
 259 }
 260 
 261 /*
 262  * Class:     java_net_Inet4AddressImpl
 263  * Method:    getHostByAddr
 264  * Signature: (I)Ljava/lang/String;
 265  */
 266 JNIEXPORT jstring JNICALL
 267 Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
 268                                             jbyteArray addrArray) {
 269     struct hostent *hp;
 270     jbyte caddr[4];
 271     jint addr;
 272     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 273     addr = ((caddr[0]<<24) & 0xff000000);
 274     addr |= ((caddr[1] <<16) & 0xff0000);
 275     addr |= ((caddr[2] <<8) & 0xff00);
 276     addr |= (caddr[3] & 0xff);
 277     addr = htonl(addr);
 278 
 279     hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
 280     if (hp == NULL) {
 281         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
 282         return NULL;
 283     }
 284     if (hp->h_name == NULL) { /* Deal with bug in Windows XP */
 285         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
 286         return NULL;
 287     }
 288     return JNU_NewStringPlatform(env, hp->h_name);
 289 }
 290 
 291 
 292 /**
 293  * ping implementation.
 294  * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
 295  * expires or a answer is received.
 296  * Returns true is an ECHO_REPLY is received, otherwise, false.
 297  */
 298 static jboolean
 299 ping4(JNIEnv *env, jint fd, struct sockaddr_in* him, jint timeout,
 300       struct sockaddr_in* netif, jint ttl) {
 301     jint size;
 302     jint n, len, hlen1, icmplen;
 303     char sendbuf[1500];
 304     char recvbuf[1500];
 305     struct icmp *icmp;
 306     struct ip *ip;
 307     WSAEVENT hEvent;
 308     struct sockaddr sa_recv;
 309     jint tmout2;
 310     u_short pid, seq;
 311     int read_rv = 0;
 312 
 313     /* Initialize the sequence number to a suitable random number and
 314        shift right one place to allow sufficient room for increamenting. */
 315     seq = ((unsigned short)rand()) >> 1;
 316 
 317     /* icmp_id is a 16 bit data type, therefore down cast the pid */
 318     pid = (u_short) _getpid();
 319     size = 60*1024;
 320     setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char *) &size, sizeof(size));
 321     /**
 322      * A TTL was specified, let's set the socket option.
 323      */
 324     if (ttl > 0) {
 325       setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *) &ttl, sizeof(ttl));
 326     }
 327 
 328     /**
 329      * A network interface was specified, let's bind to it.
 330      */
 331     if (netif != NULL) {
 332       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 333         NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
 334         closesocket(fd);
 335         return JNI_FALSE;
 336       }
 337     }
 338 
 339     /**
 340      * Let's make the socket non blocking
 341      */
 342     hEvent = WSACreateEvent();
 343     WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
 344 
 345     /**
 346      * send 1 ICMP REQUEST every second until either we get a valid reply
 347      * or the timeout expired.
 348      */
 349     do {
 350       /**
 351        * construct the ICMP header
 352        */
 353       memset(sendbuf, 0, 1500);
 354       icmp = (struct icmp *) sendbuf;
 355       icmp->icmp_type = ICMP_ECHO;
 356       icmp->icmp_code = 0;
 357       icmp->icmp_id = htons(pid);
 358       icmp->icmp_seq = htons(seq);
 359       /**
 360        * checksum has to be set to zero before we can calculate the
 361        * real checksum!
 362        */
 363       icmp->icmp_cksum = 0;
 364       icmp->icmp_cksum = in_cksum((u_short *)icmp, 64);
 365       /**
 366        * Ping!
 367        */
 368       n = sendto(fd, sendbuf, 64, 0, (struct sockaddr *)him,
 369                  sizeof(struct sockaddr));
 370       if (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
 371         NET_ThrowNew(env, WSAGetLastError(), "Can't send ICMP packet");
 372         closesocket(fd);
 373         WSACloseEvent(hEvent);
 374         return JNI_FALSE;
 375       }
 376 
 377       /*
 378        * wait for 1 second at most
 379        */
 380       tmout2 = timeout > 1000 ? 1000 : timeout;
 381       do {
 382         tmout2 = NET_Wait(env, fd, NET_WAIT_READ, tmout2);
 383         if (tmout2 >= 0) {
 384           len = sizeof(sa_recv);
 385           n = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, &sa_recv, &len);
 386           ip = (struct ip*) recvbuf;
 387           hlen1 = (ip->ip_hl) << 2;
 388           icmp = (struct icmp *) (recvbuf + hlen1);
 389           icmplen = n - hlen1;
 390           /**
 391            * Is that a proper ICMP reply?
 392            */
 393           if (icmplen >= 8 && icmp->icmp_type == ICMP_ECHOREPLY &&
 394               (ntohs(icmp->icmp_seq) == seq) && (ntohs(icmp->icmp_id) == pid)) {
 395             closesocket(fd);
 396             WSACloseEvent(hEvent);
 397             return JNI_TRUE;
 398           }
 399         }
 400       } while (tmout2 > 0);
 401       timeout -= 1000;
 402       seq++;
 403     } while (timeout > 0);
 404     closesocket(fd);
 405     WSACloseEvent(hEvent);
 406     return JNI_FALSE;
 407 }
 408 
 409 /*
 410  * Class:     java_net_Inet4AddressImpl
 411  * Method:    isReachable0
 412  * Signature: ([bI[bI)Z
 413  */
 414 JNIEXPORT jboolean JNICALL
 415 Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
 416                                            jbyteArray addrArray,
 417                                            jint timeout,
 418                                            jbyteArray ifArray,
 419                                            jint ttl) {
 420     jint addr;
 421     jbyte caddr[4];
 422     jint fd;
 423     struct sockaddr_in him;
 424     struct sockaddr_in* netif = NULL;
 425     struct sockaddr_in inf;
 426     int len = 0;
 427     WSAEVENT hEvent;
 428     int connect_rv = -1;
 429     int sz;
 430 
 431     /**
 432      * Convert IP address from byte array to integer
 433      */
 434     sz = (*env)->GetArrayLength(env, addrArray);
 435     if (sz != 4) {
 436       return JNI_FALSE;
 437     }
 438     memset((char *) &him, 0, sizeof(him));
 439     memset((char *) caddr, 0, sizeof(caddr));
 440     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 441     addr = ((caddr[0]<<24) & 0xff000000);
 442     addr |= ((caddr[1] <<16) & 0xff0000);
 443     addr |= ((caddr[2] <<8) & 0xff00);
 444     addr |= (caddr[3] & 0xff);
 445     addr = htonl(addr);
 446     /**
 447      * Socket address
 448      */
 449     him.sin_addr.s_addr = addr;
 450     him.sin_family = AF_INET;
 451     len = sizeof(him);
 452 
 453     /**
 454      * If a network interface was specified, let's convert its address
 455      * as well.
 456      */
 457     if (!(IS_NULL(ifArray))) {
 458       memset((char *) caddr, 0, sizeof(caddr));
 459       (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
 460       addr = ((caddr[0]<<24) & 0xff000000);
 461       addr |= ((caddr[1] <<16) & 0xff0000);
 462       addr |= ((caddr[2] <<8) & 0xff00);
 463       addr |= (caddr[3] & 0xff);
 464       addr = htonl(addr);
 465       inf.sin_addr.s_addr = addr;
 466       inf.sin_family = AF_INET;
 467       inf.sin_port = 0;
 468       netif = &inf;
 469     }
 470 
 471 #if 0
 472     /*
 473      * Windows implementation of ICMP & RAW sockets is too unreliable for now.
 474      * Therefore it's best not to try it at all and rely only on TCP
 475      * We may revisit and enable this code in the future.
 476      */
 477 
 478     /*
 479      * Let's try to create a RAW socket to send ICMP packets
 480      * This usually requires "root" privileges, so it's likely to fail.
 481      */
 482     fd = NET_Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 483     if (fd != -1) {
 484       /*
 485        * It didn't fail, so we can use ICMP_ECHO requests.
 486        */
 487         return ping4(env, fd, &him, timeout, netif, ttl);
 488     }
 489 #endif
 490 
 491     /*
 492      * Can't create a raw socket, so let's try a TCP socket
 493      */
 494     fd = NET_Socket(AF_INET, SOCK_STREAM, 0);
 495     if (fd == JVM_IO_ERR) {
 496         /* note: if you run out of fds, you may not be able to load
 497          * the exception class, and get a NoClassDefFoundError
 498          * instead.
 499          */
 500         NET_ThrowNew(env, WSAGetLastError(), "Can't create socket");
 501         return JNI_FALSE;
 502     }
 503     if (ttl > 0) {
 504       setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
 505     }
 506     /*
 507      * A network interface was specified, so let's bind to it.
 508      */
 509     if (netif != NULL) {
 510       if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 511         NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
 512         closesocket(fd);
 513         return JNI_FALSE;
 514       }
 515     }
 516 
 517     /*
 518      * Make the socket non blocking so we can use select/poll.
 519      */
 520     hEvent = WSACreateEvent();
 521     WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
 522 
 523     /* no need to use NET_Connect as non-blocking */
 524     him.sin_port = htons(7);    /* Echo */
 525     connect_rv = connect(fd, (struct sockaddr *)&him, len);
 526 
 527     /**
 528      * connection established or refused immediately, either way it means
 529      * we were able to reach the host!
 530      */
 531     if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {
 532         WSACloseEvent(hEvent);
 533         closesocket(fd);
 534         return JNI_TRUE;
 535     } else {
 536         int optlen;
 537 
 538         switch (WSAGetLastError()) {
 539         case WSAEHOSTUNREACH:   /* Host Unreachable */
 540         case WSAENETUNREACH:    /* Network Unreachable */
 541         case WSAENETDOWN:       /* Network is down */
 542         case WSAEPFNOSUPPORT:   /* Protocol Family unsupported */
 543           WSACloseEvent(hEvent);
 544           closesocket(fd);
 545           return JNI_FALSE;
 546         }
 547 
 548         if (WSAGetLastError() != WSAEWOULDBLOCK) {
 549             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
 550                                          "connect failed");
 551             WSACloseEvent(hEvent);
 552             closesocket(fd);
 553             return JNI_FALSE;
 554         }
 555 
 556         timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
 557 
 558         /* has connection been established */
 559 
 560         if (timeout >= 0) {
 561           optlen = sizeof(connect_rv);
 562           if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
 563                          &optlen) <0) {
 564             connect_rv = WSAGetLastError();
 565           }
 566 
 567           if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {
 568             WSACloseEvent(hEvent);
 569             closesocket(fd);
 570             return JNI_TRUE;
 571           }
 572         }
 573     }
 574     WSACloseEvent(hEvent);
 575     closesocket(fd);
 576     return JNI_FALSE;
 577 }