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