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 }