1 /*
   2  * Copyright (c) 2007, 2018, 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 "net_util.h"
  26 
  27 #include "java_net_DualStackPlainDatagramSocketImpl.h"
  28 
  29 /*
  30  * This function "purges" all outstanding ICMP port unreachable packets
  31  * outstanding on a socket and returns JNI_TRUE if any ICMP messages
  32  * have been purged. The rational for purging is to emulate normal BSD
  33  * behaviour whereby receiving a "connection reset" status resets the
  34  * socket.
  35  */
  36 static jboolean purgeOutstandingICMP(JNIEnv *env, jint fd)
  37 {
  38     jboolean got_icmp = JNI_FALSE;
  39     char buf[1];
  40     fd_set tbl;
  41     struct timeval t = { 0, 0 };
  42     SOCKETADDRESS rmtaddr;
  43     int addrlen = sizeof(rmtaddr);
  44 
  45     /*
  46      * Peek at the queue to see if there is an ICMP port unreachable. If there
  47      * is then receive it.
  48      */
  49     FD_ZERO(&tbl);
  50     FD_SET(fd, &tbl);
  51     while(1) {
  52         if (select(/*ignored*/fd+1, &tbl, 0, 0, &t) <= 0) {
  53             break;
  54         }
  55         if (recvfrom(fd, buf, 1, MSG_PEEK,
  56                      &rmtaddr.sa, &addrlen) != SOCKET_ERROR) {
  57             break;
  58         }
  59         if (WSAGetLastError() != WSAECONNRESET) {
  60             /* some other error - we don't care here */
  61             break;
  62         }
  63 
  64         recvfrom(fd, buf, 1, 0, &rmtaddr.sa, &addrlen);
  65         got_icmp = JNI_TRUE;
  66     }
  67 
  68     return got_icmp;
  69 }
  70 
  71 static jfieldID IO_fd_fdID = NULL;
  72 static jfieldID pdsi_fdID = NULL;
  73 
  74 /*
  75  * Class:     java_net_DualStackPlainDatagramSocketImpl
  76  * Method:    initIDs
  77  * Signature: ()V
  78  */
  79 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_initIDs
  80   (JNIEnv *env, jclass clazz)
  81 {
  82     pdsi_fdID = (*env)->GetFieldID(env, clazz, "fd",
  83                                    "Ljava/io/FileDescriptor;");
  84     CHECK_NULL(pdsi_fdID);
  85     IO_fd_fdID = NET_GetFileDescriptorID(env);
  86     CHECK_NULL(IO_fd_fdID);
  87     JNU_CHECK_EXCEPTION(env);
  88 
  89     initInetAddressIDs(env);
  90 }
  91 
  92 /*
  93  * Class:     java_net_DualStackPlainDatagramSocketImpl
  94  * Method:    socketCreate
  95  * Signature: ()I
  96  */
  97 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketCreate
  98   (JNIEnv *env, jclass clazz) {
  99     int fd, rv, opt=0, t=TRUE;
 100     DWORD x1, x2; /* ignored result codes */
 101 
 102     fd = (int) socket(AF_INET6, SOCK_DGRAM, 0);
 103     if (fd == INVALID_SOCKET) {
 104         NET_ThrowNew(env, WSAGetLastError(), "Socket creation failed");
 105         return -1;
 106     }
 107 
 108     rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
 109     if (rv == SOCKET_ERROR) {
 110         NET_ThrowNew(env, WSAGetLastError(), "Socket creation failed");
 111         closesocket(fd);
 112         return -1;
 113     }
 114 
 115     SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
 116     NET_SetSockOpt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL));
 117 
 118     /* SIO_UDP_CONNRESET fixes a "bug" introduced in Windows 2000, which
 119      * returns connection reset errors on unconnected UDP sockets (as well
 120      * as connected sockets). The solution is to only enable this feature
 121      * when the socket is connected.
 122      */
 123     t = FALSE;
 124     WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
 125 
 126     return fd;
 127 }
 128 
 129 /*
 130  * Class:     java_net_DualStackPlainDatagramSocketImpl
 131  * Method:    socketBind
 132  * Signature: (ILjava/net/InetAddress;I)V
 133  */
 134 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketBind
 135   (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port, jboolean exclBind) {
 136     SOCKETADDRESS sa;
 137     int rv, sa_len = 0;
 138 
 139     if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,
 140                                  &sa_len, JNI_TRUE) != 0) {
 141         return;
 142     }
 143     rv = NET_WinBind(fd, &sa, sa_len, exclBind);
 144 
 145     if (rv == SOCKET_ERROR) {
 146         if (WSAGetLastError() == WSAEACCES) {
 147             WSASetLastError(WSAEADDRINUSE);
 148         }
 149         NET_ThrowNew(env, WSAGetLastError(), "Cannot bind");
 150     }
 151 }
 152 
 153 /*
 154  * Class:     java_net_DualStackPlainDatagramSocketImpl
 155  * Method:    socketConnect
 156  * Signature: (ILjava/net/InetAddress;I)V
 157  */
 158 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketConnect
 159   (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
 160     SOCKETADDRESS sa;
 161     int rv, sa_len = 0, t;
 162     DWORD x1, x2; /* ignored result codes */
 163 
 164     if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,
 165                                    &sa_len, JNI_TRUE) != 0) {
 166         return;
 167     }
 168 
 169     rv = connect(fd, &sa.sa, sa_len);
 170     if (rv == SOCKET_ERROR) {
 171         NET_ThrowNew(env, WSAGetLastError(), "connect");
 172         return;
 173     }
 174 
 175     /* see comment in socketCreate */
 176     t = TRUE;
 177     WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
 178 }
 179 
 180 /*
 181  * Class:     java_net_DualStackPlainDatagramSocketImpl
 182  * Method:    socketDisconnect
 183  * Signature: (I)V
 184  */
 185 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketDisconnect
 186   (JNIEnv *env, jclass clazz, jint fd ) {
 187     SOCKETADDRESS sa;
 188     int sa_len = sizeof(sa);
 189     DWORD x1, x2; /* ignored result codes */
 190     int t = FALSE;
 191 
 192     memset(&sa, 0, sa_len);
 193     connect(fd, &sa.sa, sa_len);
 194 
 195     /* see comment in socketCreate */
 196     WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
 197 }
 198 
 199 /*
 200  * Class:     java_net_DualStackPlainDatagramSocketImpl
 201  * Method:    socketClose
 202  * Signature: (I)V
 203  */
 204 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketClose
 205   (JNIEnv *env, jclass clazz , jint fd) {
 206     NET_SocketClose(fd);
 207 }
 208 
 209 
 210 /*
 211  * Class:     java_net_DualStackPlainDatagramSocketImpl
 212  * Method:    socketLocalPort
 213  * Signature: (I)I
 214  */
 215 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalPort
 216   (JNIEnv *env, jclass clazz, jint fd) {
 217     SOCKETADDRESS sa;
 218     int len = sizeof(sa);
 219 
 220     if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
 221         NET_ThrowNew(env, WSAGetLastError(), "getsockname");
 222         return -1;
 223     }
 224     return (int) ntohs((u_short)GET_PORT(&sa));
 225 }
 226 
 227 /*
 228  * Class:     java_net_DualStackPlainDatagramSocketImpl
 229  * Method:    socketLocalAddress
 230  * Signature: (I)Ljava/lang/Object;
 231  */
 232 JNIEXPORT jobject JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalAddress
 233   (JNIEnv *env , jclass clazz, jint fd) {
 234     SOCKETADDRESS sa;
 235     int len = sizeof(sa);
 236     jobject iaObj;
 237     int port;
 238 
 239     if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
 240         NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
 241         return NULL;
 242     }
 243 
 244     iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
 245     return iaObj;
 246 }
 247 
 248 /*
 249  * Class:     java_net_DualStackPlainDatagramSocketImpl
 250  * Method:    socketReceiveOrPeekData
 251  * Signature: (ILjava/net/DatagramPacket;IZZ)I
 252  */
 253 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketReceiveOrPeekData
 254   (JNIEnv *env, jclass clazz, jint fd, jobject dpObj,
 255    jint timeout, jboolean connected, jboolean peek) {
 256     SOCKETADDRESS sa;
 257     int sa_len = sizeof(sa);
 258     int port, rv, flags=0;
 259     char BUF[MAX_BUFFER_LEN];
 260     char *fullPacket;
 261     BOOL retry;
 262     jlong prevTime = 0;
 263 
 264     jint packetBufferOffset, packetBufferLen;
 265     jbyteArray packetBuffer;
 266 
 267     /* if we are only peeking. Called from peekData */
 268     if (peek) {
 269         flags = MSG_PEEK;
 270     }
 271 
 272     packetBuffer = (*env)->GetObjectField(env, dpObj, dp_bufID);
 273     packetBufferOffset = (*env)->GetIntField(env, dpObj, dp_offsetID);
 274     packetBufferLen = (*env)->GetIntField(env, dpObj, dp_bufLengthID);
 275     /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
 276     * the max size of an IP packet. Anything bigger is truncated anyway.
 277     */
 278     if (packetBufferLen > MAX_PACKET_LEN) {
 279         packetBufferLen = MAX_PACKET_LEN;
 280     }
 281 
 282     if (packetBufferLen > MAX_BUFFER_LEN) {
 283         fullPacket = (char *)malloc(packetBufferLen);
 284         if (!fullPacket) {
 285             JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
 286             return -1;
 287         }
 288     } else {
 289         fullPacket = &(BUF[0]);
 290     }
 291 
 292     do {
 293         retry = FALSE;
 294 
 295         if (timeout) {
 296             if (prevTime == 0) {
 297                 prevTime = JVM_CurrentTimeMillis(env, 0);
 298             }
 299             rv = NET_Timeout(fd, timeout);
 300             if (rv <= 0) {
 301                 if (rv == 0) {
 302                     JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException",
 303                                     "Receive timed out");
 304                 } else if (rv == -1) {
 305                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 306                                     "Socket closed");
 307                 }
 308                 if (packetBufferLen > MAX_BUFFER_LEN) {
 309                     free(fullPacket);
 310                 }
 311                 return -1;
 312             }
 313         }
 314 
 315         /* receive the packet */
 316         rv = recvfrom(fd, fullPacket, packetBufferLen, flags,
 317                       &sa.sa, &sa_len);
 318 
 319         if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) {
 320             /* An icmp port unreachable - we must receive this as Windows
 321              * does not reset the state of the socket until this has been
 322              * received.
 323              */
 324             purgeOutstandingICMP(env, fd);
 325 
 326             if (connected) {
 327                 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 328                                 "ICMP Port Unreachable");
 329                 if (packetBufferLen > MAX_BUFFER_LEN)
 330                     free(fullPacket);
 331                 return -1;
 332             } else if (timeout) {
 333                 /* Adjust timeout */
 334                 jlong newTime = JVM_CurrentTimeMillis(env, 0);
 335                 timeout -= (jint)(newTime - prevTime);
 336                 if (timeout <= 0) {
 337                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 338                                     "Receive timed out");
 339                     if (packetBufferLen > MAX_BUFFER_LEN)
 340                         free(fullPacket);
 341                     return -1;
 342                 }
 343                 prevTime = newTime;
 344             }
 345             retry = TRUE;
 346         }
 347         if (!peek && rv > 0) {
 348             JVM_callNetworkReadBytes(env, rv);
 349         }
 350     } while (retry);
 351 
 352     port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa));
 353 
 354     /* truncate the data if the packet's length is too small */
 355     if (rv > packetBufferLen) {
 356         rv = packetBufferLen;
 357     }
 358     if (rv < 0) {
 359         if (WSAGetLastError() == WSAEMSGSIZE) {
 360             /* it is because the buffer is too small. It's UDP, it's
 361              * unreliable, it's all good. discard the rest of the
 362              * data..
 363              */
 364             rv = packetBufferLen;
 365         } else {
 366             /* failure */
 367             (*env)->SetIntField(env, dpObj, dp_lengthID, 0);
 368         }
 369     }
 370 
 371     if (rv == -1) {
 372         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
 373     } else if (rv == -2) {
 374         JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 375                         "operation interrupted");
 376     } else if (rv < 0) {
 377         NET_ThrowCurrent(env, "Datagram receive failed");
 378     } else {
 379         jobject packetAddress;
 380         /*
 381          * Check if there is an InetAddress already associated with this
 382          * packet. If so, we check if it is the same source address. We
 383          * can't update any existing InetAddress because it is immutable
 384          */
 385         packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID);
 386         if (packetAddress != NULL) {
 387             if (!NET_SockaddrEqualsInetAddress(env, &sa, packetAddress)) {
 388                 /* force a new InetAddress to be created */
 389                 packetAddress = NULL;
 390             }
 391         }
 392         if (!(*env)->ExceptionCheck(env)){
 393             if (packetAddress == NULL ) {
 394                 packetAddress = NET_SockaddrToInetAddress(env, &sa, &port);
 395                 if (packetAddress != NULL) {
 396                     /* stuff the new InetAddress into the packet */
 397                     (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress);
 398                 }
 399             }
 400             /* populate the packet */
 401             (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, rv,
 402                                    (jbyte *)fullPacket);
 403             (*env)->SetIntField(env, dpObj, dp_portID, port);
 404             (*env)->SetIntField(env, dpObj, dp_lengthID, rv);
 405         }
 406     }
 407 
 408     if (packetBufferLen > MAX_BUFFER_LEN) {
 409         free(fullPacket);
 410     }
 411     return port;
 412 }
 413 
 414 /*
 415  * Class:     java_net_DualStackPlainDatagramSocketImpl
 416  * Method:    socketSend
 417  * Signature: (I[BIILjava/net/InetAddress;IZ)V
 418  */
 419 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSend
 420   (JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint offset, jint length,
 421      jobject iaObj, jint port, jboolean connected) {
 422     SOCKETADDRESS sa;
 423     int rv, sa_len = 0;
 424     struct sockaddr *sap = 0;
 425     char BUF[MAX_BUFFER_LEN];
 426     char *fullPacket;
 427 
 428     // if already connected, sap arg to sendto() is null
 429     if (!connected) {
 430         if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,
 431                                       &sa_len, JNI_TRUE) != 0) {
 432             return;
 433         }
 434         sap = &sa.sa;
 435     }
 436 
 437     if (length > MAX_BUFFER_LEN) {
 438         /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
 439          * the max size of an IP packet. Anything bigger is truncated anyway.
 440          */
 441         if (length > MAX_PACKET_LEN) {
 442             length = MAX_PACKET_LEN;
 443         }
 444         fullPacket = (char *)malloc(length);
 445         if (!fullPacket) {
 446             JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
 447             return;
 448         }
 449     } else {
 450         fullPacket = &(BUF[0]);
 451     }
 452 
 453     (*env)->GetByteArrayRegion(env, data, offset, length,
 454                                (jbyte *)fullPacket);
 455     rv = sendto(fd, fullPacket, length, 0, sap, sa_len);
 456     if (rv == SOCKET_ERROR) {
 457         if (rv == -1) {
 458             NET_ThrowNew(env, WSAGetLastError(), "Datagram send failed");
 459         }
 460     }
 461 
 462     if (rv > 0) {
 463       JVM_callFileWriteBytes(env, rv);
 464     }
 465 
 466     if (length > MAX_BUFFER_LEN) {
 467         free(fullPacket);
 468     }
 469 }
 470 
 471 /*
 472  * Class:     java_net_DualStackPlainDatagramSocketImpl
 473  * Method:    socketSetIntOption
 474  * Signature: (III)V
 475  */
 476 JNIEXPORT void JNICALL
 477 Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption
 478   (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value)
 479 {
 480     int level = 0, opt = 0;
 481 
 482     if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
 483         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
 484         return;
 485     }
 486 
 487     if (NET_SetSockOpt(fd, level, opt, (char *)&value, sizeof(value)) < 0) {
 488         NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
 489     }
 490 }
 491 
 492 /*
 493  * Class:     java_net_DualStackPlainDatagramSocketImpl
 494  * Method:    socketGetIntOption
 495  * Signature: (II)I
 496  */
 497 JNIEXPORT jint JNICALL
 498 Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption
 499   (JNIEnv *env, jclass clazz, jint fd, jint cmd)
 500 {
 501     int level = 0, opt = 0, result = 0;
 502     int result_len = sizeof(result);
 503 
 504     if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
 505         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
 506         return -1;
 507     }
 508 
 509     if (NET_GetSockOpt(fd, level, opt, (void *)&result, &result_len) < 0) {
 510         NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
 511         return -1;
 512     }
 513 
 514     return result;
 515 }
 516 
 517 /*
 518  * Class:     java_net_DualStackPlainDatagramSocketImpl
 519  * Method:    dataAvailable
 520  * Signature: ()I
 521  */
 522 JNIEXPORT jint JNICALL
 523 Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable
 524   (JNIEnv *env, jobject this)
 525 {
 526     SOCKET fd;
 527     int  rv = -1;
 528     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 529 
 530     if (!IS_NULL(fdObj)) {
 531         int retval = 0;
 532         fd = (SOCKET)(*env)->GetIntField(env, fdObj, IO_fd_fdID);
 533         rv = ioctlsocket(fd, FIONREAD, &retval);
 534         if (retval > 0) {
 535             return retval;
 536         }
 537     }
 538 
 539     if (rv < 0) {
 540         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 541                         "Socket closed");
 542         return -1;
 543     }
 544 
 545     return 0;
 546 }