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