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:    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(), "JVM_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 == JVM_IO_ERR) {
 299                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 300                                     "Socket closed");
 301                 } else if (rv == JVM_IO_INTR) {
 302                     JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 303                                     "operation interrupted");
 304                 }
 305                 if (packetBufferLen > MAX_BUFFER_LEN) {
 306                     free(fullPacket);
 307                 }
 308                 return -1;
 309             }
 310         }
 311 
 312         /* receive the packet */
 313         rv = recvfrom(fd, fullPacket, packetBufferLen, flags,
 314                     (struct sockaddr *)&sa, &sa_len);
 315 
 316         if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) {
 317             /* An icmp port unreachable - we must receive this as Windows
 318              * does not reset the state of the socket until this has been
 319              * received.
 320              */
 321             purgeOutstandingICMP(env, fd);
 322 
 323             if (connected) {
 324                 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 325                                 "ICMP Port Unreachable");
 326                 if (packetBufferLen > MAX_BUFFER_LEN)
 327                     free(fullPacket);
 328                 return -1;
 329             } else if (timeout) {
 330                 /* Adjust timeout */
 331                 jlong newTime = JVM_CurrentTimeMillis(env, 0);
 332                 timeout -= (jint)(newTime - prevTime);
 333                 if (timeout <= 0) {
 334                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 335                                     "Receive timed out");
 336                     if (packetBufferLen > MAX_BUFFER_LEN)
 337                         free(fullPacket);
 338                     return -1;
 339                 }
 340                 prevTime = newTime;
 341             }
 342             retry = TRUE;
 343         }
 344     } while (retry);
 345 
 346     port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa));
 347 
 348     /* truncate the data if the packet's length is too small */
 349     if (rv > packetBufferLen) {
 350         rv = packetBufferLen;
 351     }
 352     if (rv < 0) {
 353         if (WSAGetLastError() == WSAEMSGSIZE) {
 354             /* it is because the buffer is too small. It's UDP, it's
 355              * unreliable, it's all good. discard the rest of the
 356              * data..
 357              */
 358             rv = packetBufferLen;
 359         } else {
 360             /* failure */
 361             (*env)->SetIntField(env, dpObj, dp_lengthID, 0);
 362         }
 363     }
 364 
 365     if (rv == -1) {
 366         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
 367     } else if (rv == -2) {
 368         JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 369                         "operation interrupted");
 370     } else if (rv < 0) {
 371         NET_ThrowCurrent(env, "Datagram receive failed");
 372     } else {
 373         jobject packetAddress;
 374         /*
 375          * Check if there is an InetAddress already associated with this
 376          * packet. If so, we check if it is the same source address. We
 377          * can't update any existing InetAddress because it is immutable
 378          */
 379         packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID);
 380         if (packetAddress != NULL) {
 381             if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa,
 382                                                packetAddress)) {
 383                 /* force a new InetAddress to be created */
 384                 packetAddress = NULL;
 385             }
 386         }
 387         if (packetAddress == NULL) {
 388             packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa,
 389                                                       &port);
 390             /* stuff the new Inetaddress into the packet */
 391             (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress);
 392         }
 393 
 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     if (packetBufferLen > MAX_BUFFER_LEN) {
 402         free(fullPacket);
 403     }
 404     return port;
 405 }
 406 
 407 /*
 408  * Class:     java_net_DualStackPlainDatagramSocketImpl
 409  * Method:    socketSend
 410  * Signature: (I[BIILjava/net/InetAddress;IZ)V
 411  */
 412 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSend
 413   (JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint offset, jint length,
 414      jobject iaObj, jint port, jboolean connected) {
 415     SOCKETADDRESS sa;
 416     int sa_len = sizeof(sa);
 417     SOCKETADDRESS *sap = &sa;
 418     char BUF[MAX_BUFFER_LEN];
 419     char *fullPacket;
 420     int rv;
 421 
 422     if (connected) {
 423         sap = 0; /* arg to JVM_Sendto () null in this case */
 424         sa_len = 0;
 425     } else {
 426         if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
 427                                        &sa_len, JNI_TRUE) != 0) {
 428             return;
 429         }
 430     }
 431 
 432     if (length > MAX_BUFFER_LEN) {
 433         /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
 434          * the max size of an IP packet. Anything bigger is truncated anyway.
 435          */
 436         if (length > MAX_PACKET_LEN) {
 437             length = MAX_PACKET_LEN;
 438         }
 439         fullPacket = (char *)malloc(length);
 440         if (!fullPacket) {
 441             JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
 442             return;
 443         }
 444     } else {
 445         fullPacket = &(BUF[0]);
 446     }
 447 
 448     (*env)->GetByteArrayRegion(env, data, offset, length,
 449                                (jbyte *)fullPacket);
 450     rv = sendto(fd, fullPacket, length, 0, (struct sockaddr *)sap, sa_len);
 451     if (rv == SOCKET_ERROR) {
 452         if (rv == JVM_IO_ERR) {
 453             NET_ThrowNew(env, WSAGetLastError(), "Datagram send failed");
 454         } else if (rv == JVM_IO_INTR) {
 455             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 456                             "operation interrupted");
 457         }
 458     }
 459 
 460     if (length > MAX_BUFFER_LEN) {
 461         free(fullPacket);
 462     }
 463 }
 464 
 465 /*
 466  * Class:     java_net_DualStackPlainDatagramSocketImpl
 467  * Method:    socketSetIntOption
 468  * Signature: (III)V
 469  */
 470 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption
 471   (JNIEnv *env, jclass clazz, jint fd , jint cmd, jint value) {
 472     int level, opt;
 473 
 474     if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
 475         JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
 476                                      "Invalid option");
 477         return;
 478     }
 479 
 480     if (NET_SetSockOpt(fd, level, opt, (char *)&value, sizeof(value)) < 0) {
 481         NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
 482     }
 483 }
 484 
 485 /*
 486  * Class:     java_net_DualStackPlainDatagramSocketImpl
 487  * Method:    socketGetIntOption
 488  * Signature: (II)I
 489  */
 490 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption
 491   (JNIEnv *env, jclass clazz, jint fd, jint cmd) {
 492     int level, opt, result=0;
 493     int result_len = sizeof(result);
 494 
 495     if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
 496         JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
 497                                      "Invalid option");
 498         return -1;
 499     }
 500 
 501     if (NET_GetSockOpt(fd, level, opt, (void *)&result, &result_len) < 0) {
 502         NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
 503         return -1;
 504     }
 505 
 506     return result;
 507 }