1/*
2 * Copyright (c) 2007, 2016, 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 */
36static 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
71static jfieldID IO_fd_fdID = NULL;
72static jfieldID pdsi_fdID = NULL;
73
74/*
75 * Class:     java_net_DualStackPlainDatagramSocketImpl
76 * Method:    initIDs
77 * Signature: ()V
78 */
79JNIEXPORT 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: (Z)I
96 */
97JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketCreate
98  (JNIEnv *env, jclass clazz, jboolean v6Only /*unused*/) {
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 */
134JNIEXPORT 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 */
158JNIEXPORT 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 = TRUE;
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    WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
177}
178
179/*
180 * Class:     java_net_DualStackPlainDatagramSocketImpl
181 * Method:    socketDisconnect
182 * Signature: (I)V
183 */
184JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketDisconnect
185  (JNIEnv *env, jclass clazz, jint fd ) {
186    SOCKETADDRESS sa;
187    int sa_len = sizeof(sa);
188    DWORD x1, x2; /* ignored result codes */
189    int t = FALSE;
190
191    memset(&sa, 0, sa_len);
192    connect(fd, &sa.sa, sa_len);
193
194    /* see comment in socketCreate */
195    WSAIoctl(fd, SIO_UDP_CONNRESET, &t, sizeof(t), &x1, sizeof(x1), &x2, 0, 0);
196}
197
198/*
199 * Class:     java_net_DualStackPlainDatagramSocketImpl
200 * Method:    socketClose
201 * Signature: (I)V
202 */
203JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketClose
204  (JNIEnv *env, jclass clazz , jint fd) {
205    NET_SocketClose(fd);
206}
207
208
209/*
210 * Class:     java_net_DualStackPlainDatagramSocketImpl
211 * Method:    socketLocalPort
212 * Signature: (I)I
213 */
214JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalPort
215  (JNIEnv *env, jclass clazz, jint fd) {
216    SOCKETADDRESS sa;
217    int len = sizeof(sa);
218
219    if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
220        NET_ThrowNew(env, WSAGetLastError(), "getsockname");
221        return -1;
222    }
223    return (int) ntohs((u_short)GET_PORT(&sa));
224}
225
226/*
227 * Class:     java_net_DualStackPlainDatagramSocketImpl
228 * Method:    socketLocalAddress
229 * Signature: (I)Ljava/lang/Object;
230 */
231JNIEXPORT jobject JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketLocalAddress
232  (JNIEnv *env , jclass clazz, jint fd) {
233    SOCKETADDRESS sa;
234    int len = sizeof(sa);
235    jobject iaObj;
236    int port;
237
238    if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
239        NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
240        return NULL;
241    }
242
243    iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
244    return iaObj;
245}
246
247/*
248 * Class:     java_net_DualStackPlainDatagramSocketImpl
249 * Method:    socketReceiveOrPeekData
250 * Signature: (ILjava/net/DatagramPacket;IZZ)I
251 */
252JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketReceiveOrPeekData
253  (JNIEnv *env, jclass clazz, jint fd, jobject dpObj,
254   jint timeout, jboolean connected, jboolean peek) {
255    SOCKETADDRESS sa;
256    int sa_len = sizeof(sa);
257    int port, rv, flags=0;
258    char BUF[MAX_BUFFER_LEN];
259    char *fullPacket;
260    BOOL retry;
261    jlong prevTime = 0;
262
263    jint packetBufferOffset, packetBufferLen;
264    jbyteArray packetBuffer;
265
266    /* if we are only peeking. Called from peekData */
267    if (peek) {
268        flags = MSG_PEEK;
269    }
270
271    packetBuffer = (*env)->GetObjectField(env, dpObj, dp_bufID);
272    packetBufferOffset = (*env)->GetIntField(env, dpObj, dp_offsetID);
273    packetBufferLen = (*env)->GetIntField(env, dpObj, dp_bufLengthID);
274    /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
275    * the max size of an IP packet. Anything bigger is truncated anyway.
276    */
277    if (packetBufferLen > MAX_PACKET_LEN) {
278        packetBufferLen = MAX_PACKET_LEN;
279    }
280
281    if (packetBufferLen > MAX_BUFFER_LEN) {
282        fullPacket = (char *)malloc(packetBufferLen);
283        if (!fullPacket) {
284            JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
285            return -1;
286        }
287    } else {
288        fullPacket = &(BUF[0]);
289    }
290
291    do {
292        retry = FALSE;
293
294        if (timeout) {
295            if (prevTime == 0) {
296                prevTime = JVM_CurrentTimeMillis(env, 0);
297            }
298            rv = NET_Timeout(fd, timeout);
299            if (rv <= 0) {
300                if (rv == 0) {
301                    JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException",
302                                    "Receive timed out");
303                } else if (rv == -1) {
304                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
305                                    "Socket closed");
306                }
307                if (packetBufferLen > MAX_BUFFER_LEN) {
308                    free(fullPacket);
309                }
310                return -1;
311            }
312        }
313
314        /* receive the packet */
315        rv = recvfrom(fd, fullPacket, packetBufferLen, flags,
316                      &sa.sa, &sa_len);
317
318        if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) {
319            /* An icmp port unreachable - we must receive this as Windows
320             * does not reset the state of the socket until this has been
321             * received.
322             */
323            purgeOutstandingICMP(env, fd);
324
325            if (connected) {
326                JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
327                                "ICMP Port Unreachable");
328                if (packetBufferLen > MAX_BUFFER_LEN)
329                    free(fullPacket);
330                return -1;
331            } else if (timeout) {
332                /* Adjust timeout */
333                jlong newTime = JVM_CurrentTimeMillis(env, 0);
334                timeout -= (jint)(newTime - prevTime);
335                if (timeout <= 0) {
336                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
337                                    "Receive timed out");
338                    if (packetBufferLen > MAX_BUFFER_LEN)
339                        free(fullPacket);
340                    return -1;
341                }
342                prevTime = newTime;
343            }
344            retry = TRUE;
345        }
346    } while (retry);
347
348    port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa));
349
350    /* truncate the data if the packet's length is too small */
351    if (rv > packetBufferLen) {
352        rv = packetBufferLen;
353    }
354    if (rv < 0) {
355        if (WSAGetLastError() == WSAEMSGSIZE) {
356            /* it is because the buffer is too small. It's UDP, it's
357             * unreliable, it's all good. discard the rest of the
358             * data..
359             */
360            rv = packetBufferLen;
361        } else {
362            /* failure */
363            (*env)->SetIntField(env, dpObj, dp_lengthID, 0);
364        }
365    }
366
367    if (rv == -1) {
368        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
369    } else if (rv == -2) {
370        JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
371                        "operation interrupted");
372    } else if (rv < 0) {
373        NET_ThrowCurrent(env, "Datagram receive failed");
374    } else {
375        jobject packetAddress;
376        /*
377         * Check if there is an InetAddress already associated with this
378         * packet. If so, we check if it is the same source address. We
379         * can't update any existing InetAddress because it is immutable
380         */
381        packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID);
382        if (packetAddress != NULL) {
383            if (!NET_SockaddrEqualsInetAddress(env, &sa, packetAddress)) {
384                /* force a new InetAddress to be created */
385                packetAddress = NULL;
386            }
387        }
388        if (packetAddress == NULL) {
389            packetAddress = NET_SockaddrToInetAddress(env, &sa, &port);
390            if (packetAddress != NULL) {
391                /* stuff the new Inetaddress into the packet */
392                (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress);
393            }
394        }
395
396        if (!(*env)->ExceptionCheck(env)) {
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 */
416JNIEXPORT 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 */
469JNIEXPORT void JNICALL
470Java_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 */
490JNIEXPORT jint JNICALL
491Java_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 */
515JNIEXPORT jint JNICALL
516Java_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