1/*
2 * Copyright (c) 2001, 2017, 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
26#include "jni.h"
27#include "jni_util.h"
28#include "jvm.h"
29#include "jlong.h"
30
31#include <netdb.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37
38#if defined(__linux__) || defined(_ALLBSD_SOURCE)
39#include <netinet/in.h>
40#endif
41
42#include "net_util.h"
43#include "net_util_md.h"
44#include "nio.h"
45#include "nio_util.h"
46
47#include "sun_nio_ch_DatagramChannelImpl.h"
48
49static jfieldID dci_senderID;   /* sender in sun.nio.ch.DatagramChannelImpl */
50static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */
51static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */
52static jclass isa_class;        /* java.net.InetSocketAddress */
53static jmethodID isa_ctorID;    /*   .InetSocketAddress(InetAddress, int) */
54
55JNIEXPORT void JNICALL
56Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz)
57{
58    clazz = (*env)->FindClass(env, "java/net/InetSocketAddress");
59    CHECK_NULL(clazz);
60    isa_class = (*env)->NewGlobalRef(env, clazz);
61    if (isa_class == NULL) {
62        JNU_ThrowOutOfMemoryError(env, NULL);
63        return;
64    }
65    isa_ctorID = (*env)->GetMethodID(env, clazz, "<init>",
66                                     "(Ljava/net/InetAddress;I)V");
67    CHECK_NULL(isa_ctorID);
68
69    clazz = (*env)->FindClass(env, "sun/nio/ch/DatagramChannelImpl");
70    CHECK_NULL(clazz);
71    dci_senderID = (*env)->GetFieldID(env, clazz, "sender",
72                                      "Ljava/net/SocketAddress;");
73    CHECK_NULL(dci_senderID);
74    dci_senderAddrID = (*env)->GetFieldID(env, clazz,
75                                          "cachedSenderInetAddress",
76                                          "Ljava/net/InetAddress;");
77    CHECK_NULL(dci_senderAddrID);
78    dci_senderPortID = (*env)->GetFieldID(env, clazz,
79                                          "cachedSenderPort", "I");
80    CHECK_NULL(dci_senderPortID);
81}
82
83JNIEXPORT void JNICALL
84Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jobject this,
85                                                jobject fdo, jboolean isIPv6)
86{
87    jint fd = fdval(env, fdo);
88    int rv;
89
90#if defined(__solaris__)
91    rv = connect(fd, 0, 0);
92#else
93    SOCKETADDRESS sa;
94    socklen_t len = isIPv6 ? sizeof(struct sockaddr_in6) :
95                             sizeof(struct sockaddr_in);
96
97    memset(&sa, 0, sizeof(sa));
98#if defined(_ALLBSD_SOURCE)
99    sa.sa.sa_family = isIPv6 ? AF_INET6 : AF_INET;
100#else
101    sa.sa.sa_family = AF_UNSPEC;
102#endif
103
104    rv = connect(fd, &sa.sa, len);
105
106#if defined(_ALLBSD_SOURCE)
107    if (rv < 0 && errno == EADDRNOTAVAIL)
108        rv = errno = 0;
109#elif defined(_AIX)
110    /* See W. Richard Stevens, "UNIX Network Programming, Volume 1", p. 254:
111     * 'Setting the address family to AF_UNSPEC might return EAFNOSUPPORT
112     * but that is acceptable.
113     */
114    if (rv < 0 && errno == EAFNOSUPPORT)
115        rv = errno = 0;
116#endif // defined(_ALLBSD_SOURCE) || defined(_AIX)
117
118#endif // defined(__solaris__)
119
120    if (rv < 0)
121        handleSocketError(env, errno);
122}
123
124JNIEXPORT jint JNICALL
125Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this,
126                                             jobject fdo, jlong address,
127                                             jint len, jboolean connected)
128{
129    jint fd = fdval(env, fdo);
130    void *buf = (void *)jlong_to_ptr(address);
131    SOCKETADDRESS sa;
132    socklen_t sa_len = sizeof(SOCKETADDRESS);
133    jboolean retry = JNI_FALSE;
134    jint n = 0;
135    jobject senderAddr;
136
137    if (len > MAX_PACKET_LEN) {
138        len = MAX_PACKET_LEN;
139    }
140
141    do {
142        retry = JNI_FALSE;
143        n = recvfrom(fd, buf, len, 0, &sa.sa, &sa_len);
144        if (n < 0) {
145            if (errno == EWOULDBLOCK) {
146                return IOS_UNAVAILABLE;
147            }
148            if (errno == EINTR) {
149                return IOS_INTERRUPTED;
150            }
151            if (errno == ECONNREFUSED) {
152                if (connected == JNI_FALSE) {
153                    retry = JNI_TRUE;
154                } else {
155                    JNU_ThrowByName(env, JNU_JAVANETPKG
156                                    "PortUnreachableException", 0);
157                    return IOS_THROWN;
158                }
159            } else {
160                return handleSocketError(env, errno);
161            }
162        }
163    } while (retry == JNI_TRUE);
164
165    /*
166     * If the source address and port match the cached address
167     * and port in DatagramChannelImpl then we don't need to
168     * create InetAddress and InetSocketAddress objects.
169     */
170    senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID);
171    if (senderAddr != NULL) {
172        if (!NET_SockaddrEqualsInetAddress(env, &sa, senderAddr)) {
173            senderAddr = NULL;
174        } else {
175            jint port = (*env)->GetIntField(env, this, dci_senderPortID);
176            if (port != NET_GetPortFromSockaddr(&sa)) {
177                senderAddr = NULL;
178            }
179        }
180    }
181    if (senderAddr == NULL) {
182        jobject isa = NULL;
183        int port = 0;
184        jobject ia = NET_SockaddrToInetAddress(env, &sa, &port);
185        if (ia != NULL) {
186            isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
187        }
188        CHECK_NULL_RETURN(isa, IOS_THROWN);
189
190        (*env)->SetObjectField(env, this, dci_senderAddrID, ia);
191        (*env)->SetIntField(env, this, dci_senderPortID,
192                            NET_GetPortFromSockaddr(&sa));
193        (*env)->SetObjectField(env, this, dci_senderID, isa);
194    }
195    return n;
196}
197
198JNIEXPORT jint JNICALL
199Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this,
200                                          jboolean preferIPv6, jobject fdo, jlong address,
201                                          jint len, jobject destAddress, jint destPort)
202{
203    jint fd = fdval(env, fdo);
204    void *buf = (void *)jlong_to_ptr(address);
205    SOCKETADDRESS sa;
206    int sa_len = 0;
207    jint n = 0;
208
209    if (len > MAX_PACKET_LEN) {
210        len = MAX_PACKET_LEN;
211    }
212
213    if (NET_InetAddressToSockaddr(env, destAddress, destPort, &sa,
214                                  &sa_len, preferIPv6) != 0) {
215      return IOS_THROWN;
216    }
217
218    n = sendto(fd, buf, len, 0, &sa.sa, sa_len);
219    if (n < 0) {
220        if (errno == EAGAIN) {
221            return IOS_UNAVAILABLE;
222        }
223        if (errno == EINTR) {
224            return IOS_INTERRUPTED;
225        }
226        if (errno == ECONNREFUSED) {
227            JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
228            return IOS_THROWN;
229        }
230        return handleSocketError(env, errno);
231    }
232    return n;
233}
234