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