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