1/* 2 * Copyright (c) 2007, 2016, 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 33static jclass isa_class; /* java.net.InetSocketAddress */ 34static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ 35 36/* 37 * Class: java_net_DualStackPlainSocketImpl 38 * Method: initIDs 39 * Signature: ()V 40 */ 41JNIEXPORT 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 = 0; 55} 56 57/* 58 * Class: java_net_DualStackPlainSocketImpl 59 * Method: socket0 60 * Signature: (ZZ)I 61 */ 62JNIEXPORT 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 */ 87JNIEXPORT 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, sa_len = 0; 93 94 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, 95 &sa_len, JNI_TRUE) != 0) { 96 return; 97 } 98 99 rv = NET_WinBind(fd, &sa, sa_len, exclBind); 100 101 if (rv == SOCKET_ERROR) 102 NET_ThrowNew(env, WSAGetLastError(), "NET_Bind"); 103} 104 105/* 106 * Class: java_net_DualStackPlainSocketImpl 107 * Method: connect0 108 * Signature: (ILjava/net/InetAddress;I)I 109 */ 110JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_connect0 111 (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) { 112 SOCKETADDRESS sa; 113 int rv, sa_len = 0; 114 115 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, 116 &sa_len, JNI_TRUE) != 0) { 117 return -1; 118 } 119 120 rv = connect(fd, &sa.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 */ 141JNIEXPORT 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 */ 213JNIEXPORT 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, &sa.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 */ 235JNIEXPORT 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, &sa.sa, &len) == SOCKET_ERROR) { 245 NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name"); 246 return; 247 } 248 iaObj = NET_SockaddrToInetAddress(env, &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 */ 263JNIEXPORT 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 */ 275JNIEXPORT 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, &sa.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 SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0); 298 299 ia = NET_SockaddrToInetAddress(env, &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 */ 311JNIEXPORT 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 */ 332JNIEXPORT 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 */ 348JNIEXPORT 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 */ 358JNIEXPORT 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 */ 369JNIEXPORT void JNICALL 370Java_java_net_DualStackPlainSocketImpl_setIntOption 371 (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) 372{ 373 int level = 0, opt = 0; 374 struct linger linger = {0, 0}; 375 char *parg; 376 int arglen; 377 378 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 379 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 380 return; 381 } 382 383 if (opt == java_net_SocketOptions_SO_LINGER) { 384 parg = (char *)&linger; 385 arglen = sizeof(linger); 386 if (value >= 0) { 387 linger.l_onoff = 1; 388 linger.l_linger = (unsigned short)value; 389 } else { 390 linger.l_onoff = 0; 391 linger.l_linger = 0; 392 } 393 } else { 394 parg = (char *)&value; 395 arglen = sizeof(value); 396 } 397 398 if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) { 399 NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); 400 } 401} 402 403/* 404 * Class: java_net_DualStackPlainSocketImpl 405 * Method: getIntOption 406 * Signature: (II)I 407 */ 408JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption 409 (JNIEnv *env, jclass clazz, jint fd, jint cmd) 410{ 411 int level = 0, opt = 0; 412 int result=0; 413 struct linger linger = {0, 0}; 414 char *arg; 415 int arglen; 416 417 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 418 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 419 return -1; 420 } 421 422 if (opt == java_net_SocketOptions_SO_LINGER) { 423 arg = (char *)&linger; 424 arglen = sizeof(linger); 425 } else { 426 arg = (char *)&result; 427 arglen = sizeof(result); 428 } 429 430 if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) { 431 NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); 432 return -1; 433 } 434 435 if (opt == java_net_SocketOptions_SO_LINGER) 436 return linger.l_onoff ? linger.l_linger : -1; 437 else 438 return result; 439} 440 441 442/* 443 * Class: java_net_DualStackPlainSocketImpl 444 * Method: sendOOB 445 * Signature: (II)V 446 */ 447JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_sendOOB 448 (JNIEnv *env, jclass clazz, jint fd, jint data) { 449 jint n; 450 unsigned char d = (unsigned char) data & 0xff; 451 452 n = send(fd, (char *)&data, 1, MSG_OOB); 453 if (n == SOCKET_ERROR) { 454 NET_ThrowNew(env, WSAGetLastError(), "send"); 455 } 456} 457 458/* 459 * Class: java_net_DualStackPlainSocketImpl 460 * Method: configureBlocking 461 * Signature: (IZ)V 462 */ 463JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_configureBlocking 464 (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) { 465 u_long arg; 466 int result; 467 468 if (blocking == JNI_TRUE) { 469 arg = SET_BLOCKING; // 0 470 } else { 471 arg = SET_NONBLOCKING; // 1 472 } 473 474 result = ioctlsocket(fd, FIONBIO, &arg); 475 if (result == SOCKET_ERROR) { 476 NET_ThrowNew(env, WSAGetLastError(), "configureBlocking"); 477 } 478} 479