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