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