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 ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); 300 isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); 301 (*env)->SetObjectArrayElement(env, isaa, 0, isa); 302 303 return newfd; 304 } 305 306 /* 307 * Class: java_net_DualStackPlainSocketImpl 308 * Method: waitForNewConnection 309 * Signature: (II)V 310 */ 311 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForNewConnection 312 (JNIEnv *env, jclass clazz, jint fd, jint timeout) { 313 int rv; 314 315 rv = NET_Timeout(fd, timeout); 316 if (rv == 0) { 317 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", 318 "Accept timed out"); 319 } else if (rv == -1) { 320 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); 321 } else if (rv == -2) { 322 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", 323 "operation interrupted"); 324 } 325 } 326 327 /* 328 * Class: java_net_DualStackPlainSocketImpl 329 * Method: available0 330 * Signature: (I)I 331 */ 332 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_available0 333 (JNIEnv *env, jclass clazz, jint fd) { 334 jint available = -1; 335 336 if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) { 337 NET_ThrowNew(env, WSAGetLastError(), "socket available"); 338 } 339 340 return available; 341 } 342 343 /* 344 * Class: java_net_DualStackPlainSocketImpl 345 * Method: close0 346 * Signature: (I)V 347 */ 348 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_close0 349 (JNIEnv *env, jclass clazz, jint fd) { 350 NET_SocketClose(fd); 351 } 352 353 /* 354 * Class: java_net_DualStackPlainSocketImpl 355 * Method: shutdown0 356 * Signature: (II)V 357 */ 358 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_shutdown0 359 (JNIEnv *env, jclass clazz, jint fd, jint howto) { 360 shutdown(fd, howto); 361 } 362 363 364 /* 365 * Class: java_net_DualStackPlainSocketImpl 366 * Method: setIntOption 367 * Signature: (III)V 368 */ 369 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_setIntOption 370 (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) { 371 372 int level = 0, opt = 0; 373 struct linger linger = {0, 0}; 374 char *parg; 375 int arglen; 376 377 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 378 JNU_ThrowByNameWithLastError(env, 379 JNU_JAVANETPKG "SocketException", 380 "Invalid option"); 381 return; 382 } 383 384 if (opt == java_net_SocketOptions_SO_LINGER) { 385 parg = (char *)&linger; 386 arglen = sizeof(linger); 387 if (value >= 0) { 388 linger.l_onoff = 1; 389 linger.l_linger = (unsigned short)value; 390 } else { 391 linger.l_onoff = 0; 392 linger.l_linger = 0; 393 } 394 } else { 395 parg = (char *)&value; 396 arglen = sizeof(value); 397 } 398 399 if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) { 400 NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); 401 } 402 } 403 404 /* 405 * Class: java_net_DualStackPlainSocketImpl 406 * Method: getIntOption 407 * Signature: (II)I 408 */ 409 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption 410 (JNIEnv *env, jclass clazz, jint fd, jint cmd) { 411 412 int level = 0, opt = 0; 413 int result=0; 414 struct linger linger = {0, 0}; 415 char *arg; 416 int arglen; 417 418 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 419 JNU_ThrowByNameWithLastError(env, 420 JNU_JAVANETPKG "SocketException", 421 "Unsupported socket option"); 422 return -1; 423 } 424 425 if (opt == java_net_SocketOptions_SO_LINGER) { 426 arg = (char *)&linger; 427 arglen = sizeof(linger); 428 } else { 429 arg = (char *)&result; 430 arglen = sizeof(result); 431 } 432 433 if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) { 434 NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); 435 return -1; 436 } 437 438 if (opt == java_net_SocketOptions_SO_LINGER) 439 return linger.l_onoff ? linger.l_linger : -1; 440 else 441 return result; 442 } 443 444 445 /* 446 * Class: java_net_DualStackPlainSocketImpl 447 * Method: sendOOB 448 * Signature: (II)V 449 */ 450 JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_sendOOB 451 (JNIEnv *env, jclass clazz, jint fd, jint data) { 452 jint n; 453 unsigned char d = (unsigned char) data & 0xff; 454 455 n = send(fd, (char *)&data, 1, MSG_OOB); 456 if (n == SOCKET_ERROR) { 457 NET_ThrowNew(env, WSAGetLastError(), "send"); 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 }