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