1 /*
   2  * Copyright (c) 2000, 2019, 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 #include <malloc.h>
  26 
  27 #include "net_util.h"
  28 
  29 #include "java_net_InetAddress.h"
  30 #include "java_net_Inet4AddressImpl.h"
  31 
  32 /*
  33  * Inet4AddressImpl
  34  */
  35 
  36 /*
  37  * Class:     java_net_Inet4AddressImpl
  38  * Method:    getLocalHostName
  39  * Signature: ()Ljava/lang/String;
  40  */
  41 JNIEXPORT jstring JNICALL
  42 Java_java_net_Inet4AddressImpl_getLocalHostName(JNIEnv *env, jobject this) {
  43     char hostname[256];
  44 
  45     if (gethostname(hostname, sizeof(hostname)) == -1) {
  46         strcpy(hostname, "localhost");
  47     }
  48     return JNU_NewStringPlatform(env, hostname);
  49 }
  50 
  51 /*
  52  * Find an internet address for a given hostname. Note that this
  53  * code only works for addresses of type INET. The translation
  54  * of %d.%d.%d.%d to an address (int) occurs in java now, so the
  55  * String "host" shouldn't be a %d.%d.%d.%d string. The only
  56  * exception should be when any of the %d are out of range and
  57  * we fallback to a lookup.
  58  *
  59  * Class:     java_net_Inet4AddressImpl
  60  * Method:    lookupAllHostAddr
  61  * Signature: (Ljava/lang/String;)[[B
  62  */
  63 JNIEXPORT jobjectArray JNICALL
  64 Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
  65                                                  jstring host) {
  66     jobjectArray ret = NULL;
  67     const char *hostname;
  68     int error = 0;
  69     struct addrinfo hints, *res = NULL, *resNew = NULL, *last = NULL,
  70         *iterator;
  71 
  72     initInetAddressIDs(env);
  73     JNU_CHECK_EXCEPTION_RETURN(env, NULL);
  74 
  75     if (IS_NULL(host)) {
  76         JNU_ThrowNullPointerException(env, "host argument is null");
  77         return NULL;
  78     }
  79     hostname = JNU_GetStringPlatformChars(env, host, JNI_FALSE);
  80     CHECK_NULL_RETURN(hostname, NULL);
  81 
  82     // try once, with our static buffer
  83     memset(&hints, 0, sizeof(hints));
  84     hints.ai_flags = AI_CANONNAME;
  85     hints.ai_family = AF_INET;
  86 
  87     error = getaddrinfo(hostname, NULL, &hints, &res);
  88 
  89     if (error) {
  90         // report error
  91         NET_ThrowByNameWithLastError(env, "java/net/UnknownHostException",
  92                                      hostname);
  93         goto cleanupAndReturn;
  94     } else {
  95         int i = 0;
  96         iterator = res;
  97         while (iterator != NULL) {
  98             // skip duplicates
  99             int skip = 0;
 100             struct addrinfo *iteratorNew = resNew;
 101             while (iteratorNew != NULL) {
 102                 struct sockaddr_in *addr1, *addr2;
 103                 addr1 = (struct sockaddr_in *)iterator->ai_addr;
 104                 addr2 = (struct sockaddr_in *)iteratorNew->ai_addr;
 105                 if (addr1->sin_addr.s_addr == addr2->sin_addr.s_addr) {
 106                     skip = 1;
 107                     break;
 108                 }
 109                 iteratorNew = iteratorNew->ai_next;
 110             }
 111 
 112             if (!skip) {
 113                 struct addrinfo *next
 114                     = (struct addrinfo *)malloc(sizeof(struct addrinfo));
 115                 if (!next) {
 116                     JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
 117                     ret = NULL;
 118                     goto cleanupAndReturn;
 119                 }
 120                 memcpy(next, iterator, sizeof(struct addrinfo));
 121                 next->ai_next = NULL;
 122                 if (resNew == NULL) {
 123                     resNew = next;
 124                 } else {
 125                     last->ai_next = next;
 126                 }
 127                 last = next;
 128                 i++;
 129             }
 130             iterator = iterator->ai_next;
 131         }
 132 
 133         // allocate array - at this point i contains the number of addresses
 134         ret = (*env)->NewObjectArray(env, i, ia_class, NULL);
 135         if (IS_NULL(ret)) {
 136             goto cleanupAndReturn;
 137         }
 138 
 139         i = 0;
 140         iterator = resNew;
 141         while (iterator != NULL) {
 142             jobject iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
 143             if (IS_NULL(iaObj)) {
 144                 ret = NULL;
 145                 goto cleanupAndReturn;
 146             }
 147             setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in *)
 148                                 (iterator->ai_addr))->sin_addr.s_addr));
 149             if ((*env)->ExceptionCheck(env))
 150                 goto cleanupAndReturn;
 151             setInetAddress_hostName(env, iaObj, host);
 152             if ((*env)->ExceptionCheck(env))
 153                 goto cleanupAndReturn;
 154             (*env)->SetObjectArrayElement(env, ret, i++, iaObj);
 155             iterator = iterator->ai_next;
 156         }
 157     }
 158 cleanupAndReturn:
 159     JNU_ReleaseStringPlatformChars(env, host, hostname);
 160     while (resNew != NULL) {
 161         last = resNew;
 162         resNew = resNew->ai_next;
 163         free(last);
 164     }
 165     if (res != NULL) {
 166         freeaddrinfo(res);
 167     }
 168     return ret;
 169 }
 170 
 171 /*
 172  * Class:     java_net_Inet4AddressImpl
 173  * Method:    getHostByAddr
 174  * Signature: (I)Ljava/lang/String;
 175  *
 176  * Theoretically the UnknownHostException could be enriched with gai error
 177  * information. But as it is silently ignored anyway, there's no need for this.
 178  * It's only important that either a valid hostname is returned or an
 179  * UnknownHostException is thrown.
 180  */
 181 JNIEXPORT jstring JNICALL
 182 Java_java_net_Inet4AddressImpl_getHostByAddr(JNIEnv *env, jobject this,
 183                                              jbyteArray addrArray) {
 184     jstring ret = NULL;
 185     char host[NI_MAXHOST + 1];
 186     jbyte caddr[4];
 187     jint addr;
 188     struct sockaddr_in sa;
 189 
 190     // construct a sockaddr_in structure
 191     memset((char *)&sa, 0, sizeof(struct sockaddr_in));
 192     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 193     addr = ((caddr[0] << 24) & 0xff000000);
 194     addr |= ((caddr[1] << 16) & 0xff0000);
 195     addr |= ((caddr[2] << 8) & 0xff00);
 196     addr |= (caddr[3] & 0xff);
 197     sa.sin_addr.s_addr = htonl(addr);
 198     sa.sin_family = AF_INET;
 199 
 200     if (getnameinfo((struct sockaddr *)&sa, sizeof(struct sockaddr_in),
 201                     host, NI_MAXHOST, NULL, 0, NI_NAMEREQD)) {
 202         JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
 203     } else {
 204         ret = (*env)->NewStringUTF(env, host);
 205         if (ret == NULL) {
 206             JNU_ThrowByName(env, "java/net/UnknownHostException", NULL);
 207         }
 208     }
 209 
 210     return ret;
 211 }
 212 
 213 /**
 214  * ping implementation using tcp port 7 (echo)
 215  */
 216 static jboolean
 217 tcp_ping4(JNIEnv *env, SOCKETADDRESS *sa, SOCKETADDRESS *netif, jint timeout,
 218           jint ttl)
 219 {
 220     jint fd;
 221     int connect_rv = -1;
 222     WSAEVENT hEvent;
 223 
 224     // open a TCP socket
 225     fd = NET_Socket(AF_INET, SOCK_STREAM, 0);
 226     if (fd == SOCKET_ERROR) {
 227         // note: if you run out of fds, you may not be able to load
 228         // the exception class, and get a NoClassDefFoundError instead.
 229         NET_ThrowNew(env, WSAGetLastError(), "Can't create socket");
 230         return JNI_FALSE;
 231     }
 232 
 233     // set TTL
 234     if (ttl > 0) {
 235         setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
 236     }
 237 
 238     // A network interface was specified, so let's bind to it.
 239     if (netif != NULL) {
 240         if (bind(fd, &netif->sa, sizeof(struct sockaddr_in)) < 0) {
 241             NET_ThrowNew(env, WSAGetLastError(), "Can't bind socket");
 242             closesocket(fd);
 243             return JNI_FALSE;
 244         }
 245     }
 246 
 247     // Make the socket non blocking so we can use select/poll.
 248     hEvent = WSACreateEvent();
 249     WSAEventSelect(fd, hEvent, FD_READ|FD_CONNECT|FD_CLOSE);
 250 
 251     sa->sa4.sin_port = htons(7); // echo port
 252     connect_rv = connect(fd, &sa->sa, sizeof(struct sockaddr_in));
 253 
 254     // connection established or refused immediately, either way it means
 255     // we were able to reach the host!
 256     if (connect_rv == 0 || WSAGetLastError() == WSAECONNREFUSED) {
 257         WSACloseEvent(hEvent);
 258         closesocket(fd);
 259         return JNI_TRUE;
 260     }
 261 
 262     switch (WSAGetLastError()) {
 263     case WSAEHOSTUNREACH:   // Host Unreachable
 264     case WSAENETUNREACH:    // Network Unreachable
 265     case WSAENETDOWN:       // Network is down
 266     case WSAEPFNOSUPPORT:   // Protocol Family unsupported
 267         WSACloseEvent(hEvent);
 268         closesocket(fd);
 269         return JNI_FALSE;
 270     case WSAEWOULDBLOCK:    // this is expected as we'll probably have to wait
 271         break;
 272     default:
 273         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
 274                                      "connect failed");
 275         WSACloseEvent(hEvent);
 276         closesocket(fd);
 277         return JNI_FALSE;
 278     }
 279 
 280     timeout = NET_Wait(env, fd, NET_WAIT_CONNECT, timeout);
 281     if (timeout >= 0) {
 282         // connection has been established, check for error condition
 283         int optlen = sizeof(connect_rv);
 284         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&connect_rv,
 285                        &optlen) < 0)
 286         {
 287             connect_rv = WSAGetLastError();
 288         }
 289         if (connect_rv == 0 || connect_rv == WSAECONNREFUSED) {
 290             WSACloseEvent(hEvent);
 291             closesocket(fd);
 292             return JNI_TRUE;
 293         }
 294     }
 295     WSACloseEvent(hEvent);
 296     closesocket(fd);
 297     return JNI_FALSE;
 298 }
 299 
 300 /**
 301  * ping implementation.
 302  * Send a ICMP_ECHO_REQUEST packet every second until either the timeout
 303  * expires or a answer is received.
 304  * Returns true is an ECHO_REPLY is received, otherwise, false.
 305  */
 306 static jboolean
 307 ping4(JNIEnv *env, HANDLE hIcmpFile, SOCKETADDRESS *sa,
 308       SOCKETADDRESS *netif, jint timeout)
 309 {
 310     DWORD dwRetVal = 0;
 311     char SendData[32] = {0};
 312     LPVOID ReplyBuffer = NULL;
 313     DWORD ReplySize = 0;
 314     jboolean ret = JNI_FALSE;
 315 
 316     // See https://msdn.microsoft.com/en-us/library/aa366050%28VS.85%29.aspx
 317     // or https://msdn.microsoft.com/en-us/library/windows/desktop/aa366051%28v=vs.85%29.aspx
 318     ReplySize = sizeof(ICMP_ECHO_REPLY)   // The buffer should be large enough
 319                                           // to hold at least one ICMP_ECHO_REPLY
 320                                           // structure
 321                 + sizeof(SendData)        // plus RequestSize bytes of data.
 322                 + 8;                      // This buffer should also be large enough
 323                                           // to also hold 8 more bytes of data
 324                                           // (the size of an ICMP error message)
 325 
 326     ReplyBuffer = (VOID *)malloc(ReplySize);
 327     if (ReplyBuffer == NULL) {
 328         IcmpCloseHandle(hIcmpFile);
 329         NET_ThrowNew(env, -1, "Unable to allocate memory");
 330         return JNI_FALSE;
 331     }
 332 
 333     if (netif == NULL) {
 334         dwRetVal = IcmpSendEcho(hIcmpFile,  // HANDLE IcmpHandle,
 335                                 sa->sa4.sin_addr.s_addr, // IPAddr DestinationAddress,
 336                                 SendData,   // LPVOID RequestData,
 337                                 sizeof(SendData),   // WORD RequestSize,
 338                                 NULL,       // PIP_OPTION_INFORMATION RequestOptions,
 339                                 ReplyBuffer,// LPVOID ReplyBuffer,
 340                                 ReplySize,  // DWORD ReplySize,
 341                                 // Note: IcmpSendEcho and its derivatives
 342                                 // seem to have an undocumented minimum
 343                                 // timeout of 1000ms below which the
 344                                 // api behaves inconsistently.
 345                                 (timeout < 1000) ? 1000 : timeout); // DWORD Timeout
 346     } else {
 347         dwRetVal = IcmpSendEcho2Ex(hIcmpFile,  // HANDLE IcmpHandle,
 348                                    NULL,       // HANDLE Event
 349                                    NULL,       // PIO_APC_ROUTINE ApcRoutine
 350                                    NULL,       // ApcContext
 351                                    netif->sa4.sin_addr.s_addr, // IPAddr SourceAddress,
 352                                    sa->sa4.sin_addr.s_addr, // IPAddr DestinationAddress,
 353                                    SendData,   // LPVOID RequestData,
 354                                    sizeof(SendData), // WORD RequestSize,
 355                                    NULL,       // PIP_OPTION_INFORMATION RequestOptions,
 356                                    ReplyBuffer,// LPVOID ReplyBuffer,
 357                                    ReplySize,  // DWORD ReplySize,
 358                                    (timeout < 1000) ? 1000 : timeout); // DWORD Timeout
 359     }
 360 
 361     if (dwRetVal == 0) { // if the call failed
 362         TCHAR *buf = NULL;
 363         DWORD n;
 364         DWORD err = WSAGetLastError();
 365         switch (err) {
 366             case ERROR_NO_NETWORK:
 367             case ERROR_NETWORK_UNREACHABLE:
 368             case ERROR_HOST_UNREACHABLE:
 369             case ERROR_PROTOCOL_UNREACHABLE:
 370             case ERROR_PORT_UNREACHABLE:
 371             case ERROR_REQUEST_ABORTED:
 372             case ERROR_INCORRECT_ADDRESS:
 373             case ERROR_HOST_DOWN:
 374             case ERROR_INVALID_COMPUTERNAME:
 375             case ERROR_INVALID_NETNAME:
 376             case WSAEHOSTUNREACH:   /* Host Unreachable */
 377             case WSAENETUNREACH:    /* Network Unreachable */
 378             case WSAENETDOWN:       /* Network is down */
 379             case WSAEPFNOSUPPORT:   /* Protocol Family unsupported */
 380             case IP_REQ_TIMED_OUT:
 381                 break;
 382             default:
 383                 n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
 384                         FORMAT_MESSAGE_FROM_SYSTEM,
 385                         NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 386                         (LPTSTR)&buf, 0, NULL);
 387                 if (n > 3) {
 388                     // Drop final '.', CR, LF
 389                     if (buf[n - 1] == TEXT('\n')) n--;
 390                     if (buf[n - 1] == TEXT('\r')) n--;
 391                     if (buf[n - 1] == TEXT('.')) n--;
 392                     buf[n] = TEXT('\0');
 393                 }
 394                 NET_ThrowNew(env, err, buf);
 395                 LocalFree(buf);
 396                 break;
 397         }
 398     } else {
 399         PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
 400 
 401         // This is to take into account the undocumented minimum
 402         // timeout mentioned in the IcmpSendEcho call above.
 403         // We perform an extra check to make sure that our
 404         // roundtrip time was less than our desired timeout
 405         // for cases where that timeout is < 1000ms.
 406         if (pEchoReply->Status == IP_SUCCESS &&
 407             (int)pEchoReply->RoundTripTime <= timeout)
 408         {
 409             ret = JNI_TRUE;
 410         }
 411     }
 412 
 413     free(ReplyBuffer);
 414     IcmpCloseHandle(hIcmpFile);
 415 
 416     return ret;
 417 }
 418 
 419 /*
 420  * Class:     java_net_Inet4AddressImpl
 421  * Method:    isReachable0
 422  * Signature: ([bI[bI)Z
 423  */
 424 JNIEXPORT jboolean JNICALL
 425 Java_java_net_Inet4AddressImpl_isReachable0(JNIEnv *env, jobject this,
 426                                             jbyteArray addrArray, jint timeout,
 427                                             jbyteArray ifArray, jint ttl)
 428 {
 429     jbyte caddr[4];
 430     jint addr = 0, sz;
 431     SOCKETADDRESS sa, inf, *netif = NULL;
 432     HANDLE hIcmpFile;
 433 
 434     // check if address array size is 4 (IPv4 address)
 435     sz = (*env)->GetArrayLength(env, addrArray);
 436     if (sz != 4) {
 437       return JNI_FALSE;
 438     }
 439 
 440     // convert IP address from byte array to integer
 441     memset((char *)caddr, 0, sizeof(caddr));
 442     (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
 443     addr = ((caddr[0] << 24) & 0xff000000);
 444     addr |= ((caddr[1] << 16) & 0xff0000);
 445     addr |= ((caddr[2] << 8) & 0xff00);
 446     addr |= (caddr[3] & 0xff);
 447     memset((char *)&sa, 0, sizeof(SOCKETADDRESS));
 448     sa.sa4.sin_addr.s_addr = htonl(addr);
 449     sa.sa4.sin_family = AF_INET;
 450 
 451     // If a network interface was specified, let's convert its address as well.
 452     if (!(IS_NULL(ifArray))) {
 453         memset((char *)caddr, 0, sizeof(caddr));
 454         (*env)->GetByteArrayRegion(env, ifArray, 0, 4, caddr);
 455         addr = ((caddr[0] << 24) & 0xff000000);
 456         addr |= ((caddr[1] << 16) & 0xff0000);
 457         addr |= ((caddr[2] << 8) & 0xff00);
 458         addr |= (caddr[3] & 0xff);
 459         memset((char *)&inf, 0, sizeof(SOCKETADDRESS));
 460         inf.sa4.sin_addr.s_addr = htonl(addr);
 461         inf.sa4.sin_family = AF_INET;
 462         netif = &inf;
 463     }
 464 
 465     // Let's try to create an ICMP handle.
 466     hIcmpFile = IcmpCreateFile();
 467     if (hIcmpFile == INVALID_HANDLE_VALUE) {
 468         int err = WSAGetLastError();
 469         if (err == ERROR_ACCESS_DENIED) {
 470             // fall back to TCP echo if access is denied to ICMP
 471             return tcp_ping4(env, &sa, netif, timeout, ttl);
 472         } else {
 473             NET_ThrowNew(env, err, "Unable to create ICMP file handle");
 474             return JNI_FALSE;
 475         }
 476     } else {
 477         // It didn't fail, so we can use ICMP.
 478         return ping4(env, hIcmpFile, &sa, netif, timeout);
 479     }
 480 }