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