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