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 } while (retry); 348 349 port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa)); 350 351 /* truncate the data if the packet's length is too small */ 352 if (rv > packetBufferLen) { 353 rv = packetBufferLen; 354 } 355 if (rv < 0) { 356 if (WSAGetLastError() == WSAEMSGSIZE) { 357 /* it is because the buffer is too small. It's UDP, it's 358 * unreliable, it's all good. discard the rest of the 359 * data.. 360 */ 361 rv = packetBufferLen; 362 } else { 363 /* failure */ 364 (*env)->SetIntField(env, dpObj, dp_lengthID, 0); 365 } 366 } 367 368 if (rv == -1) { 369 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); 370 } else if (rv == -2) { 371 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", 372 "operation interrupted"); 373 } else if (rv < 0) { 374 NET_ThrowCurrent(env, "Datagram receive failed"); 375 } else { 376 jobject packetAddress; 377 /* 378 * Check if there is an InetAddress already associated with this 379 * packet. If so, we check if it is the same source address. We 380 * can't update any existing InetAddress because it is immutable 381 */ 382 packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID); 383 if (packetAddress != NULL) { 384 if (!NET_SockaddrEqualsInetAddress(env, &sa, packetAddress)) { 385 /* force a new InetAddress to be created */ 386 packetAddress = NULL; 387 } 388 } 389 if (!(*env)->ExceptionCheck(env)){ 390 if (packetAddress == NULL ) { 391 packetAddress = NET_SockaddrToInetAddress(env, &sa, &port); 392 if (packetAddress != NULL) { 393 /* stuff the new InetAddress into the packet */ 394 (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress); 395 } 396 } 397 /* populate the packet */ 398 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, rv, 399 (jbyte *)fullPacket); 400 (*env)->SetIntField(env, dpObj, dp_portID, port); 401 (*env)->SetIntField(env, dpObj, dp_lengthID, rv); 402 } 403 } 404 405 if (packetBufferLen > MAX_BUFFER_LEN) { 406 free(fullPacket); 407 } 408 return port; 409 } 410 411 /* 412 * Class: java_net_DualStackPlainDatagramSocketImpl 413 * Method: socketSend 414 * Signature: (I[BIILjava/net/InetAddress;IZ)V 415 */ 416 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSend 417 (JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint offset, jint length, 418 jobject iaObj, jint port, jboolean connected) { 419 SOCKETADDRESS sa; 420 int rv, sa_len = 0; 421 struct sockaddr *sap = 0; 422 char BUF[MAX_BUFFER_LEN]; 423 char *fullPacket; 424 425 // if already connected, sap arg to sendto() is null 426 if (!connected) { 427 if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, 428 &sa_len, JNI_TRUE) != 0) { 429 return; 430 } 431 sap = &sa.sa; 432 } 433 434 if (length > MAX_BUFFER_LEN) { 435 /* Note: the buffer needn't be greater than 65,536 (0xFFFF) 436 * the max size of an IP packet. Anything bigger is truncated anyway. 437 */ 438 if (length > MAX_PACKET_LEN) { 439 length = MAX_PACKET_LEN; 440 } 441 fullPacket = (char *)malloc(length); 442 if (!fullPacket) { 443 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); 444 return; 445 } 446 } else { 447 fullPacket = &(BUF[0]); 448 } 449 450 (*env)->GetByteArrayRegion(env, data, offset, length, 451 (jbyte *)fullPacket); 452 rv = sendto(fd, fullPacket, length, 0, sap, sa_len); 453 if (rv == SOCKET_ERROR) { 454 if (rv == -1) { 455 NET_ThrowNew(env, WSAGetLastError(), "Datagram send failed"); 456 } 457 } 458 459 if (length > MAX_BUFFER_LEN) { 460 free(fullPacket); 461 } 462 } 463 464 /* 465 * Class: java_net_DualStackPlainDatagramSocketImpl 466 * Method: socketSetIntOption 467 * Signature: (III)V 468 */ 469 JNIEXPORT void JNICALL 470 Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption 471 (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) 472 { 473 int level = 0, opt = 0; 474 475 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 476 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 477 return; 478 } 479 480 if (NET_SetSockOpt(fd, level, opt, (char *)&value, sizeof(value)) < 0) { 481 NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); 482 } 483 } 484 485 /* 486 * Class: java_net_DualStackPlainDatagramSocketImpl 487 * Method: socketGetIntOption 488 * Signature: (II)I 489 */ 490 JNIEXPORT jint JNICALL 491 Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption 492 (JNIEnv *env, jclass clazz, jint fd, jint cmd) 493 { 494 int level = 0, opt = 0, result = 0; 495 int result_len = sizeof(result); 496 497 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 498 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 499 return -1; 500 } 501 502 if (NET_GetSockOpt(fd, level, opt, (void *)&result, &result_len) < 0) { 503 NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); 504 return -1; 505 } 506 507 return result; 508 } 509 510 /* 511 * Class: java_net_DualStackPlainDatagramSocketImpl 512 * Method: dataAvailable 513 * Signature: ()I 514 */ 515 JNIEXPORT jint JNICALL 516 Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable 517 (JNIEnv *env, jobject this) 518 { 519 SOCKET fd; 520 int rv = -1; 521 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 522 523 if (!IS_NULL(fdObj)) { 524 int retval = 0; 525 fd = (SOCKET)(*env)->GetIntField(env, fdObj, IO_fd_fdID); 526 rv = ioctlsocket(fd, FIONREAD, &retval); 527 if (retval > 0) { 528 return retval; 529 } 530 } 531 532 if (rv < 0) { 533 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 534 "Socket closed"); 535 return -1; 536 } 537 538 return 0; 539 }