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_DualStackPlainDatagramSocketImpl.h" 28 29 /* 30 * This function "purges" all outstanding ICMP port unreachable packets 31 * outstanding on a socket and returns JNI_TRUE if any ICMP messages 32 * have been purged. The rational for purging is to emulate normal BSD 33 * behaviour whereby receiving a "connection reset" status resets the 34 * socket. 35 */ 36 static jboolean purgeOutstandingICMP(JNIEnv *env, jint fd) 37 { 38 jboolean got_icmp = JNI_FALSE; 39 char buf[1]; 40 fd_set tbl; 41 struct timeval t = { 0, 0 }; 42 SOCKETADDRESS rmtaddr; 43 int addrlen = sizeof(rmtaddr); 44 45 /* 46 * Peek at the queue to see if there is an ICMP port unreachable. If there 47 * is then receive it. 48 */ 49 FD_ZERO(&tbl); 50 FD_SET(fd, &tbl); 51 while(1) { 52 if (select(/*ignored*/fd+1, &tbl, 0, 0, &t) <= 0) { 53 break; 54 } 55 if (recvfrom(fd, buf, 1, MSG_PEEK, 56 &rmtaddr.sa, &addrlen) != SOCKET_ERROR) { 57 break; 58 } 59 if (WSAGetLastError() != WSAECONNRESET) { 60 /* some other error - we don't care here */ 61 break; 62 } 63 64 recvfrom(fd, buf, 1, 0, &rmtaddr.sa, &addrlen); 65 got_icmp = JNI_TRUE; 66 } 67 68 return got_icmp; 69 } 70 71 static jfieldID IO_fd_fdID = NULL; 72 static jfieldID pdsi_fdID = NULL; 73 74 /* 75 * Class: java_net_DualStackPlainDatagramSocketImpl 76 * Method: initIDs 77 * Signature: ()V 78 */ 79 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_initIDs 80 (JNIEnv *env, jclass clazz) 81 { 82 pdsi_fdID = (*env)->GetFieldID(env, clazz, "fd", 83 "Ljava/io/FileDescriptor;"); 84 CHECK_NULL(pdsi_fdID); 85 IO_fd_fdID = NET_GetFileDescriptorID(env); 86 CHECK_NULL(IO_fd_fdID); 87 JNU_CHECK_EXCEPTION(env); 88 89 initInetAddressIDs(env); 90 } 91 92 /* 93 * Class: java_net_DualStackPlainDatagramSocketImpl 94 * Method: socketCreate 95 * Signature: ()I 96 */ 97 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketCreate 98 (JNIEnv *env, jclass clazz) { 99 int fd, rv, opt=0, t=TRUE; 100 DWORD x1, x2; /* ignored result codes */ 101 102 fd = (int) socket(AF_INET6, SOCK_DGRAM, 0); 103 if (fd == INVALID_SOCKET) { 104 NET_ThrowNew(env, WSAGetLastError(), "Socket creation failed"); 105 return -1; 106 } 107 108 rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt)); 109 if (rv == SOCKET_ERROR) { 110 NET_ThrowNew(env, WSAGetLastError(), "Socket creation failed"); 111 closesocket(fd); 112 return -1; 113 } 114 115 SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); 116 NET_SetSockOpt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL)); 117 118 /* SIO_UDP_CONNRESET fixes a "bug" introduced in Windows 2000, which 119 * returns connection reset errors on unconnected UDP sockets (as well 120 * as connected sockets). The solution is to only enable this feature 121 * when the socket is connected. 122 */ 123 t = FALSE; 124 WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0); 125 126 return fd; 127 } 128 129 /* 130 * Class: java_net_DualStackPlainDatagramSocketImpl 131 * Method: socketBind 132 * Signature: (ILjava/net/InetAddress;I)V 133 */ 134 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketBind 135 (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port, jboolean exclBind) { 136 SOCKETADDRESS sa; 137 int rv, sa_len = 0; 138 139 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, 140 &sa_len, JNI_TRUE) != 0) { 141 return; 142 } 143 rv = NET_WinBind(fd, &sa, sa_len, exclBind); 144 145 if (rv == SOCKET_ERROR) { 146 if (WSAGetLastError() == WSAEACCES) { 147 WSASetLastError(WSAEADDRINUSE); 148 } 149 NET_ThrowNew(env, WSAGetLastError(), "Cannot bind"); 150 } 151 } 152 153 /* 154 * Class: java_net_DualStackPlainDatagramSocketImpl 155 * Method: socketConnect 156 * Signature: (ILjava/net/InetAddress;I)V 157 */ 158 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketConnect 159 (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) { 160 SOCKETADDRESS sa; 161 int rv, sa_len = 0, t; 162 DWORD x1, x2; /* ignored result codes */ 163 164 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, 165 &sa_len, JNI_TRUE) != 0) { 166 return; 167 } 168 169 rv = connect(fd, &sa.sa, sa_len); 170 if (rv == SOCKET_ERROR) { 171 NET_ThrowNew(env, WSAGetLastError(), "connect"); 172 return; 173 } 174 175 /* see comment in socketCreate */ 176 t = TRUE; 177 WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0); 178 } 179 180 /* 181 * Class: java_net_DualStackPlainDatagramSocketImpl 182 * Method: socketDisconnect 183 * Signature: (I)V 184 */ 185 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketDisconnect 186 (JNIEnv *env, jclass clazz, jint fd ) { 187 SOCKETADDRESS sa; 188 int sa_len = sizeof(sa); 189 DWORD x1, x2; /* ignored result codes */ 190 int t = FALSE; 191 192 memset(&sa, 0, sa_len); 193 connect(fd, &sa.sa, sa_len); 194 195 /* see comment in socketCreate */ 196 WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0); 197 } 198 199 /* 200 * Class: java_net_DualStackPlainDatagramSocketImpl 201 * Method: socketClose 202 * Signature: (I)V 203 */ 204 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketClose 205 (JNIEnv *env, jclass clazz , jint fd) { 206 NET_SocketClose(fd); 207 } 208 209 210 /* 211 * Class: java_net_DualStackPlainDatagramSocketImpl 212 * Method: socketLocalPort 213 * Signature: (I)I 214 */ 215 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalPort 216 (JNIEnv *env, jclass clazz, jint fd) { 217 SOCKETADDRESS sa; 218 int len = sizeof(sa); 219 220 if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) { 221 NET_ThrowNew(env, WSAGetLastError(), "getsockname"); 222 return -1; 223 } 224 return (int) ntohs((u_short)GET_PORT(&sa)); 225 } 226 227 /* 228 * Class: java_net_DualStackPlainDatagramSocketImpl 229 * Method: socketLocalAddress 230 * Signature: (I)Ljava/lang/Object; 231 */ 232 JNIEXPORT jobject JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalAddress 233 (JNIEnv *env , jclass clazz, jint fd) { 234 SOCKETADDRESS sa; 235 int len = sizeof(sa); 236 jobject iaObj; 237 int port; 238 239 if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) { 240 NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name"); 241 return NULL; 242 } 243 244 iaObj = NET_SockaddrToInetAddress(env, &sa, &port); 245 return iaObj; 246 } 247 248 /* 249 * Class: java_net_DualStackPlainDatagramSocketImpl 250 * Method: socketReceiveOrPeekData 251 * Signature: (ILjava/net/DatagramPacket;IZZ)I 252 */ 253 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketReceiveOrPeekData 254 (JNIEnv *env, jclass clazz, jint fd, jobject dpObj, 255 jint timeout, jboolean connected, jboolean peek) { 256 SOCKETADDRESS sa; 257 int sa_len = sizeof(sa); 258 int port, rv, flags=0; 259 char BUF[MAX_BUFFER_LEN]; 260 char *fullPacket; 261 BOOL retry; 262 jlong prevTime = 0; 263 264 jint packetBufferOffset, packetBufferLen; 265 jbyteArray packetBuffer; 266 267 /* if we are only peeking. Called from peekData */ 268 if (peek) { 269 flags = MSG_PEEK; 270 } 271 272 packetBuffer = (*env)->GetObjectField(env, dpObj, dp_bufID); 273 packetBufferOffset = (*env)->GetIntField(env, dpObj, dp_offsetID); 274 packetBufferLen = (*env)->GetIntField(env, dpObj, dp_bufLengthID); 275 /* Note: the buffer needn't be greater than 65,536 (0xFFFF) 276 * the max size of an IP packet. Anything bigger is truncated anyway. 277 */ 278 if (packetBufferLen > MAX_PACKET_LEN) { 279 packetBufferLen = MAX_PACKET_LEN; 280 } 281 282 if (packetBufferLen > MAX_BUFFER_LEN) { 283 fullPacket = (char *)malloc(packetBufferLen); 284 if (!fullPacket) { 285 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); 286 return -1; 287 } 288 } else { 289 fullPacket = &(BUF[0]); 290 } 291 292 do { 293 retry = FALSE; 294 295 if (timeout) { 296 if (prevTime == 0) { 297 prevTime = JVM_CurrentTimeMillis(env, 0); 298 } 299 rv = NET_Timeout(fd, timeout); 300 if (rv <= 0) { 301 if (rv == 0) { 302 JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException", 303 "Receive timed out"); 304 } else if (rv == -1) { 305 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 306 "Socket closed"); 307 } 308 if (packetBufferLen > MAX_BUFFER_LEN) { 309 free(fullPacket); 310 } 311 return -1; 312 } 313 } 314 315 /* receive the packet */ 316 rv = recvfrom(fd, fullPacket, packetBufferLen, flags, 317 &sa.sa, &sa_len); 318 319 if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) { 320 /* An icmp port unreachable - we must receive this as Windows 321 * does not reset the state of the socket until this has been 322 * received. 323 */ 324 purgeOutstandingICMP(env, fd); 325 326 if (connected) { 327 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 328 "ICMP Port Unreachable"); 329 if (packetBufferLen > MAX_BUFFER_LEN) 330 free(fullPacket); 331 return -1; 332 } else if (timeout) { 333 /* Adjust timeout */ 334 jlong newTime = JVM_CurrentTimeMillis(env, 0); 335 timeout -= (jint)(newTime - prevTime); 336 if (timeout <= 0) { 337 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", 338 "Receive timed out"); 339 if (packetBufferLen > MAX_BUFFER_LEN) 340 free(fullPacket); 341 return -1; 342 } 343 prevTime = newTime; 344 } 345 retry = TRUE; 346 } 347 if (!peek && rv > 0) { 348 JVM_callNetworkReadBytes(env, rv); 349 } 350 } while (retry); 351 352 port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa)); 353 354 /* truncate the data if the packet's length is too small */ 355 if (rv > packetBufferLen) { 356 rv = packetBufferLen; 357 } 358 if (rv < 0) { 359 if (WSAGetLastError() == WSAEMSGSIZE) { 360 /* it is because the buffer is too small. It's UDP, it's 361 * unreliable, it's all good. discard the rest of the 362 * data.. 363 */ 364 rv = packetBufferLen; 365 } else { 366 /* failure */ 367 (*env)->SetIntField(env, dpObj, dp_lengthID, 0); 368 } 369 } 370 371 if (rv == -1) { 372 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); 373 } else if (rv == -2) { 374 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", 375 "operation interrupted"); 376 } else if (rv < 0) { 377 NET_ThrowCurrent(env, "Datagram receive failed"); 378 } else { 379 jobject packetAddress; 380 /* 381 * Check if there is an InetAddress already associated with this 382 * packet. If so, we check if it is the same source address. We 383 * can't update any existing InetAddress because it is immutable 384 */ 385 packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID); 386 if (packetAddress != NULL) { 387 if (!NET_SockaddrEqualsInetAddress(env, &sa, packetAddress)) { 388 /* force a new InetAddress to be created */ 389 packetAddress = NULL; 390 } 391 } 392 if (!(*env)->ExceptionCheck(env)){ 393 if (packetAddress == NULL ) { 394 packetAddress = NET_SockaddrToInetAddress(env, &sa, &port); 395 if (packetAddress != NULL) { 396 /* stuff the new InetAddress into the packet */ 397 (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress); 398 } 399 } 400 /* populate the packet */ 401 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, rv, 402 (jbyte *)fullPacket); 403 (*env)->SetIntField(env, dpObj, dp_portID, port); 404 (*env)->SetIntField(env, dpObj, dp_lengthID, rv); 405 } 406 } 407 408 if (packetBufferLen > MAX_BUFFER_LEN) { 409 free(fullPacket); 410 } 411 return port; 412 } 413 414 /* 415 * Class: java_net_DualStackPlainDatagramSocketImpl 416 * Method: socketSend 417 * Signature: (I[BIILjava/net/InetAddress;IZ)V 418 */ 419 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSend 420 (JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint offset, jint length, 421 jobject iaObj, jint port, jboolean connected) { 422 SOCKETADDRESS sa; 423 int rv, sa_len = 0; 424 struct sockaddr *sap = 0; 425 char BUF[MAX_BUFFER_LEN]; 426 char *fullPacket; 427 428 // if already connected, sap arg to sendto() is null 429 if (!connected) { 430 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, 431 &sa_len, JNI_TRUE) != 0) { 432 return; 433 } 434 sap = &sa.sa; 435 } 436 437 if (length > MAX_BUFFER_LEN) { 438 /* Note: the buffer needn't be greater than 65,536 (0xFFFF) 439 * the max size of an IP packet. Anything bigger is truncated anyway. 440 */ 441 if (length > MAX_PACKET_LEN) { 442 length = MAX_PACKET_LEN; 443 } 444 fullPacket = (char *)malloc(length); 445 if (!fullPacket) { 446 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); 447 return; 448 } 449 } else { 450 fullPacket = &(BUF[0]); 451 } 452 453 (*env)->GetByteArrayRegion(env, data, offset, length, 454 (jbyte *)fullPacket); 455 rv = sendto(fd, fullPacket, length, 0, sap, sa_len); 456 if (rv == SOCKET_ERROR) { 457 if (rv == -1) { 458 NET_ThrowNew(env, WSAGetLastError(), "Datagram send failed"); 459 } 460 } 461 462 if (rv > 0) { 463 JVM_callFileWriteBytes(env, rv); 464 } 465 466 if (length > MAX_BUFFER_LEN) { 467 free(fullPacket); 468 } 469 } 470 471 /* 472 * Class: java_net_DualStackPlainDatagramSocketImpl 473 * Method: socketSetIntOption 474 * Signature: (III)V 475 */ 476 JNIEXPORT void JNICALL 477 Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption 478 (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) 479 { 480 int level = 0, opt = 0; 481 482 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 483 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 484 return; 485 } 486 487 if (NET_SetSockOpt(fd, level, opt, (char *)&value, sizeof(value)) < 0) { 488 NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); 489 } 490 } 491 492 /* 493 * Class: java_net_DualStackPlainDatagramSocketImpl 494 * Method: socketGetIntOption 495 * Signature: (II)I 496 */ 497 JNIEXPORT jint JNICALL 498 Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption 499 (JNIEnv *env, jclass clazz, jint fd, jint cmd) 500 { 501 int level = 0, opt = 0, result = 0; 502 int result_len = sizeof(result); 503 504 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 505 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 506 return -1; 507 } 508 509 if (NET_GetSockOpt(fd, level, opt, (void *)&result, &result_len) < 0) { 510 NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); 511 return -1; 512 } 513 514 return result; 515 } 516 517 /* 518 * Class: java_net_DualStackPlainDatagramSocketImpl 519 * Method: dataAvailable 520 * Signature: ()I 521 */ 522 JNIEXPORT jint JNICALL 523 Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable 524 (JNIEnv *env, jobject this) 525 { 526 SOCKET fd; 527 int rv = -1; 528 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 529 530 if (!IS_NULL(fdObj)) { 531 int retval = 0; 532 fd = (SOCKET)(*env)->GetIntField(env, fdObj, IO_fd_fdID); 533 rv = ioctlsocket(fd, FIONREAD, &retval); 534 if (retval > 0) { 535 return retval; 536 } 537 } 538 539 if (rv < 0) { 540 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 541 "Socket closed"); 542 return -1; 543 } 544 545 return 0; 546 }