/* * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ #include #include #include #include "Sctp.h" #include "jni.h" #include "jni_util.h" #include "nio_util.h" #include "nio.h" #include "net_util.h" #include "net_util_md.h" #include "sun_nio_ch_SctpNet.h" #include "sun_nio_ch_SctpStdSocketOption.h" static jclass isaCls = 0; static jmethodID isaCtrID = 0; static const char* nativeSctpLib = "libsctp.so.1"; static jboolean funcsLoaded = JNI_FALSE; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM *vm, void *reserved) { return JNI_VERSION_1_2; } static int preCloseFD = -1; /* File descriptor to which we dup other fd's before closing them for real */ /** * Loads the native sctp library that contains the socket extension * functions, as well as locating the individual functions. * There will be a pending exception if this method returns false. */ jboolean loadSocketExtensionFuncs (JNIEnv* env) { if (dlopen(nativeSctpLib, RTLD_GLOBAL | RTLD_LAZY) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } if ((nio_sctp_getladdrs = (sctp_getladdrs_func*) dlsym(RTLD_DEFAULT, "sctp_getladdrs")) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } if ((nio_sctp_freeladdrs = (sctp_freeladdrs_func*) dlsym(RTLD_DEFAULT, "sctp_freeladdrs")) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } if ((nio_sctp_getpaddrs = (sctp_getpaddrs_func*) dlsym(RTLD_DEFAULT, "sctp_getpaddrs")) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } if ((nio_sctp_freepaddrs = (sctp_freepaddrs_func*) dlsym(RTLD_DEFAULT, "sctp_freepaddrs")) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } if ((nio_sctp_bindx = (sctp_bindx_func*) dlsym(RTLD_DEFAULT, "sctp_bindx")) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } if ((nio_sctp_peeloff = (sctp_peeloff_func*) dlsym(RTLD_DEFAULT, "sctp_peeloff")) == NULL) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", dlerror()); return JNI_FALSE; } funcsLoaded = JNI_TRUE; return JNI_TRUE; } jint handleSocketError(JNIEnv *env, jint errorValue) { char *xn; switch (errorValue) { case EINPROGRESS: /* Non-blocking connect */ return 0; case EPROTO: xn= JNU_JAVANETPKG "ProtocolException"; break; case ECONNREFUSED: xn = JNU_JAVANETPKG "ConnectException"; break; case ETIMEDOUT: xn = JNU_JAVANETPKG "ConnectException"; break; case EHOSTUNREACH: xn = JNU_JAVANETPKG "NoRouteToHostException"; break; case EADDRINUSE: /* Fall through */ case EADDRNOTAVAIL: xn = JNU_JAVANETPKG "BindException"; break; default: xn = JNU_JAVANETPKG "SocketException"; break; } errno = errorValue; JNU_ThrowByNameWithLastError(env, xn, "NioSocketError"); return IOS_THROWN; } /* * Class: sun_nio_ch_SctpNet * Method: init * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_init (JNIEnv *env, jclass cl) { int sp[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) { JNU_ThrowIOExceptionWithLastError(env, "socketpair failed"); return; } preCloseFD = sp[0]; close(sp[1]); } /* * Class: sun_nio_ch_SctpNet * Method: socket0 * Signature: (Z)I */ JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpNet_socket0 (JNIEnv *env, jclass klass, jboolean oneToOne) { int fd; struct sctp_event_subscribe event; #ifdef AF_INET6 int domain = ipv6_available() ? AF_INET6 : AF_INET; #else int domain = AF_INET; #endif /* Try to load the socket API extension functions */ if (!funcsLoaded && !loadSocketExtensionFuncs(env)) { return 0; } fd = socket(domain, (oneToOne ? SOCK_STREAM : SOCK_SEQPACKET), IPPROTO_SCTP); if (fd < 0) { return handleSocketError(env, errno); } /* Enable events */ memset(&event, 0, sizeof(event)); event.sctp_data_io_event = 1; event.sctp_association_event = 1; event.sctp_address_event = 1; event.sctp_send_failure_event = 1; //event.sctp_peer_error_event = 1; event.sctp_shutdown_event = 1; //event.sctp_partial_delivery_event = 1; //event.sctp_adaptation_layer_event = 1; if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) { handleSocketError(env, errno); } return fd; } /* * Class: sun_nio_ch_SctpNet * Method: bindx * Signature: (I[Ljava/net/InetAddress;IIZ)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_bindx (JNIEnv *env, jclass klass, jint fd, jobjectArray addrs, jint port, jint addrsLength, jboolean add, jboolean preferIPv6) { SOCKADDR *sap, *tmpSap; int i, sa_len = sizeof(SOCKADDR); jobject ia; if (addrsLength < 1) return; if ((sap = calloc(addrsLength, sa_len)) == NULL) { JNU_ThrowOutOfMemoryError(env, "heap allocation failure"); return; } tmpSap = sap; for (i=0; iGetObjectArrayElement(env, addrs, i); if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr*)tmpSap, &sa_len, preferIPv6) != 0) { free(sap); return; } tmpSap++; } if (nio_sctp_bindx(fd, (void*)sap, addrsLength, add ? SCTP_BINDX_ADD_ADDR : SCTP_BINDX_REM_ADDR) != 0) { handleSocketError(env, errno); } free(sap); } /* * Class: sun_nio_ch_SctpNet * Method: listen0 * Signature: (II)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_listen0 (JNIEnv *env, jclass cl, jint fd, jint backlog) { if (listen(fd, backlog) < 0) handleSocketError(env, errno); } /* * Class: sun_nio_ch_SctpNet * Method: connect0 * Signature: (ILjava/net/InetAddress;I)I */ JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpNet_connect0 (JNIEnv *env, jclass clazz, int fd, jobject iao, jint port) { SOCKADDR sa; int sa_len = SOCKADDR_LEN; int rv; if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *) &sa, &sa_len, JNI_TRUE) != 0) { return IOS_THROWN; } rv = connect(fd, (struct sockaddr *)&sa, sa_len); if (rv != 0) { if (errno == EINPROGRESS) { return IOS_UNAVAILABLE; } else if (errno == EINTR) { return IOS_INTERRUPTED; } return handleSocketError(env, errno); } return 1; } /* * Class: sun_nio_ch_SctpNet * Method: close0 * Signature: (I)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_close0 (JNIEnv *env, jclass clazz, jint fd) { if (fd != -1) { int rv = close(fd); if (rv < 0) JNU_ThrowIOExceptionWithLastError(env, "Close failed"); } } /* * Class: sun_nio_ch_SctpNet * Method: preClose0 * Signature: (I)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_preClose0 (JNIEnv *env, jclass clazz, jint fd) { if (preCloseFD >= 0) { if (dup2(preCloseFD, fd) < 0) JNU_ThrowIOExceptionWithLastError(env, "dup2 failed"); } } void initializeISA (JNIEnv* env) { if (isaCls == 0) { jclass c = (*env)->FindClass(env, "java/net/InetSocketAddress"); CHECK_NULL(c); isaCls = (*env)->NewGlobalRef(env, c); CHECK_NULL(isaCls); (*env)->DeleteLocalRef(env, c); isaCtrID = (*env)->GetMethodID(env, isaCls, "", "(Ljava/net/InetAddress;I)V"); } } jobject SockAddrToInetSocketAddress (JNIEnv *env, struct sockaddr* sap) { int port = 0; jobject ia = NET_SockaddrToInetAddress(env, sap, &port); if (ia == NULL) return NULL; if (isaCls == 0) { initializeISA(env); CHECK_NULL_RETURN(isaCls, NULL); } return (*env)->NewObject(env, isaCls, isaCtrID, ia, port); } /* * Class: sun_nio_ch_SctpNet * Method: getLocalAddresses0 * Signature: (I)[Ljava/net/SocketAddress; */ JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getLocalAddresses0 (JNIEnv *env, jclass klass, jint fd) { void *addr_buf, *laddr; struct sockaddr* sap; int i, addrCount; jobjectArray isaa; #ifdef __solaris__ if ((addrCount = nio_sctp_getladdrs(fd, 0, (void **)&addr_buf)) == -1) { #else /* __linux__ */ if ((addrCount = nio_sctp_getladdrs(fd, 0, (struct sockaddr **)&addr_buf)) == -1) { #endif handleSocketError(env, errno); return NULL; } if (addrCount < 1) return NULL; if (isaCls == 0) { initializeISA(env); CHECK_NULL_RETURN(isaCls, NULL); } isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL); if (isaa == NULL) { nio_sctp_freeladdrs(addr_buf); return NULL; } laddr = addr_buf; for (i=0; iNewObject(env, isaCls, isaCtrID, ia, port); if (isa != NULL) (*env)->SetObjectArrayElement(env, isaa, i, isa); if (sap->sa_family == AF_INET) addr_buf = ((struct sockaddr_in*)addr_buf) + 1; else addr_buf = ((struct sockaddr_in6*)addr_buf) + 1; } nio_sctp_freeladdrs(laddr); return isaa; } jobjectArray getRemoteAddresses (JNIEnv *env, jint fd, sctp_assoc_t id) { void *addr_buf, *paddr; struct sockaddr* sap; int i, addrCount; jobjectArray isaa; #if __solaris__ if ((addrCount = nio_sctp_getpaddrs(fd, id, (void **)&addr_buf)) == -1) { #else /* __linux__ */ if ((addrCount = nio_sctp_getpaddrs(fd, id, (struct sockaddr**)&addr_buf)) == -1) { #endif handleSocketError(env, errno); return NULL; } if (addrCount < 1) return NULL; if (isaCls == 0) { initializeISA(env); CHECK_NULL_RETURN(isaCls, NULL); } isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL); if (isaa == NULL) { nio_sctp_freepaddrs(addr_buf); return NULL; } paddr = addr_buf; for (i=0; iNewObject(env, isaCls, isaCtrID, ia, port); if (isa != NULL) (*env)->SetObjectArrayElement(env, isaa, i, isa); if (sap->sa_family == AF_INET) addr_buf = ((struct sockaddr_in*)addr_buf) + 1; else addr_buf = ((struct sockaddr_in6*)addr_buf) + 1; } nio_sctp_freepaddrs(paddr); return isaa; } /* * Class: sun_nio_ch_SctpNet * Method: getRemoteAddresses0 * Signature: (II)[Ljava/net/SocketAddress; */ JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getRemoteAddresses0 (JNIEnv *env, jclass klass, jint fd, jint assocId) { return getRemoteAddresses(env, fd, assocId); } /* Map the Java level option to the native level */ int mapSocketOption (jint cmd, int *level, int *optname) { static struct { jint cmd; int level; int optname; } const opts[] = { { sun_nio_ch_SctpStdSocketOption_SCTP_DISABLE_FRAGMENTS, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS }, { sun_nio_ch_SctpStdSocketOption_SCTP_EXPLICIT_COMPLETE, IPPROTO_SCTP, SCTP_EXPLICIT_EOR }, { sun_nio_ch_SctpStdSocketOption_SCTP_FRAGMENT_INTERLEAVE, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE }, { sun_nio_ch_SctpStdSocketOption_SCTP_NODELAY, IPPROTO_SCTP, SCTP_NODELAY }, { sun_nio_ch_SctpStdSocketOption_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, { sun_nio_ch_SctpStdSocketOption_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, { sun_nio_ch_SctpStdSocketOption_SO_LINGER, SOL_SOCKET, SO_LINGER } }; int i; for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { if (cmd == opts[i].cmd) { *level = opts[i].level; *optname = opts[i].optname; return 0; } } /* not found */ return -1; } /* * Class: sun_nio_ch_SctpNet * Method: setIntOption0 * Signature: (III)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setIntOption0 (JNIEnv *env, jclass klass, jint fd, jint opt, int arg) { int klevel, kopt; int result; struct linger linger; void *parg; int arglen; if (mapSocketOption(opt, &klevel, &kopt) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Unsupported socket option"); return; } if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) { parg = (void *)&linger; arglen = sizeof(linger); if (arg >= 0) { linger.l_onoff = 1; linger.l_linger = arg; } else { linger.l_onoff = 0; linger.l_linger = 0; } } else { parg = (void *)&arg; arglen = sizeof(arg); } if (NET_SetSockOpt(fd, klevel, kopt, parg, arglen) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun_nio_ch_SctpNet.setIntOption0"); } } /* * Class: sun_nio_ch_SctpNet * Method: getIntOption0 * Signature: (II)I */ JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_getIntOption0 (JNIEnv *env, jclass klass, jint fd, jint opt) { int klevel, kopt; int result; struct linger linger; void *arg; unsigned int arglen; if (mapSocketOption(opt, &klevel, &kopt) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Unsupported socket option"); return -1; } if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) { arg = (void *)&linger; arglen = sizeof(linger); } else { arg = (void *)&result; arglen = sizeof(result); } if (NET_GetSockOpt(fd, klevel, kopt, arg, &arglen) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.getIntOption"); return -1; } if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) return linger.l_onoff ? linger.l_linger : -1; else return result; } /* * Class: sun_nio_ch_SctpNet * Method: getPrimAddrOption0 * Signature: (II)Ljava/net/SocketAddress; */ JNIEXPORT jobject JNICALL Java_sun_nio_ch_SctpNet_getPrimAddrOption0 (JNIEnv *env, jclass klass, jint fd, jint assocId) { struct sctp_setprim prim; unsigned int prim_len = sizeof(prim); struct sockaddr* sap = (struct sockaddr*)&prim.ssp_addr; prim.ssp_assoc_id = assocId; if (getsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, &prim_len) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.SctpNet.getPrimAddrOption0"); return NULL; } return SockAddrToInetSocketAddress(env, sap); } /* * Class: sun_nio_ch_SctpNet * Method: setPrimAddrOption0 * Signature: (IILjava/net/InetAddress;I)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPrimAddrOption0 (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) { struct sctp_setprim prim; struct sockaddr* sap = (struct sockaddr*)&prim.ssp_addr; int sap_len; if (NET_InetAddressToSockaddr(env, iaObj, port, sap, &sap_len, JNI_TRUE) != 0) { return; } prim.ssp_assoc_id = assocId; if (setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, sizeof(prim)) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.SctpNet.setPrimAddrOption0"); } } /* * Class: sun_nio_ch_SctpNet * Method: setPeerPrimAddrOption0 * Signature: (IILjava/net/InetAddress;I)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0 (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) { struct sctp_setpeerprim prim; struct sockaddr_storage ss; int ss_len = sizeof(ss); if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss, &ss_len, JNI_TRUE) != 0) { return; } prim.sspp_assoc_id = assocId; prim.sspp_addr = ss; if (setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, &prim, sizeof(prim)) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.SctpNet.setPeerPrimAddrOption0"); } } /* * Class: sun_nio_ch_SctpNet * Method: getInitMsgOption0 * Signature: (I[I)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_getInitMsgOption0 (JNIEnv *env, jclass klass, jint fd, jintArray retVal) { struct sctp_initmsg sctp_initmsg; unsigned int sim_len = sizeof(sctp_initmsg); int vals[2]; if (getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg, &sim_len) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.SctpNet.getInitMsgOption0"); return; } vals[0] = sctp_initmsg.sinit_max_instreams; vals[1] = sctp_initmsg.sinit_num_ostreams; (*env)->SetIntArrayRegion(env, retVal, 0, 2, vals); } /* * Class: sun_nio_ch_SctpNet * Method: setInitMsgOption0 * Signature: (III)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setInitMsgOption0 (JNIEnv *env, jclass klass, jint fd, jint inArg, jint outArg) { struct sctp_initmsg sctp_initmsg; sctp_initmsg.sinit_max_instreams = (unsigned int)inArg; sctp_initmsg.sinit_num_ostreams = (unsigned int)outArg; sctp_initmsg.sinit_max_attempts = 0; // default sctp_initmsg.sinit_max_init_timeo = 0; // default if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg, sizeof(sctp_initmsg)) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.SctpNet.setInitMsgOption0"); } } /* * Class: sun_nio_ch_SctpNet * Method: shutdown0 * Signature: (II)V */ JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_shutdown0 (JNIEnv *env, jclass klass, jint fd, jint assocId) { int rv; struct msghdr msg[1]; struct iovec iov[1]; int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo)); char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; struct cmsghdr* cmsg; struct sctp_sndrcvinfo *sri; /* SctpSocketChannel */ if (assocId < 0) { shutdown(fd, SHUT_WR); return; } memset(msg, 0, sizeof (*msg)); memset(cbuf, 0, cbuf_size); msg->msg_name = NULL; msg->msg_namelen = 0; iov->iov_base = NULL; iov->iov_len = 0; msg->msg_iov = iov; msg->msg_iovlen = 1; msg->msg_control = cbuf; msg->msg_controllen = cbuf_size; msg->msg_flags = 0; cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_SNDRCV; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); /* Initialize the payload: */ sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg); memset(sri, 0, sizeof (*sri)); if (assocId > 0) { sri->sinfo_assoc_id = assocId; } sri->sinfo_flags = sri->sinfo_flags | SCTP_EOF; /* Sum of the length of all control messages in the buffer. */ msg->msg_controllen = cmsg->cmsg_len; if ((rv = sendmsg(fd, msg, 0)) < 0) { handleSocketError(env, errno); } } /* * Class: sun_nio_ch_SctpNet * Method: branch * Signature: (II)I */ JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_branch0 (JNIEnv *env, jclass klass, jint fd, jint assocId) { int newfd = 0; if ((newfd = nio_sctp_peeloff(fd, assocId)) < 0) { handleSocketError(env, errno); } return newfd; }