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