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 */
purgeOutstandingICMP(JNIEnv * env,jint fd)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_initIDs(JNIEnv * env,jclass clazz)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketCreate(JNIEnv * env,jclass clazz,jboolean v6Only)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketBind(JNIEnv * env,jclass clazz,jint fd,jobject iaObj,jint port,jboolean exclBind)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketConnect(JNIEnv * env,jclass clazz,jint fd,jobject iaObj,jint port)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketDisconnect(JNIEnv * env,jclass clazz,jint fd)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketClose(JNIEnv * env,jclass clazz,jint fd)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalPort(JNIEnv * env,jclass clazz,jint fd)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalAddress(JNIEnv * env,jclass clazz,jint fd)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketReceiveOrPeekData(JNIEnv * env,jclass clazz,jint fd,jobject dpObj,jint timeout,jboolean connected,jboolean peek)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketSend(JNIEnv * env,jclass clazz,jint fd,jbyteArray data,jint offset,jint length,jobject iaObj,jint port,jboolean connected)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption(JNIEnv * env,jclass clazz,jint fd,jint cmd,jint value)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption(JNIEnv * env,jclass clazz,jint fd,jint cmd)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 */
Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable(JNIEnv * env,jobject this)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 }
551