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