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 }