1 /*
   2  * Copyright (c) 2000, 2013, 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 #include <iphlpapi.h>
  35 #include <icmpapi.h>
  36 
  37 #include "java_net_InetAddress.h"
  38 #include "java_net_Inet4AddressImpl.h"
  39 #include "net_util.h"
  40 #include "icmp.h"
  41 
  42 
  43 /*
  44  * Returns true if hostname is in dotted IP address format. Note that this
  45  * function performs a syntax check only. For each octet it just checks that
  46  * the octet is at most 3 digits.
  47  */
  48 jboolean isDottedIPAddress(const char *hostname, unsigned int *addrp) {
  49     char *c = (char *)hostname;
  50     int octets = 0;
  51     unsigned int cur = 0;
  52     int digit_cnt = 0;
  53 
  54     while (*c) {
  55         if (*c == '.') {
  56             if (digit_cnt == 0) {
  57                 return JNI_FALSE;
  58             } else {
  59                 if (octets < 4) {
  60                     addrp[octets++] = cur;
  61                     cur = 0;
  62                     digit_cnt = 0;
  63                 } else {
  64                     return JNI_FALSE;
  65                 }
  66             }
  67             c++;
  68             continue;
  69         }
  70 
  71         if ((*c < '0') || (*c > '9')) {
  72             return JNI_FALSE;
  73         }
  74 
  75         digit_cnt++;
  76         if (digit_cnt > 3) {
  77             return JNI_FALSE;
  78         }
  79 
  80         /* don't check if current octet > 255 */
  81         cur = cur*10 + (*c - '0');
  82 
  83         /* Move onto next character and check for EOF */
  84         c++;
  85         if (*c == '\0') {
  86             if (octets < 4) {
  87                 addrp[octets++] = cur;
  88             } else {
  89                 return JNI_FALSE;
  90             }
  91         }
  92     }
  93 
  94     return (jboolean)(octets == 4);
  95 }
  96 
  97 /*
  98  * Inet4AddressImpl
  99  */
 100 
 101 /*
 102  * Class:     java_net_Inet4AddressImpl
 103  * Method:    getLocalHostName
 104  * Signature: ()Ljava/lang/String;
 105  */
 106 JNIEXPORT jstring JNICALL
 107 Java_java_net_Inet4AddressImpl_getLocalHostName (JNIEnv *env, jobject this) {
 108     char hostname[256];
 109 
 110     if (gethostname(hostname, sizeof hostname) == -1) {
 111         strcpy(hostname, "localhost");
 112     }
 113     return JNU_NewStringPlatform(env, hostname);
 114 }
 115 
 116 /*
 117  * Find an internet address for a given hostname.  Not this this
 118  * code only works for addresses of type INET. The translation
 119  * of %d.%d.%d.%d to an address (int) occurs in java now, so the
 120  * String "host" shouldn't be a %d.%d.%d.%d string. The only
 121  * exception should be when any of the %d are out of range and
 122  * we fallback to a lookup.
 123  *
 124  * Class:     java_net_Inet4AddressImpl
 125  * Method:    lookupAllHostAddr
 126  * Signature: (Ljava/lang/String;)[[B
 127  *
 128  * This is almost shared code
 129  */
 130 
 131 JNIEXPORT jobjectArray JNICALL
 132 Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
 133                                                 jstring host) {
 134     const char *hostname;
 135     struct hostent *hp;
 136     unsigned int addr[4];
 137 
 138     jobjectArray ret = NULL;
 139 
 140     initInetAddressIDs(env);
 141     JNU_CHECK_EXCEPTION_RETURN(env, NULL);
 142 
 143     if (IS_NULL(host)) {
 144         JNU_ThrowNullPointerException(env, "host argument");
 145         return NULL;
 146     }
 147     hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
 148     CHECK_NULL_RETURN(hostname, NULL);
 149 
 150     /*
 151      * The NT/2000 resolver tolerates a space in front of localhost. This
 152      * is not consistent with other implementations of gethostbyname.
 153      * In addition we must do a white space check on Solaris to avoid a
 154      * bug whereby 0.0.0.0 is returned if any host name has a white space.
 155      */
 156     if (isspace(hostname[0])) {
 157         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
 158         goto cleanupAndReturn;
 159     }
 160 
 161     /*
 162      * If the format is x.x.x.x then don't use gethostbyname as Windows
 163      * is unable to handle octets which are out of range.
 164      */
 165     if (isDottedIPAddress(hostname, &addr[0])) {
 166         unsigned int address;
 167         jobject iaObj;
 168 
 169         /*
 170          * Are any of the octets out of range?
 171          */
 172         if (addr[0] > 255 || addr[1] > 255 || addr[2] > 255 || addr[3] > 255) {
 173             JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
 174             goto cleanupAndReturn;
 175         }
 176 
 177         /*
 178          * Return an byte array with the populated address.
 179          */
 180         address = (addr[3]<<24) & 0xff000000;
 181         address |= (addr[2]<<16) & 0xff0000;
 182         address |= (addr[1]<<8) & 0xff00;
 183         address |= addr[0];
 184 
 185         ret = (*env)->NewObjectArray(env, 1, ia_class, NULL);
 186 
 187         if (IS_NULL(ret)) {
 188             goto cleanupAndReturn;
 189         }
 190 
 191         iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
 192         if (IS_NULL(iaObj)) {
 193           ret = NULL;
 194           goto cleanupAndReturn;
 195         }
 196         setInetAddress_addr(env, iaObj, ntohl(address));
 197         (*env)->SetObjectArrayElement(env, ret, 0, iaObj);
 198         JNU_ReleaseStringPlatformChars(env, host, hostname);
 199         return ret;
 200     }
 201 
 202     /*
 203      * Perform the lookup
 204      */
 205     if ((hp = gethostbyname((char*)hostname)) != NULL) {
 206         struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
 207         int len = sizeof(struct in_addr);
 208         int i = 0;
 209 
 210         while (*addrp != (struct in_addr *) 0) {
 211             i++;
 212             addrp++;
 213         }
 214 
 215         ret = (*env)->NewObjectArray(env, i, ia_class, NULL);
 216 
 217         if (IS_NULL(ret)) {
 218             goto cleanupAndReturn;
 219         }
 220 
 221         addrp = (struct in_addr **) hp->h_addr_list;
 222         i = 0;
 223         while (*addrp != (struct in_addr *) 0) {
 224           jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
 225           if (IS_NULL(iaObj)) {
 226             ret = NULL;
 227             goto cleanupAndReturn;
 228           }
 229           setInetAddress_addr(env, iaObj, ntohl((*addrp)->s_addr));
 230           setInetAddress_hostName(env, iaObj, host);
 231           (*env)->SetObjectArrayElement(env, ret, i, iaObj);
 232           addrp++;
 233           i++;
 234         }
 235     } else if (WSAGetLastError() == WSATRY_AGAIN) {
 236         NET_ThrowByNameWithLastError(env,
 237                                      JNU_JAVANETPKG "UnknownHostException",
 238                                      hostname);
 239     } else {
 240         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", hostname);
 241     }
 242 
 243 cleanupAndReturn:
 244     JNU_ReleaseStringPlatformChars(env, host, hostname);
 245     return ret;
 246 }
 247 
 248 /*
 249  * Class:     java_net_Inet4AddressImpl
 250  * Method:    getHostByAddr
 251  * Signature: (I)Ljava/lang/String;
 252  */
 253 JNIEXPORT jstring JNICALL
 254 Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
 255                                             jbyteArray addrArray) {
 256     struct hostent *hp;
 257     jbyte caddr[4];
 258     jint addr;
 259     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 260     addr = ((caddr[0]<<24) & 0xff000000);
 261     addr |= ((caddr[1] <<16) & 0xff0000);
 262     addr |= ((caddr[2] <<8) & 0xff00);
 263     addr |= (caddr[3] & 0xff);
 264     addr = htonl(addr);
 265 
 266     hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
 267     if (hp == NULL) {
 268         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
 269         return NULL;
 270     }
 271     if (hp->h_name == NULL) { /* Deal with bug in Windows XP */
 272         JNU_ThrowByName(env, JNU_JAVANETPKG "UnknownHostException", 0);
 273         return NULL;
 274     }
 275     return JNU_NewStringPlatform(env, hp->h_name);
 276 }
 277 
 278 static jboolean
 279 tcp_ping4(JNIEnv *env,
 280           jbyteArray addrArray,
 281           jint timeout,
 282           jbyteArray ifArray,
 283           jint ttl)
 284 {
 285     jint addr;
 286     jbyte caddr[4];
 287     jint fd;
 288     struct sockaddr_in him;
 289     struct sockaddr_in* netif = NULL;
 290     struct sockaddr_in inf;
 291     int len = 0;
 292     WSAEVENT hEvent;
 293     int connect_rv = -1;
 294     int sz;
 295 
 296     /**
 297      * Convert IP address from byte array to integer
 298      */
 299     sz = (*env)->GetArrayLength(env, addrArray);
 300     if (sz != 4) {
 301         return JNI_FALSE;
 302     }
 303     memset((char *) &him, 0, sizeof(him));
 304     memset((char *) caddr, 0, sizeof(caddr));
 305     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 306     addr = ((caddr[0]<<24) & 0xff000000);
 307     addr |= ((caddr[1] <<16) & 0xff0000);
 308     addr |= ((caddr[2] <<8) & 0xff00);
 309     addr |= (caddr[3] & 0xff);
 310     addr = htonl(addr);
 311     /**
 312      * Socket address
 313      */
 314     him.sin_addr.s_addr = addr;
 315     him.sin_family = AF_INET;
 316     len = sizeof(him);
 317 
 318     /**
 319      * If a network interface was specified, let's convert its address
 320      * as well.
 321      */
 322     if (!(IS_NULL(ifArray))) {
 323         memset((char *) caddr, 0, sizeof(caddr));
 324         (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
 325         addr = ((caddr[0]<<24) & 0xff000000);
 326         addr |= ((caddr[1] <<16) & 0xff0000);
 327         addr |= ((caddr[2] <<8) & 0xff00);
 328         addr |= (caddr[3] & 0xff);
 329         addr = htonl(addr);
 330         inf.sin_addr.s_addr = addr;
 331         inf.sin_family = AF_INET;
 332         inf.sin_port = 0;
 333         netif = &inf;
 334     }
 335 
 336     /*
 337      * Can't create a raw socket, so let's try a TCP socket
 338      */
 339     fd = NET_Socket(AF_INET, SOCK_STREAM, 0);
 340     if (fd == -1) {
 341         /* note: if you run out of fds, you may not be able to load
 342          * the exception class, and get a NoClassDefFoundError
 343          * instead.
 344          */
 345         NET_ThrowNew(env, WSAGetLastError(), "Can't create socket");
 346         return JNI_FALSE;
 347     }
 348     if (ttl > 0) {
 349         setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
 350     }
 351     /*
 352      * A network interface was specified, so let's bind to it.
 353      */
 354     if (netif != NULL) {
 355         if (bind(fd, (struct sockaddr*)netif, sizeof(struct sockaddr_in)) < 0) {
 356             NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
 357             closesocket(fd);
 358             return JNI_FALSE;
 359         }
 360     }
 361 
 362     /*
 363      * Make the socket non blocking so we can use select/poll.
 364      */
 365     hEvent = WSACreateEvent();
 366     WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
 367 
 368     /* no need to use NET_Connect as non-blocking */
 369     him.sin_port = htons(7);    /* Echo */
 370     connect_rv = connect(fd, (struct sockaddr *)&him, len);
 371 
 372     /**
 373      * connection established or refused immediately, either way it means
 374      * we were able to reach the host!
 375      */
 376     if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {
 377         WSACloseEvent(hEvent);
 378         closesocket(fd);
 379         return JNI_TRUE;
 380     } else {
 381         int optlen;
 382 
 383         switch (WSAGetLastError()) {
 384         case WSAEHOSTUNREACH:   /* Host Unreachable */
 385         case WSAENETUNREACH:    /* Network Unreachable */
 386         case WSAENETDOWN:       /* Network is down */
 387         case WSAEPFNOSUPPORT:   /* Protocol Family unsupported */
 388             WSACloseEvent(hEvent);
 389             closesocket(fd);
 390             return JNI_FALSE;
 391         }
 392 
 393         if (WSAGetLastError() != WSAEWOULDBLOCK) {
 394             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
 395                                          "connect failed");
 396             WSACloseEvent(hEvent);
 397             closesocket(fd);
 398             return JNI_FALSE;
 399         }
 400 
 401         timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
 402 
 403         /* has connection been established */
 404 
 405         if (timeout >= 0) {
 406             optlen = sizeof(connect_rv);
 407             if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
 408                            &optlen) <0) {
 409                 connect_rv = WSAGetLastError();
 410             }
 411 
 412             if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {
 413                 WSACloseEvent(hEvent);
 414                 closesocket(fd);
 415                 return JNI_TRUE;
 416             }
 417         }
 418     }
 419     WSACloseEvent(hEvent);
 420     closesocket(fd);
 421     return JNI_FALSE;
 422 }
 423 
 424 /**
 425  * ping implementation.
 426  * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
 427  * expires or a answer is received.
 428  * Returns true is an ECHO_REPLY is received, otherwise, false.
 429  */
 430 static jboolean
 431 ping4(JNIEnv *env,
 432       unsigned long src_addr,
 433       unsigned long dest_addr,
 434       jint timeout,
 435       HANDLE hIcmpFile)
 436 {
 437     // See https://msdn.microsoft.com/en-us/library/aa366050%28VS.85%29.aspx
 438 
 439     DWORD dwRetVal = 0;
 440     char SendData[32] = {0};
 441     LPVOID ReplyBuffer = NULL;
 442     DWORD ReplySize = 0;
 443     jboolean ret = JNI_FALSE;
 444 
 445     ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
 446     ReplyBuffer = (VOID*) malloc(ReplySize);
 447     if (ReplyBuffer == NULL) {
 448         IcmpCloseHandle(hIcmpFile);
 449         NET_ThrowNew(env, WSAGetLastError(), "Unable to allocate memory");
 450         return JNI_FALSE;
 451     }
 452 
 453     if (src_addr == 0) {
 454         dwRetVal = IcmpSendEcho(hIcmpFile,  // HANDLE IcmpHandle,
 455                                 dest_addr,  // IPAddr DestinationAddress,
 456                                 SendData,   // LPVOID RequestData,
 457                                 sizeof(SendData),   // WORD RequestSize,
 458                                 NULL,       // PIP_OPTION_INFORMATION RequestOptions,
 459                                 ReplyBuffer,// LPVOID ReplyBuffer,
 460                                 ReplySize,  // DWORD ReplySize,
 461                                 // Note: IcmpSendEcho and its derivatives
 462                                 // seem to have an undocumented minimum
 463                                 // timeout of 1000ms below which the
 464                                 // api behaves inconsistently.
 465                                 (timeout < 1000) ? 1000 : timeout);   // DWORD Timeout
 466     } else {
 467         dwRetVal = IcmpSendEcho2Ex(hIcmpFile,  // HANDLE IcmpHandle,
 468                                    NULL,       // HANDLE Event
 469                                    NULL,       // PIO_APC_ROUTINE ApcRoutine
 470                                    NULL,       // ApcContext
 471                                    src_addr,   // IPAddr SourceAddress,
 472                                    dest_addr,  // IPAddr DestinationAddress,
 473                                    SendData,   // LPVOID RequestData,
 474                                    sizeof(SendData),   // WORD RequestSize,
 475                                    NULL,       // PIP_OPTION_INFORMATION RequestOptions,
 476                                    ReplyBuffer,// LPVOID ReplyBuffer,
 477                                    ReplySize,  // DWORD ReplySize,
 478                                    (timeout < 1000) ? 1000 : timeout);   // DWORD Timeout
 479     }
 480 
 481     if (dwRetVal != 0) {
 482         PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
 483         if ((int)pEchoReply->RoundTripTime <= timeout)
 484             ret = JNI_TRUE;
 485     }
 486 
 487     free(ReplyBuffer);
 488     IcmpCloseHandle(hIcmpFile);
 489 
 490     return ret;
 491 }
 492 
 493 /*
 494  * Class:     java_net_Inet4AddressImpl
 495  * Method:    isReachable0
 496  * Signature: ([bI[bI)Z
 497  */
 498 JNIEXPORT jboolean JNICALL
 499 Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
 500                                            jbyteArray addrArray,
 501                                            jint timeout,
 502                                            jbyteArray ifArray,
 503                                            jint ttl) {
 504     jint src_addr = 0;
 505     jint dest_addr = 0;
 506     jbyte caddr[4];
 507     int sz;
 508     HANDLE hIcmpFile;
 509 
 510     /**
 511      * Convert IP address from byte array to integer
 512      */
 513     sz = (*env)->GetArrayLength(env, addrArray);
 514     if (sz != 4) {
 515       return JNI_FALSE;
 516     }
 517     memset((char *) caddr, 0, sizeof(caddr));
 518     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 519     dest_addr = ((caddr[0]<<24) & 0xff000000);
 520     dest_addr |= ((caddr[1] <<16) & 0xff0000);
 521     dest_addr |= ((caddr[2] <<8) & 0xff00);
 522     dest_addr |= (caddr[3] & 0xff);
 523     dest_addr = htonl(dest_addr);
 524 
 525     /**
 526      * If a network interface was specified, let's convert its address
 527      * as well.
 528      */
 529     if (!(IS_NULL(ifArray))) {
 530         memset((char *) caddr, 0, sizeof(caddr));
 531         (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
 532         src_addr = ((caddr[0]<<24) & 0xff000000);
 533         src_addr |= ((caddr[1] <<16) & 0xff0000);
 534         src_addr |= ((caddr[2] <<8) & 0xff00);
 535         src_addr |= (caddr[3] & 0xff);
 536         src_addr = htonl(src_addr);
 537     }
 538 
 539     hIcmpFile = IcmpCreateFile();
 540     if (hIcmpFile == INVALID_HANDLE_VALUE) {
 541         int err = WSAGetLastError();
 542         if (err == ERROR_ACCESS_DENIED) {
 543             // fall back to TCP echo if access is denied to ICMP
 544             return tcp_ping4(env, addrArray, timeout, ifArray, ttl);
 545         } else {
 546             NET_ThrowNew(env, err, "Unable to create ICMP file handle");
 547             return JNI_FALSE;
 548         }
 549     } else {
 550         return ping4(env, src_addr, dest_addr, timeout, hIcmpFile);
 551     }
 552 }
 553