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