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