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 }