Home | History | Annotate | Download | only in native
      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
     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
     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 #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 
    149 JNIEXPORT jint JNICALL
    150 Java_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 
    239 JNIEXPORT jint JNICALL
    240 Java_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 
    277 static 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 
    284 void register_sun_nio_ch_DatagramChannelImpl(JNIEnv* env) {
    285   jniRegisterNativeMethods(env, "sun/nio/ch/DatagramChannelImpl", gMethods, NELEM(gMethods));
    286 }
    287