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_DualStackPlainSocketImpl.h"
  30 
  31 #define SET_BLOCKING 0
  32 #define SET_NONBLOCKING 1
  33 
  34 static jclass isa_class;        /* java.net.InetSocketAddress */
  35 static jmethodID isa_ctorID;    /* InetSocketAddress(InetAddress, int) */
  36 
  37 /*
  38  * Class:     java_net_DualStackPlainSocketImpl
  39  * Method:    initIDs
  40  * Signature: ()V
  41  */
  42 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_initIDs
  43   (JNIEnv *env, jclass clazz) {
  44 
  45     jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
  46     CHECK_NULL(cls);
  47     isa_class = (*env)->NewGlobalRef(env, cls);
  48     isa_ctorID = (*env)->GetMethodID(env, cls, "<init>",
  49                                      "(Ljava/net/InetAddress;I)V");
  50 
  51     // implement read timeout with select.
  52     isRcvTimeoutSupported = 0;
  53 }
  54 
  55 /*
  56  * Class:     java_net_DualStackPlainSocketImpl
  57  * Method:    socket0
  58  * Signature: (ZZ)I
  59  */
  60 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0
  61   (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) {
  62     int fd, rv, opt=0;
  63 
  64     fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0);
  65     if (fd == INVALID_SOCKET) {
  66         NET_ThrowNew(env, WSAGetLastError(), "create");
  67         return -1;
  68     }
  69 
  70     rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
  71     if (rv == SOCKET_ERROR) {
  72         NET_ThrowNew(env, WSAGetLastError(), "create");
  73     }
  74 
  75     SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
  76 
  77     return fd;
  78 }
  79 
  80 /*
  81  * Class:     java_net_DualStackPlainSocketImpl
  82  * Method:    bind0
  83  * Signature: (ILjava/net/InetAddress;I)V
  84  */
  85 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0
  86   (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,
  87    jboolean exclBind)
  88 {
  89     SOCKETADDRESS sa;
  90     int rv;
  91     int sa_len = sizeof(sa);
  92 
  93     if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
  94                                  &sa_len, JNI_TRUE) != 0) {
  95       return;
  96     }
  97 
  98     rv = NET_WinBind(fd, (struct sockaddr *)&sa, sa_len, exclBind);
  99 
 100     if (rv == SOCKET_ERROR)
 101         NET_ThrowNew(env, WSAGetLastError(), "JVM_Bind");
 102 }
 103 
 104 /*
 105  * Class:     java_net_DualStackPlainSocketImpl
 106  * Method:    connect0
 107  * Signature: (ILjava/net/InetAddress;I)I
 108  */
 109 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_connect0
 110   (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
 111     SOCKETADDRESS sa;
 112     int rv;
 113     int sa_len = sizeof(sa);
 114 
 115     if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
 116                                  &sa_len, JNI_TRUE) != 0) {
 117       return -1;
 118     }
 119 
 120     rv = connect(fd, (struct sockaddr *)&sa, sa_len);
 121     if (rv == SOCKET_ERROR) {
 122         int err = WSAGetLastError();
 123         if (err == WSAEWOULDBLOCK) {
 124             return java_net_DualStackPlainSocketImpl_WOULDBLOCK;
 125         } else if (err == WSAEADDRNOTAVAIL) {
 126             JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",
 127                 "connect: Address is invalid on local machine, or port is not valid on remote machine");
 128         } else {
 129             NET_ThrowNew(env, err, "connect");
 130         }
 131         return -1;  // return value not important.
 132     }
 133     return rv;
 134 }
 135 
 136 /*
 137  * Class:     java_net_DualStackPlainSocketImpl
 138  * Method:    waitForConnect
 139  * Signature: (II)V
 140  */
 141 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForConnect
 142   (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
 143     int rv, retry;
 144     int optlen = sizeof(rv);
 145     fd_set wr, ex;
 146     struct timeval t;
 147 
 148     FD_ZERO(&wr);
 149     FD_ZERO(&ex);
 150     FD_SET(fd, &wr);
 151     FD_SET(fd, &ex);
 152     t.tv_sec = timeout / 1000;
 153     t.tv_usec = (timeout % 1000) * 1000;
 154 
 155     /*
 156      * Wait for timeout, connection established or
 157      * connection failed.
 158      */
 159     rv = select(fd+1, 0, &wr, &ex, &t);
 160 
 161     /*
 162      * Timeout before connection is established/failed so
 163      * we throw exception and shutdown input/output to prevent
 164      * socket from being used.
 165      * The socket should be closed immediately by the caller.
 166      */
 167     if (rv == 0) {
 168         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 169                         "connect timed out");
 170         shutdown( fd, SD_BOTH );
 171         return;
 172     }
 173 
 174     /*
 175      * Socket is writable or error occurred. On some Windows editions
 176      * the socket will appear writable when the connect fails so we
 177      * check for error rather than writable.
 178      */
 179     if (!FD_ISSET(fd, &ex)) {
 180         return;         /* connection established */
 181     }
 182 
 183     /*
 184      * Connection failed. The logic here is designed to work around
 185      * bug on Windows NT whereby using getsockopt to obtain the
 186      * last error (SO_ERROR) indicates there is no error. The workaround
 187      * on NT is to allow winsock to be scheduled and this is done by
 188      * yielding and retrying. As yielding is problematic in heavy
 189      * load conditions we attempt up to 3 times to get the error reason.
 190      */
 191     for (retry=0; retry<3; retry++) {
 192         NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,
 193                        (char*)&rv, &optlen);
 194         if (rv) {
 195             break;
 196         }
 197         Sleep(0);
 198     }
 199 
 200     if (rv == 0) {
 201         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 202                         "Unable to establish connection");
 203     } else {
 204         NET_ThrowNew(env, rv, "connect");
 205     }
 206 }
 207 
 208 /*
 209  * Class:     java_net_DualStackPlainSocketImpl
 210  * Method:    localPort0
 211  * Signature: (I)I
 212  */
 213 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_localPort0
 214   (JNIEnv *env, jclass clazz, jint fd) {
 215     SOCKETADDRESS sa;
 216     int len = sizeof(sa);
 217 
 218     if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
 219         if (WSAGetLastError() == WSAENOTSOCK) {
 220             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 221                     "Socket closed");
 222         } else {
 223             NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");
 224         }
 225         return -1;
 226     }
 227     return (int) ntohs((u_short)GET_PORT(&sa));
 228 }
 229 
 230 /*
 231  * Class:     java_net_DualStackPlainSocketImpl
 232  * Method:    localAddress
 233  * Signature: (ILjava/net/InetAddressContainer;)V
 234  */
 235 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_localAddress
 236   (JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) {
 237     int port;
 238     SOCKETADDRESS sa;
 239     int len = sizeof(sa);
 240     jobject iaObj;
 241     jclass iaContainerClass;
 242     jfieldID iaFieldID;
 243 
 244     if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
 245         NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
 246         return;
 247     }
 248     iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
 249     CHECK_NULL(iaObj);
 250 
 251     iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj);
 252     iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;");
 253     CHECK_NULL(iaFieldID);
 254     (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
 255 }
 256 
 257 
 258 /*
 259  * Class:     java_net_DualStackPlainSocketImpl
 260  * Method:    listen0
 261  * Signature: (II)V
 262  */
 263 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0
 264   (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
 265     if (listen(fd, backlog) == SOCKET_ERROR) {
 266         NET_ThrowNew(env, WSAGetLastError(), "listen failed");
 267     }
 268 }
 269 
 270 /*
 271  * Class:     java_net_DualStackPlainSocketImpl
 272  * Method:    accept0
 273  * Signature: (I[Ljava/net/InetSocketAddress;)I
 274  */
 275 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_accept0
 276   (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {
 277     int newfd, port=0;
 278     jobject isa;
 279     jobject ia;
 280     SOCKETADDRESS sa;
 281     int len = sizeof(sa);
 282 
 283     memset((char *)&sa, 0, len);
 284     newfd = accept(fd, (struct sockaddr *)&sa, &len);
 285 
 286     if (newfd == INVALID_SOCKET) {
 287         if (WSAGetLastError() == -2) {
 288             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 289                             "operation interrupted");
 290         } else {
 291             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 292                             "socket closed");
 293         }
 294         return -1;
 295     }
 296 
 297     ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
 298     isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
 299     (*env)->SetObjectArrayElement(env, isaa, 0, isa);
 300 
 301     return newfd;
 302 }
 303 
 304 /*
 305  * Class:     java_net_DualStackPlainSocketImpl
 306  * Method:    waitForNewConnection
 307  * Signature: (II)V
 308  */
 309 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForNewConnection
 310   (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
 311     int rv;
 312 
 313     rv = NET_Timeout(fd, timeout);
 314     if (rv == 0) {
 315         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 316                         "Accept timed out");
 317     } else if (rv == -1) {
 318         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
 319     } else if (rv == -2) {
 320         JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 321                         "operation interrupted");
 322     }
 323 }
 324 
 325 /*
 326  * Class:     java_net_DualStackPlainSocketImpl
 327  * Method:    available0
 328  * Signature: (I)I
 329  */
 330 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_available0
 331   (JNIEnv *env, jclass clazz, jint fd) {
 332     jint available = -1;
 333 
 334     if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) {
 335         NET_ThrowNew(env, WSAGetLastError(), "socket available");
 336     }
 337 
 338     return available;
 339 }
 340 
 341 /*
 342  * Class:     java_net_DualStackPlainSocketImpl
 343  * Method:    close0
 344  * Signature: (I)V
 345  */
 346 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_close0
 347   (JNIEnv *env, jclass clazz, jint fd) {
 348      NET_SocketClose(fd);
 349 }
 350 
 351 /*
 352  * Class:     java_net_DualStackPlainSocketImpl
 353  * Method:    shutdown0
 354  * Signature: (II)V
 355  */
 356 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_shutdown0
 357   (JNIEnv *env, jclass clazz, jint fd, jint howto) {
 358     shutdown(fd, howto);
 359 }
 360 
 361 
 362 /*
 363  * Class:     java_net_DualStackPlainSocketImpl
 364  * Method:    setIntOption
 365  * Signature: (III)V
 366  */
 367 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_setIntOption
 368   (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) {
 369 
 370     int level, opt;
 371     struct linger linger;
 372     char *parg;
 373     int arglen;
 374 
 375     if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
 376         JNU_ThrowByNameWithLastError(env,
 377                                      JNU_JAVANETPKG "SocketException",
 378                                      "Invalid option");
 379         return;
 380     }
 381 
 382     if (opt == java_net_SocketOptions_SO_LINGER) {
 383         parg = (char *)&linger;
 384         arglen = sizeof(linger);
 385         if (value >= 0) {
 386             linger.l_onoff = 1;
 387             linger.l_linger = (unsigned short)value;
 388         } else {
 389             linger.l_onoff = 0;
 390             linger.l_linger = 0;
 391         }
 392     } else {
 393         parg = (char *)&value;
 394         arglen = sizeof(value);
 395     }
 396 
 397     if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) {
 398         NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
 399     }
 400 }
 401 
 402 /*
 403  * Class:     java_net_DualStackPlainSocketImpl
 404  * Method:    getIntOption
 405  * Signature: (II)I
 406  */
 407 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption
 408   (JNIEnv *env, jclass clazz, jint fd, jint cmd) {
 409 
 410     int level, opt;
 411     int result=0;
 412     struct linger linger;
 413     char *arg;
 414     int arglen;
 415 
 416     if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
 417         JNU_ThrowByNameWithLastError(env,
 418                                      JNU_JAVANETPKG "SocketException",
 419                                      "Unsupported socket option");
 420         return -1;
 421     }
 422 
 423     if (opt == java_net_SocketOptions_SO_LINGER) {
 424         arg = (char *)&linger;
 425         arglen = sizeof(linger);
 426     } else {
 427         arg = (char *)&result;
 428         arglen = sizeof(result);
 429     }
 430 
 431     if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) {
 432         NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
 433         return -1;
 434     }
 435 
 436     if (opt == java_net_SocketOptions_SO_LINGER)
 437         return linger.l_onoff ? linger.l_linger : -1;
 438     else
 439         return result;
 440 }
 441 
 442 
 443 /*
 444  * Class:     java_net_DualStackPlainSocketImpl
 445  * Method:    sendOOB
 446  * Signature: (II)V
 447  */
 448 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_sendOOB
 449   (JNIEnv *env, jclass clazz, jint fd, jint data) {
 450     jint n;
 451     unsigned char d = (unsigned char) data & 0xff;
 452 
 453     n = send(fd, (char *)&data, 1, MSG_OOB);
 454     if (n == JVM_IO_ERR) {
 455         NET_ThrowNew(env, WSAGetLastError(), "send");
 456     } else if (n == JVM_IO_INTR) {
 457         JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
 458     }
 459 }
 460 
 461 /*
 462  * Class:     java_net_DualStackPlainSocketImpl
 463  * Method:    configureBlocking
 464  * Signature: (IZ)V
 465  */
 466 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_configureBlocking
 467   (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {
 468     u_long arg;
 469     int result;
 470 
 471     if (blocking == JNI_TRUE) {
 472         arg = SET_BLOCKING;    // 0
 473     } else {
 474         arg = SET_NONBLOCKING;   // 1
 475     }
 476 
 477     result = ioctlsocket(fd, FIONBIO, &arg);
 478     if (result == SOCKET_ERROR) {
 479         NET_ThrowNew(env, WSAGetLastError(), "configureBlocking");
 480     }
 481 }