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