xref: /aosp_15_r20/libcore/ojluni/src/main/native/DatagramChannelImpl.c (revision 89a6322812dc8573315e60046e7959c50dad91d4)
1 /*
2  * Copyright (c) 2001, 2012, 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 
48 #include <nativehelper/JNIHelp.h>
49 
50 #define NATIVE_METHOD(className, functionName, signature) \
51 { #functionName, signature, (void*)(Java_sun_nio_ch_ ## className ## _ ## functionName) }
52 
53 
54 static jfieldID dci_senderID;   /* sender in sun.nio.ch.DatagramChannelImpl */
55 static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */
56 static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */
57 static jclass isa_class;        /* java.net.InetSocketAddress */
58 static jmethodID isa_ctorID;    /*   .InetSocketAddress(InetAddress, int) */
59 
60 JNIEXPORT void JNICALL
Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv * env,jclass clazz)61 Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz)
62 {
63     clazz = (*env)->FindClass(env, "java/net/InetSocketAddress");
64     CHECK_NULL(clazz);
65     isa_class = (*env)->NewGlobalRef(env, clazz);
66     if (isa_class == NULL) {
67         JNU_ThrowOutOfMemoryError(env, NULL);
68         return;
69     }
70     isa_ctorID = (*env)->GetMethodID(env, clazz, "<init>",
71                                      "(Ljava/net/InetAddress;I)V");
72     CHECK_NULL(isa_ctorID);
73 
74     clazz = (*env)->FindClass(env, "sun/nio/ch/DatagramChannelImpl");
75     CHECK_NULL(clazz);
76     dci_senderID = (*env)->GetFieldID(env, clazz, "sender",
77                                       "Ljava/net/SocketAddress;");
78     CHECK_NULL(dci_senderID);
79     dci_senderAddrID = (*env)->GetFieldID(env, clazz,
80                                           "cachedSenderInetAddress",
81                                           "Ljava/net/InetAddress;");
82     CHECK_NULL(dci_senderAddrID);
83     dci_senderPortID = (*env)->GetFieldID(env, clazz,
84                                           "cachedSenderPort", "I");
85     CHECK_NULL(dci_senderPortID);
86 }
87 
88 JNIEXPORT void JNICALL
Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv * env,jobject this,jobject fdo,jboolean isIPv6)89 Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jobject this,
90                                                 jobject fdo, jboolean isIPv6)
91 {
92     jint fd = fdval(env, fdo);
93     int rv;
94 
95 #ifdef __solaris__
96     rv = connect(fd, 0, 0);
97 #endif
98 
99 // Android-changed: Fuchsia: Pass linux-style args to connect.
100 // #if defined(__linux__) || defined(_ALLBSD_SOURCE) || defined(_AIX)
101 #if defined(__linux__) || defined(_ALLBSD_SOURCE) || defined(_AIX) || defined(__Fuchsia__)
102     {
103         int len;
104         SOCKADDR sa;
105 
106         memset(&sa, 0, sizeof(sa));
107 
108 #ifdef AF_INET6
109         if (isIPv6) {
110             struct sockaddr_in6 *him6 = (struct sockaddr_in6 *)&sa;
111 #if defined(_ALLBSD_SOURCE)
112             him6->sin6_family = AF_INET6;
113 #else
114             him6->sin6_family = AF_UNSPEC;
115 #endif
116             len = sizeof(struct sockaddr_in6);
117         } else
118 #endif
119         {
120             struct sockaddr_in *him4 = (struct sockaddr_in*)&sa;
121 #if defined(_ALLBSD_SOURCE)
122             him4->sin_family = AF_INET;
123 #else
124             him4->sin_family = AF_UNSPEC;
125 #endif
126             len = sizeof(struct sockaddr_in);
127         }
128 
129         rv = connect(fd, (struct sockaddr *)&sa, len);
130 
131 #if defined(_ALLBSD_SOURCE)
132         if (rv < 0 && errno == EADDRNOTAVAIL)
133                 rv = errno = 0;
134 #endif
135 #if defined(_AIX)
136         /* See W. Richard Stevens, "UNIX Network Programming, Volume 1", p. 254:
137          * 'Setting the address family to AF_UNSPEC might return EAFNOSUPPORT
138          * but that is acceptable.
139          */
140         if (rv < 0 && errno == EAFNOSUPPORT)
141             rv = errno = 0;
142 #endif
143     }
144 #endif
145 
146     if (rv < 0)
147         handleSocketError(env, errno);
148 
149 }
150 
151 JNIEXPORT jint JNICALL
Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv * env,jobject this,jobject fdo,jlong address,jint len,jboolean connected)152 Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this,
153                                              jobject fdo, jlong address,
154                                              jint len, jboolean connected)
155 {
156     jint fd = fdval(env, fdo);
157     void *buf = (void *)jlong_to_ptr(address);
158     SOCKADDR sa;
159     socklen_t sa_len = SOCKADDR_LEN;
160     jboolean retry = JNI_FALSE;
161     jint n = 0;
162     jobject senderAddr;
163 
164     if (len > MAX_PACKET_LEN) {
165         len = MAX_PACKET_LEN;
166     }
167 
168     memset(&sa, 0, sa_len);
169 
170     do {
171         retry = JNI_FALSE;
172         n = recvfrom(fd, buf, len, 0, (struct sockaddr *)&sa, &sa_len);
173         if (n < 0) {
174             if (errno == EWOULDBLOCK) {
175                 return IOS_UNAVAILABLE;
176             }
177             if (errno == EINTR) {
178                 return IOS_INTERRUPTED;
179             }
180             if (errno == ECONNREFUSED) {
181                 if (connected == JNI_FALSE) {
182                     retry = JNI_TRUE;
183                 } else {
184                     JNU_ThrowByName(env, JNU_JAVANETPKG
185                                     "PortUnreachableException", 0);
186                     return IOS_THROWN;
187                 }
188             } else {
189                 return handleSocketError(env, errno);
190             }
191         }
192     } while (retry == JNI_TRUE);
193 
194     // Peer (or other thread) has performed an orderly shutdown, sockaddr will be
195     // invalid.
196     if (n == 0 && ((struct sockaddr *)&sa)->sa_family == 0) {
197         // zero the sender field, so receive() returns null and not
198         // random garbage
199         (*env)->SetObjectField(env, this, dci_senderID, NULL);
200         return n;
201     }
202 
203     /*
204      * If the source address and port match the cached address
205      * and port in DatagramChannelImpl then we don't need to
206      * create InetAddress and InetSocketAddress objects.
207      */
208     senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID);
209     if (senderAddr != NULL) {
210         if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa,
211                                            senderAddr)) {
212             senderAddr = NULL;
213         } else {
214             jint port = (*env)->GetIntField(env, this, dci_senderPortID);
215             if (port != NET_GetPortFromSockaddr((struct sockaddr *)&sa)) {
216                 senderAddr = NULL;
217             }
218         }
219     }
220     if (senderAddr == NULL) {
221         jobject isa = NULL;
222         int port;
223         jobject ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
224         if ((*env)->ExceptionCheck(env)) {
225             return IOS_THROWN;
226         }
227 
228         if (ia != NULL) {
229             isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
230         }
231         CHECK_NULL_RETURN(isa, IOS_THROWN);
232 
233         (*env)->SetObjectField(env, this, dci_senderAddrID, ia);
234         (*env)->SetIntField(env, this, dci_senderPortID,
235                             NET_GetPortFromSockaddr((struct sockaddr *)&sa));
236         (*env)->SetObjectField(env, this, dci_senderID, isa);
237     }
238     return n;
239 }
240 
241 JNIEXPORT jint JNICALL
Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv * env,jobject this,jboolean preferIPv6,jobject fdo,jlong address,jint len,jobject destAddress,jint destPort)242 Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this,
243                                           jboolean preferIPv6, jobject fdo, jlong address,
244                                           jint len, jobject destAddress, jint destPort)
245 {
246     jint fd = fdval(env, fdo);
247     void *buf = (void *)jlong_to_ptr(address);
248     SOCKADDR sa;
249     int sa_len = SOCKADDR_LEN;
250     jint n = 0;
251 
252     if (len > MAX_PACKET_LEN) {
253         len = MAX_PACKET_LEN;
254     }
255 
256     if (NET_InetAddressToSockaddr(env, destAddress, destPort,
257                                   (struct sockaddr *)&sa,
258                                   &sa_len, preferIPv6) != 0) {
259       return IOS_THROWN;
260     }
261 
262     n = sendto(fd, buf, len, 0, (struct sockaddr *)&sa, sa_len);
263     if (n < 0) {
264         if (errno == EAGAIN) {
265             return IOS_UNAVAILABLE;
266         }
267         if (errno == EINTR) {
268             return IOS_INTERRUPTED;
269         }
270         if (errno == ECONNREFUSED) {
271             JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
272             return IOS_THROWN;
273         }
274         return handleSocketError(env, errno);
275     }
276     return n;
277 }
278 
279 static JNINativeMethod gMethods[] = {
280   NATIVE_METHOD(DatagramChannelImpl, initIDs, "()V"),
281   NATIVE_METHOD(DatagramChannelImpl, disconnect0, "(Ljava/io/FileDescriptor;Z)V"),
282   NATIVE_METHOD(DatagramChannelImpl, receive0, "(Ljava/io/FileDescriptor;JIZ)I"),
283   NATIVE_METHOD(DatagramChannelImpl, send0, "(ZLjava/io/FileDescriptor;JILjava/net/InetAddress;I)I"),
284 };
285 
register_sun_nio_ch_DatagramChannelImpl(JNIEnv * env)286 void register_sun_nio_ch_DatagramChannelImpl(JNIEnv* env) {
287   jniRegisterNativeMethods(env, "sun/nio/ch/DatagramChannelImpl", gMethods, NELEM(gMethods));
288 }
289