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