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