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) != SOCKET_ERROR) { 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(), "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 == -1) { 309 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 310 "Socket closed"); 311 } 312 if (packetBufferLen > MAX_BUFFER_LEN) { 313 free(fullPacket); 314 } 315 return -1; 316 } 317 } 318 319 /* receive the packet */ 320 rv = recvfrom(fd, fullPacket, packetBufferLen, flags, 321 (struct sockaddr *)&sa, &sa_len); 322 323 if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) { 324 /* An icmp port unreachable - we must receive this as Windows 325 * does not reset the state of the socket until this has been 326 * received. 327 */ 328 purgeOutstandingICMP(env, fd); 329 330 if (connected) { 331 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 332 "ICMP Port Unreachable"); 333 if (packetBufferLen > MAX_BUFFER_LEN) 334 free(fullPacket); 335 return -1; 336 } else if (timeout) { 337 /* Adjust timeout */ 338 jlong newTime = JVM_CurrentTimeMillis(env, 0); 339 timeout -= (jint)(newTime - prevTime); 340 if (timeout <= 0) { 341 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", 342 "Receive timed out"); 343 if (packetBufferLen > MAX_BUFFER_LEN) 344 free(fullPacket); 345 return -1; 346 } 347 prevTime = newTime; 348 } 349 retry = TRUE; 350 } 351 } while (retry); 352 353 port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa)); 354 355 /* truncate the data if the packet's length is too small */ 356 if (rv > packetBufferLen) { 357 rv = packetBufferLen; 358 } 359 if (rv < 0) { 360 if (WSAGetLastError() == WSAEMSGSIZE) { 361 /* it is because the buffer is too small. It's UDP, it's 362 * unreliable, it's all good. discard the rest of the 363 * data.. 364 */ 365 rv = packetBufferLen; 366 } else { 367 /* failure */ 368 (*env)->SetIntField(env, dpObj, dp_lengthID, 0); 369 } 370 } 371 372 if (rv == -1) { 373 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); 374 } else if (rv == -2) { 375 JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", 376 "operation interrupted"); 377 } else if (rv < 0) { 378 NET_ThrowCurrent(env, "Datagram receive failed"); 379 } else { 380 jobject packetAddress; 381 /* 382 * Check if there is an InetAddress already associated with this 383 * packet. If so, we check if it is the same source address. We 384 * can't update any existing InetAddress because it is immutable 385 */ 386 packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID); 387 if (packetAddress != NULL) { 388 if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa, 389 packetAddress)) { 390 /* force a new InetAddress to be created */ 391 packetAddress = NULL; 392 } 393 } 394 if (packetAddress == NULL) { 395 packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, 396 &port); 397 if (packetAddress != NULL) { 398 /* stuff the new Inetaddress into the packet */ 399 (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress); 400 } 401 } 402 403 if (!(*env)->ExceptionCheck(env)) { 404 /* populate the packet */ 405 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, rv, 406 (jbyte *)fullPacket); 407 (*env)->SetIntField(env, dpObj, dp_portID, port); 408 (*env)->SetIntField(env, dpObj, dp_lengthID, rv); 409 } 410 } 411 412 if (packetBufferLen > MAX_BUFFER_LEN) { 413 free(fullPacket); 414 } 415 return port; 416 } 417 418 /* 419 * Class: java_net_DualStackPlainDatagramSocketImpl 420 * Method: socketSend 421 * Signature: (I[BIILjava/net/InetAddress;IZ)V 422 */ 423 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSend 424 (JNIEnv *env, jclass clazz, jint fd, jbyteArray data, jint offset, jint length, 425 jobject iaObj, jint port, jboolean connected) { 426 SOCKETADDRESS sa; 427 int sa_len = sizeof(sa); 428 SOCKETADDRESS *sap = &sa; 429 char BUF[MAX_BUFFER_LEN]; 430 char *fullPacket; 431 int rv; 432 433 if (connected) { 434 sap = 0; /* arg to sendto () null in this case */ 435 sa_len = 0; 436 } else { 437 if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa, 438 &sa_len, JNI_TRUE) != 0) { 439 return; 440 } 441 } 442 443 if (length > MAX_BUFFER_LEN) { 444 /* Note: the buffer needn't be greater than 65,536 (0xFFFF) 445 * the max size of an IP packet. Anything bigger is truncated anyway. 446 */ 447 if (length > MAX_PACKET_LEN) { 448 length = MAX_PACKET_LEN; 449 } 450 fullPacket = (char *)malloc(length); 451 if (!fullPacket) { 452 JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); 453 return; 454 } 455 } else { 456 fullPacket = &(BUF[0]); 457 } 458 459 (*env)->GetByteArrayRegion(env, data, offset, length, 460 (jbyte *)fullPacket); 461 rv = sendto(fd, fullPacket, length, 0, (struct sockaddr *)sap, sa_len); 462 if (rv == SOCKET_ERROR) { 463 if (rv == -1) { 464 NET_ThrowNew(env, WSAGetLastError(), "Datagram send failed"); 465 } 466 } 467 468 if (length > MAX_BUFFER_LEN) { 469 free(fullPacket); 470 } 471 } 472 473 /* 474 * Class: java_net_DualStackPlainDatagramSocketImpl 475 * Method: socketSetIntOption 476 * Signature: (III)V 477 */ 478 JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption 479 (JNIEnv *env, jclass clazz, jint fd , jint cmd, jint value) { 480 int level = 0, opt = 0; 481 482 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 483 JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 484 "Invalid option"); 485 return; 486 } 487 488 if (NET_SetSockOpt(fd, level, opt, (char *)&value, sizeof(value)) < 0) { 489 NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); 490 } 491 } 492 493 /* 494 * Class: java_net_DualStackPlainDatagramSocketImpl 495 * Method: socketGetIntOption 496 * Signature: (II)I 497 */ 498 JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption 499 (JNIEnv *env, jclass clazz, jint fd, jint cmd) { 500 int level = 0, opt = 0, result=0; 501 int result_len = sizeof(result); 502 503 if (NET_MapSocketOption(cmd, &level, &opt) < 0) { 504 JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 505 "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 Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable 523 (JNIEnv *env, jobject this) { 524 SOCKET fd; 525 int rv = -1; 526 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 527 528 if (!IS_NULL(fdObj)) { 529 int retval = 0; 530 fd = (SOCKET)(*env)->GetIntField(env, fdObj, IO_fd_fdID); 531 rv = ioctlsocket(fd, FIONREAD, &retval); 532 if (retval > 0) { 533 return retval; 534 } 535 } 536 537 if (rv < 0) { 538 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 539 "Socket closed"); 540 return -1; 541 } 542 543 return 0; 544 }