diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/java.base/unix/native/libnio/ch/DatagramChannelImpl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/unix/native/libnio/ch/DatagramChannelImpl.c Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) || defined(_ALLBSD_SOURCE) +#include +#endif + +#include "net_util.h" +#include "net_util_md.h" +#include "nio.h" +#include "nio_util.h" + +#include "sun_nio_ch_DatagramChannelImpl.h" + +static jfieldID dci_senderID; /* sender in sun.nio.ch.DatagramChannelImpl */ +static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */ +static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */ +static jclass isa_class; /* java.net.InetSocketAddress */ +static jmethodID isa_ctorID; /* .InetSocketAddress(InetAddress, int) */ + +JNIEXPORT void JNICALL +Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz) +{ + clazz = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL(clazz); + isa_class = (*env)->NewGlobalRef(env, clazz); + if (isa_class == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + return; + } + isa_ctorID = (*env)->GetMethodID(env, clazz, "", + "(Ljava/net/InetAddress;I)V"); + CHECK_NULL(isa_ctorID); + + clazz = (*env)->FindClass(env, "sun/nio/ch/DatagramChannelImpl"); + CHECK_NULL(clazz); + dci_senderID = (*env)->GetFieldID(env, clazz, "sender", + "Ljava/net/SocketAddress;"); + CHECK_NULL(dci_senderID); + dci_senderAddrID = (*env)->GetFieldID(env, clazz, + "cachedSenderInetAddress", + "Ljava/net/InetAddress;"); + CHECK_NULL(dci_senderAddrID); + dci_senderPortID = (*env)->GetFieldID(env, clazz, + "cachedSenderPort", "I"); + CHECK_NULL(dci_senderPortID); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jobject this, + jobject fdo, jboolean isIPv6) +{ + jint fd = fdval(env, fdo); + int rv; + +#if defined(__solaris__) + rv = connect(fd, 0, 0); +#else + SOCKETADDRESS sa; + socklen_t len = isIPv6 ? sizeof(struct sockaddr_in6) : + sizeof(struct sockaddr_in); + + memset(&sa, 0, sizeof(sa)); +#if defined(_ALLBSD_SOURCE) + sa.sa.sa_family = isIPv6 ? AF_INET6 : AF_INET; +#else + sa.sa.sa_family = AF_UNSPEC; +#endif + + rv = connect(fd, &sa.sa, len); + +#if defined(_ALLBSD_SOURCE) + if (rv < 0 && errno == EADDRNOTAVAIL) + rv = errno = 0; +#elif defined(_AIX) + /* See W. Richard Stevens, "UNIX Network Programming, Volume 1", p. 254: + * 'Setting the address family to AF_UNSPEC might return EAFNOSUPPORT + * but that is acceptable. + */ + if (rv < 0 && errno == EAFNOSUPPORT) + rv = errno = 0; +#endif // defined(_ALLBSD_SOURCE) || defined(_AIX) + +#endif // defined(__solaris__) + + if (rv < 0) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, + jobject fdo, jlong address, + jint len, jboolean connected) +{ + jint fd = fdval(env, fdo); + void *buf = (void *)jlong_to_ptr(address); + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + jboolean retry = JNI_FALSE; + jint n = 0; + jobject senderAddr; + + if (len > MAX_PACKET_LEN) { + len = MAX_PACKET_LEN; + } + + do { + retry = JNI_FALSE; + n = recvfrom(fd, buf, len, 0, &sa.sa, &sa_len); + if (n < 0) { + if (errno == EWOULDBLOCK) { + return IOS_UNAVAILABLE; + } + if (errno == EINTR) { + return IOS_INTERRUPTED; + } + if (errno == ECONNREFUSED) { + if (connected == JNI_FALSE) { + retry = JNI_TRUE; + } else { + JNU_ThrowByName(env, JNU_JAVANETPKG + "PortUnreachableException", 0); + return IOS_THROWN; + } + } else { + return handleSocketError(env, errno); + } + } + } while (retry == JNI_TRUE); + + /* + * If the source address and port match the cached address + * and port in DatagramChannelImpl then we don't need to + * create InetAddress and InetSocketAddress objects. + */ + senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); + if (senderAddr != NULL) { + if (!NET_SockaddrEqualsInetAddress(env, &sa, senderAddr)) { + senderAddr = NULL; + } else { + jint port = (*env)->GetIntField(env, this, dci_senderPortID); + if (port != NET_GetPortFromSockaddr(&sa)) { + senderAddr = NULL; + } + } + } + if (senderAddr == NULL) { + jobject isa = NULL; + int port = 0; + jobject ia = NET_SockaddrToInetAddress(env, &sa, &port); + if (ia != NULL) { + isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); + } + CHECK_NULL_RETURN(isa, IOS_THROWN); + + (*env)->SetObjectField(env, this, dci_senderAddrID, ia); + (*env)->SetIntField(env, this, dci_senderPortID, + NET_GetPortFromSockaddr(&sa)); + (*env)->SetObjectField(env, this, dci_senderID, isa); + } + return n; +} + +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) +{ + jint fd = fdval(env, fdo); + void *buf = (void *)jlong_to_ptr(address); + SOCKETADDRESS sa; + int sa_len = 0; + jint n = 0; + + if (len > MAX_PACKET_LEN) { + len = MAX_PACKET_LEN; + } + + if (NET_InetAddressToSockaddr(env, destAddress, destPort, &sa, + &sa_len, preferIPv6) != 0) { + return IOS_THROWN; + } + + n = sendto(fd, buf, len, 0, &sa.sa, sa_len); + if (n < 0) { + if (errno == EAGAIN) { + return IOS_UNAVAILABLE; + } + if (errno == EINTR) { + return IOS_INTERRUPTED; + } + if (errno == ECONNREFUSED) { + JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0); + return IOS_THROWN; + } + return handleSocketError(env, errno); + } + return n; +}