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