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