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