--- old/src/solaris/native/sun/nio/ch/SctpChannelImpl.c Mon Jan 30 16:04:23 2012 +++ /dev/null Mon Jan 30 16:04:23 2012 @@ -1,594 +0,0 @@ -/* - * Copyright (c) 2009, 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 -#include -#include "Sctp.h" - -#include "jni.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_SctpChannelImpl.h" -#include "sun_nio_ch_SctpAssocChange.h" -#include "sun_nio_ch_SctpResultContainer.h" -#include "sun_nio_ch_SctpPeerAddrChange.h" - -/* sizeof(union sctp_notification */ -#define NOTIFICATION_BUFFER_SIZE 280 - -#define MESSAGE_IMPL_CLASS "sun/nio/ch/SctpMessageInfoImpl" -#define RESULT_CONTAINER_CLASS "sun/nio/ch/SctpResultContainer" -#define SEND_FAILED_CLASS "sun/nio/ch/SctpSendFailed" -#define ASSOC_CHANGE_CLASS "sun/nio/ch/SctpAssocChange" -#define PEER_CHANGE_CLASS "sun/nio/ch/SctpPeerAddrChange" -#define SHUTDOWN_CLASS "sun/nio/ch/SctpShutdown" - -struct controlData { - int assocId; - unsigned short streamNumber; - jboolean unordered; - unsigned int ppid; -}; - -static jclass smi_class; /* sun.nio.ch.SctpMessageInfoImpl */ -static jmethodID smi_ctrID; /* sun.nio.ch.SctpMessageInfoImpl. */ -static jfieldID src_valueID; /* sun.nio.ch.SctpResultContainer.value */ -static jfieldID src_typeID; /* sun.nio.ch.SctpResultContainer.type */ -static jclass ssf_class; /* sun.nio.ch.SctpSendFailed */ -static jmethodID ssf_ctrID; /* sun.nio.ch.SctpSendFailed. */ -static jclass sac_class; /* sun.nio.ch.SctpAssociationChanged */ -static jmethodID sac_ctrID; /* sun.nio.ch.SctpAssociationChanged. */ -static jclass spc_class; /* sun.nio.ch.SctpPeerAddressChanged */ -static jmethodID spc_ctrID; /* sun.nio.ch.SctpPeerAddressChanged. */ -static jclass ss_class; /* sun.nio.ch.SctpShutdown */ -static jmethodID ss_ctrID; /* sun.nio.ch.SctpShutdown. */ -static jfieldID isa_addrID; /* java.net.InetSocketAddress.addr */ -static jfieldID isa_portID; /* java.net.InetSocketAddress.port */ - -/* defined in SctpNet.c */ -jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr); - -/* use SocketChannelImpl's checkConnect implementation */ -extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env, - jobject this, jobject fdo, jboolean block, jboolean ready); - -/* - * Class: sun_nio_ch_SctpChannelImpl - * Method: initIDs - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_sun_nio_ch_SctpChannelImpl_initIDs - (JNIEnv *env, jclass klass) { - jclass cls; - - /* SctpMessageInfoImpl */ - cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS); - CHECK_NULL(cls); - smi_class = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(smi_class); - smi_ctrID = (*env)->GetMethodID(env, cls, "", - "(ILjava/net/SocketAddress;IIZZI)V"); - CHECK_NULL(smi_ctrID); - - /* SctpResultContainer */ - cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS); - CHECK_NULL(cls); - src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;"); - CHECK_NULL(src_valueID); - src_typeID = (*env)->GetFieldID(env, cls, "type", "I"); - CHECK_NULL(src_typeID); - - /* SctpSendFailed */ - cls = (*env)->FindClass(env, SEND_FAILED_CLASS); - CHECK_NULL(cls); - ssf_class = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(ssf_class); - ssf_ctrID = (*env)->GetMethodID(env, cls, "", - "(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V"); - CHECK_NULL(ssf_ctrID); - - /* SctpAssocChange */ - cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS); - CHECK_NULL(cls); - sac_class = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(sac_class); - sac_ctrID = (*env)->GetMethodID(env, cls, "", "(IIII)V"); - CHECK_NULL(sac_ctrID); - - /* SctpPeerAddrChange */ - cls = (*env)->FindClass(env, PEER_CHANGE_CLASS); - CHECK_NULL(cls); - spc_class = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(spc_class); - spc_ctrID = (*env)->GetMethodID(env, cls, "", - "(ILjava/net/SocketAddress;I)V"); - CHECK_NULL(spc_ctrID); - - /* sun.nio.ch.SctpShutdown */ - cls = (*env)->FindClass(env, SHUTDOWN_CLASS); - CHECK_NULL(cls); - ss_class = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(ss_class); - ss_ctrID = (*env)->GetMethodID(env, cls, "", "(I)V"); - CHECK_NULL(ss_ctrID); - - /* InetSocketAddress */ - cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); - CHECK_NULL(cls); - isa_addrID = (*env)->GetFieldID(env, cls, "addr", "Ljava/net/InetAddress;"); - CHECK_NULL(isa_addrID); - isa_portID = (*env)->GetFieldID(env, cls, "port", "I"); -} - -void getControlData - (struct msghdr* msg, struct controlData* cdata) { - struct cmsghdr* cmsg; - - for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { - if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) { - struct sctp_sndrcvinfo *sri; - - sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg); - cdata->assocId = sri->sinfo_assoc_id; - cdata->streamNumber = sri->sinfo_stream; - cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE : - JNI_FALSE; - cdata->ppid = ntohl(sri->sinfo_ppid); - - return; - } - } - return; -} - -void setControlData - (struct msghdr* msg, struct controlData* cdata) { - struct cmsghdr* cmsg; - struct sctp_sndrcvinfo *sri; - - 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 (cdata->streamNumber > 0) { - sri->sinfo_stream = cdata->streamNumber; - } - if (cdata->assocId > 0) { - sri->sinfo_assoc_id = cdata->assocId; - } - if (cdata->unordered == JNI_TRUE) { - sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED; - } - - if (cdata->ppid > 0) { - sri->sinfo_ppid = htonl(cdata->ppid); - } - - /* Sum of the length of all control messages in the buffer. */ - msg->msg_controllen = cmsg->cmsg_len; -} - -// TODO: test: can create send failed without any data? if so need to -// update API so that buffer can be null if no data. -void handleSendFailed - (JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf, - int read, jboolean isEOR, struct sockaddr* sap) { - jobject bufferObj = NULL, resultObj, isaObj; - char *addressP; - struct sctp_sndrcvinfo *sri; - int remaining, dataLength; - - /* the actual undelivered message data is directly after the ssf */ - int dataOffset = sizeof(struct sctp_send_failed); - - sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info; - - /* the number of bytes remaining to be read in the sctp_send_failed notif*/ - remaining = ssf->ssf_length - read; - - /* the size of the actual undelivered message */ - dataLength = ssf->ssf_length - dataOffset; - - /* retrieved address from sockaddr */ - isaObj = SockAddrToInetSocketAddress(env, sap); - - /* data retrieved from sff_data */ - if (dataLength > 0) { - struct iovec iov[1]; - struct msghdr msg[1]; - int rv, alreadyRead; - char *dataP = (char*) ssf; - dataP += dataOffset; - - if ((addressP = malloc(dataLength)) == NULL) { - JNU_ThrowOutOfMemoryError(env, "handleSendFailed"); - return; - } - - memset(msg, 0, sizeof (*msg)); - msg->msg_iov = iov; - msg->msg_iovlen = 1; - - bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength); - CHECK_NULL(bufferObj); - - alreadyRead = read - dataOffset; - if (alreadyRead > 0) { - memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead); - iov->iov_base = addressP + alreadyRead; - iov->iov_len = dataLength - alreadyRead; - } else { - iov->iov_base = addressP; - iov->iov_len = dataLength; - } - - if (remaining > 0) { - if ((rv = recvmsg(fd, msg, 0)) < 0) { - handleSocketError(env, errno); - return; - } - - if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) { - //TODO: assert false: "should not reach here"; - return; - } - // TODO: Set and document (in API) buffers position. - } - } - - /* create SctpSendFailed */ - resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id, - isaObj, bufferObj, ssf->ssf_error, sri->sinfo_stream); - CHECK_NULL(resultObj); - (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); - (*env)->SetIntField(env, resultContainerObj, src_typeID, - sun_nio_ch_SctpResultContainer_SEND_FAILED); -} - -void handleAssocChange - (JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) { - jobject resultObj; - int state = 0; - - switch (sac->sac_state) { - case SCTP_COMM_UP : - state = sun_nio_ch_SctpAssocChange_SCTP_COMM_UP; - break; - case SCTP_COMM_LOST : - state = sun_nio_ch_SctpAssocChange_SCTP_COMM_LOST; - break; - case SCTP_RESTART : - state = sun_nio_ch_SctpAssocChange_SCTP_RESTART; - break; - case SCTP_SHUTDOWN_COMP : - state = sun_nio_ch_SctpAssocChange_SCTP_SHUTDOWN; - break; - case SCTP_CANT_STR_ASSOC : - state = sun_nio_ch_SctpAssocChange_SCTP_CANT_START; - } - - /* create SctpAssociationChanged */ - resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id, - state, sac->sac_outbound_streams, sac->sac_inbound_streams); - CHECK_NULL(resultObj); - (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); - (*env)->SetIntField(env, resultContainerObj, src_typeID, - sun_nio_ch_SctpResultContainer_ASSOCIATION_CHANGED); -} - -void handleShutdown - (JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) { - /* create SctpShutdown */ - jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id); - CHECK_NULL(resultObj); - (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); - (*env)->SetIntField(env, resultContainerObj, src_typeID, - sun_nio_ch_SctpResultContainer_SHUTDOWN); -} - -void handlePeerAddrChange - (JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) { - int event = 0; - jobject addressObj, resultObj; - unsigned int state = spc->spc_state; - - switch (state) { - case SCTP_ADDR_AVAILABLE : - event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_AVAILABLE; - break; - case SCTP_ADDR_UNREACHABLE : - event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_UNREACHABLE; - break; - case SCTP_ADDR_REMOVED : - event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_REMOVED; - break; - case SCTP_ADDR_ADDED : - event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_ADDED; - break; - case SCTP_ADDR_MADE_PRIM : - event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_MADE_PRIM; -#ifdef __linux__ /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */ - break; - case SCTP_ADDR_CONFIRMED : - event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_CONFIRMED; -#endif /* __linux__ */ - } - - addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr); - - /* create SctpPeerAddressChanged */ - resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id, - addressObj, event); - CHECK_NULL(resultObj); - (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); - (*env)->SetIntField(env, resultContainerObj, src_typeID, - sun_nio_ch_SctpResultContainer_PEER_ADDRESS_CHANGED); -} - -void handleUninteresting - (union sctp_notification *snp) { - //fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type); -} - -/** - * Handle notifications from the SCTP stack. - * Returns JNI_TRUE if the notification is one that is of interest to the - * Java API, otherwise JNI_FALSE. - */ -jboolean handleNotification - (JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp, - int read, jboolean isEOR, struct sockaddr* sap) { - switch (snp->sn_header.sn_type) { - case SCTP_SEND_FAILED: - handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed, - read, isEOR, sap); - return JNI_TRUE; - case SCTP_ASSOC_CHANGE: - handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change); - return JNI_TRUE; - case SCTP_SHUTDOWN_EVENT: - handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event); - return JNI_TRUE; - case SCTP_PEER_ADDR_CHANGE: - handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change); - return JNI_TRUE; - default : - /* the Java API is not interested in this event, maybe we are? */ - handleUninteresting(snp); - } - return JNI_FALSE; -} - -void handleMessage - (JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read, - jboolean isEOR, struct sockaddr* sap) { - jobject isa, resultObj; - struct controlData cdata[1]; - - if (read == 0) { - /* we reached EOF */ - read = -1; - } - - isa = SockAddrToInetSocketAddress(env, sap); - getControlData(msg, cdata); - - /* create SctpMessageInfoImpl */ - resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId, - isa, read, cdata->streamNumber, - isEOR ? JNI_TRUE : JNI_FALSE, - cdata->unordered, cdata->ppid); - CHECK_NULL(resultObj); - (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); - (*env)->SetIntField(env, resultContainerObj, src_typeID, - sun_nio_ch_SctpResultContainer_MESSAGE); -} - -/* - * Class: sun_nio_ch_SctpChannelImpl - * Method: receive0 - * Signature: (ILsun/nio/ch/SctpResultContainer;JIZ)I - */ -JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_receive0 - (JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj, - jlong address, jint length, jboolean peek) { - SOCKADDR sa; - int sa_len = sizeof(sa); - ssize_t rv = 0; - jlong *addr = jlong_to_ptr(address); - struct iovec iov[1]; - struct msghdr msg[1]; - char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; - int flags = peek == JNI_TRUE ? MSG_PEEK : 0; - - /* Set up the msghdr structure for receiving */ - memset(msg, 0, sizeof (*msg)); - msg->msg_name = &sa; - msg->msg_namelen = sa_len; - iov->iov_base = addr; - iov->iov_len = length; - msg->msg_iov = iov; - msg->msg_iovlen = 1; - msg->msg_control = cbuf; - msg->msg_controllen = sizeof(cbuf); - msg->msg_flags = 0; - - do { - if ((rv = recvmsg(fd, msg, flags)) < 0) { - if (errno == EWOULDBLOCK) { - return IOS_UNAVAILABLE; - } else if (errno == EINTR) { - return IOS_INTERRUPTED; - -#ifdef __linux__ - } else if (errno == ENOTCONN) { - /* ENOTCONN when EOF reached */ - rv = 0; - /* there will be no control data */ - msg->msg_controllen = 0; -#endif /* __linux__ */ - - } else { - handleSocketError(env, errno); - return 0; - } - } - - if (msg->msg_flags & MSG_NOTIFICATION) { - char *bufp = (char*)addr; - union sctp_notification *snp; - - if (!(msg->msg_flags & MSG_EOR) && length < NOTIFICATION_BUFFER_SIZE) { - char buf[NOTIFICATION_BUFFER_SIZE]; - int rvSAVE = rv; - memcpy(buf, addr, rv); - iov->iov_base = buf + rv; - iov->iov_len = NOTIFICATION_BUFFER_SIZE - rv; - if ((rv = recvmsg(fd, msg, flags)) < 0) { - handleSocketError(env, errno); - return 0; - } - bufp = buf; - rv += rvSAVE; - } - snp = (union sctp_notification *) bufp; - if (handleNotification(env, fd, resultContainerObj, snp, rv, - (msg->msg_flags & MSG_EOR), - (struct sockaddr*)&sa ) == JNI_TRUE) { - /* We have received a notification that is of interest to - to the Java API. The appropriate notification will be - set in the result container. */ - return 0; - } - - // set iov back to addr, and reset msg_controllen - iov->iov_base = addr; - iov->iov_len = length; - msg->msg_control = cbuf; - msg->msg_controllen = sizeof(cbuf); - } - } while (msg->msg_flags & MSG_NOTIFICATION); - - handleMessage(env, resultContainerObj, msg, rv, - (msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa); - return rv; -} - -/* - * Class: sun_nio_ch_SctpChannelImpl - * Method: send0 - * Signature: (IJILjava/net/SocketAddress;IIZI)I - */ -JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_send0 - (JNIEnv *env, jclass klass, jint fd, jlong address, jint length, - jobject saTarget, jint assocId, jint streamNumber, jboolean unordered, - jint ppid) { - SOCKADDR sa; - int sa_len = sizeof(sa); - ssize_t rv = 0; - jlong *addr = jlong_to_ptr(address); - struct iovec iov[1]; - struct msghdr msg[1]; - int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo)); - char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; - struct controlData cdata[1]; - - /* SctpChannel: - * saTarget may contain the preferred address or NULL to use primary, - * assocId will always be -1 - * SctpMultiChannell: - * Setup new association, saTarget will contain address, assocId = -1 - * Association already existing, assocId != -1, saTarget = preferred addr - */ - if (saTarget != NULL /*&& assocId <= 0*/) { - - jobject targetAddress = (*env)->GetObjectField(env, saTarget, isa_addrID); - jint targetPort = (*env)->GetIntField(env, saTarget, isa_portID); - - if (NET_InetAddressToSockaddr(env, targetAddress, targetPort, - (struct sockaddr *)&sa, - &sa_len, JNI_TRUE) != 0) { - return IOS_THROWN; - } - } else { - memset(&sa, '\x0', sa_len); - sa_len = 0; - } - - /* Set up the msghdr structure for sending */ - memset(msg, 0, sizeof (*msg)); - memset(cbuf, 0, cbuf_size); - msg->msg_name = &sa; - msg->msg_namelen = sa_len; - iov->iov_base = addr; - iov->iov_len = length; - msg->msg_iov = iov; - msg->msg_iovlen = 1; - msg->msg_control = cbuf; - msg->msg_controllen = cbuf_size; - msg->msg_flags = 0; - - cdata->streamNumber = streamNumber; - cdata->assocId = assocId; - cdata->unordered = unordered; - cdata->ppid = ppid; - setControlData(msg, cdata); - - if ((rv = sendmsg(fd, msg, 0)) < 0) { - if (errno == EWOULDBLOCK) { - return IOS_UNAVAILABLE; - } else if (errno == EINTR) { - return IOS_INTERRUPTED; - } else if (errno == EPIPE) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Socket is shutdown for writing"); - } else { - handleSocketError(env, errno); - return 0; - } - } - - return rv; -} - -/* - * Class: sun_nio_ch_SctpChannelImpl - * Method: checkConnect - * Signature: (Ljava/io/FileDescriptor;ZZ)I - */ -JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_checkConnect - (JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) { - return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this, - fdo, block, ready); -} - --- /dev/null Mon Jan 30 16:04:23 2012 +++ new/src/solaris/native/sun/nio/ch/sctp/SctpChannelImpl.c Mon Jan 30 16:04:23 2012 @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2009, 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 +#include +#include "Sctp.h" + +#include "jni.h" +#include "nio_util.h" +#include "nio.h" +#include "net_util.h" +#include "net_util_md.h" +#include "sun_nio_ch_sctp_SctpNet.h" +#include "sun_nio_ch_sctp_SctpChannelImpl.h" +#include "sun_nio_ch_sctp_SctpAssocChange.h" +#include "sun_nio_ch_sctp_SctpResultContainer.h" +#include "sun_nio_ch_sctp_SctpPeerAddrChange.h" + +/* sizeof(union sctp_notification */ +#define NOTIFICATION_BUFFER_SIZE 280 + +#define MESSAGE_IMPL_CLASS "sun/nio/ch/sctp/SctpMessageInfoImpl" +#define RESULT_CONTAINER_CLASS "sun/nio/ch/sctp/SctpResultContainer" +#define SEND_FAILED_CLASS "sun/nio/ch/sctp/SctpSendFailed" +#define ASSOC_CHANGE_CLASS "sun/nio/ch/sctp/SctpAssocChange" +#define PEER_CHANGE_CLASS "sun/nio/ch/sctp/SctpPeerAddrChange" +#define SHUTDOWN_CLASS "sun/nio/ch/sctp/SctpShutdown" + +struct controlData { + int assocId; + unsigned short streamNumber; + jboolean unordered; + unsigned int ppid; +}; + +static jclass smi_class; /* sun.nio.ch.sctp.SctpMessageInfoImpl */ +static jmethodID smi_ctrID; /* sun.nio.ch.sctp.SctpMessageInfoImpl. */ +static jfieldID src_valueID; /* sun.nio.ch.sctp.SctpResultContainer.value */ +static jfieldID src_typeID; /* sun.nio.ch.sctp.SctpResultContainer.type */ +static jclass ssf_class; /* sun.nio.ch.sctp.SctpSendFailed */ +static jmethodID ssf_ctrID; /* sun.nio.ch.sctp.SctpSendFailed. */ +static jclass sac_class; /* sun.nio.ch.sctp.SctpAssociationChanged */ +static jmethodID sac_ctrID; /* sun.nio.ch.sctp.SctpAssociationChanged.*/ +static jclass spc_class; /* sun.nio.ch.sctp.SctpPeerAddressChanged */ +static jmethodID spc_ctrID; /* sun.nio.ch.sctp.SctpPeerAddressChanged.*/ +static jclass ss_class; /* sun.nio.ch.sctp.SctpShutdown */ +static jmethodID ss_ctrID; /* sun.nio.ch.sctp.SctpShutdown. */ +static jfieldID isa_addrID; /* java.net.InetSocketAddress.addr */ +static jfieldID isa_portID; /* java.net.InetSocketAddress.port */ + +/* defined in SctpNet.c */ +jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr); + +jint handleSocketError(JNIEnv *env, jint errorValue); + +/* use SocketChannelImpl's checkConnect implementation */ +extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env, + jobject this, jobject fdo, jboolean block, jboolean ready); + +/* + * Class: sun_nio_ch_sctp_SctpChannelImpl + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_initIDs + (JNIEnv *env, jclass klass) { + jclass cls; + + /* SctpMessageInfoImpl */ + cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS); + CHECK_NULL(cls); + smi_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(smi_class); + smi_ctrID = (*env)->GetMethodID(env, cls, "", + "(ILjava/net/SocketAddress;IIZZI)V"); + CHECK_NULL(smi_ctrID); + + /* SctpResultContainer */ + cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS); + CHECK_NULL(cls); + src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;"); + CHECK_NULL(src_valueID); + src_typeID = (*env)->GetFieldID(env, cls, "type", "I"); + CHECK_NULL(src_typeID); + + /* SctpSendFailed */ + cls = (*env)->FindClass(env, SEND_FAILED_CLASS); + CHECK_NULL(cls); + ssf_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(ssf_class); + ssf_ctrID = (*env)->GetMethodID(env, cls, "", + "(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V"); + CHECK_NULL(ssf_ctrID); + + /* SctpAssocChange */ + cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS); + CHECK_NULL(cls); + sac_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(sac_class); + sac_ctrID = (*env)->GetMethodID(env, cls, "", "(IIII)V"); + CHECK_NULL(sac_ctrID); + + /* SctpPeerAddrChange */ + cls = (*env)->FindClass(env, PEER_CHANGE_CLASS); + CHECK_NULL(cls); + spc_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(spc_class); + spc_ctrID = (*env)->GetMethodID(env, cls, "", + "(ILjava/net/SocketAddress;I)V"); + CHECK_NULL(spc_ctrID); + + /* sun.nio.ch.SctpShutdown */ + cls = (*env)->FindClass(env, SHUTDOWN_CLASS); + CHECK_NULL(cls); + ss_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(ss_class); + ss_ctrID = (*env)->GetMethodID(env, cls, "", "(I)V"); + CHECK_NULL(ss_ctrID); + + /* InetSocketAddress */ + cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL(cls); + isa_addrID = (*env)->GetFieldID(env, cls, "addr", "Ljava/net/InetAddress;"); + CHECK_NULL(isa_addrID); + isa_portID = (*env)->GetFieldID(env, cls, "port", "I"); +} + +void getControlData + (struct msghdr* msg, struct controlData* cdata) { + struct cmsghdr* cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) { + struct sctp_sndrcvinfo *sri; + + sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg); + cdata->assocId = sri->sinfo_assoc_id; + cdata->streamNumber = sri->sinfo_stream; + cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE : + JNI_FALSE; + cdata->ppid = ntohl(sri->sinfo_ppid); + + return; + } + } + return; +} + +void setControlData + (struct msghdr* msg, struct controlData* cdata) { + struct cmsghdr* cmsg; + struct sctp_sndrcvinfo *sri; + + 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 (cdata->streamNumber > 0) { + sri->sinfo_stream = cdata->streamNumber; + } + if (cdata->assocId > 0) { + sri->sinfo_assoc_id = cdata->assocId; + } + if (cdata->unordered == JNI_TRUE) { + sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED; + } + + if (cdata->ppid > 0) { + sri->sinfo_ppid = htonl(cdata->ppid); + } + + /* Sum of the length of all control messages in the buffer. */ + msg->msg_controllen = cmsg->cmsg_len; +} + +// TODO: test: can create send failed without any data? if so need to +// update API so that buffer can be null if no data. +void handleSendFailed + (JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf, + int read, jboolean isEOR, struct sockaddr* sap) { + jobject bufferObj = NULL, resultObj, isaObj; + char *addressP; + struct sctp_sndrcvinfo *sri; + int remaining, dataLength; + + /* the actual undelivered message data is directly after the ssf */ + int dataOffset = sizeof(struct sctp_send_failed); + + sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info; + + /* the number of bytes remaining to be read in the sctp_send_failed notif*/ + remaining = ssf->ssf_length - read; + + /* the size of the actual undelivered message */ + dataLength = ssf->ssf_length - dataOffset; + + /* retrieved address from sockaddr */ + isaObj = SockAddrToInetSocketAddress(env, sap); + + /* data retrieved from sff_data */ + if (dataLength > 0) { + struct iovec iov[1]; + struct msghdr msg[1]; + int rv, alreadyRead; + char *dataP = (char*) ssf; + dataP += dataOffset; + + if ((addressP = malloc(dataLength)) == NULL) { + JNU_ThrowOutOfMemoryError(env, "handleSendFailed"); + return; + } + + memset(msg, 0, sizeof (*msg)); + msg->msg_iov = iov; + msg->msg_iovlen = 1; + + bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength); + CHECK_NULL(bufferObj); + + alreadyRead = read - dataOffset; + if (alreadyRead > 0) { + memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead); + iov->iov_base = addressP + alreadyRead; + iov->iov_len = dataLength - alreadyRead; + } else { + iov->iov_base = addressP; + iov->iov_len = dataLength; + } + + if (remaining > 0) { + if ((rv = recvmsg(fd, msg, 0)) < 0) { + handleSocketError(env, errno); + return; + } + + if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) { + //TODO: assert false: "should not reach here"; + return; + } + // TODO: Set and document (in API) buffers position. + } + } + + /* create SctpSendFailed */ + resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id, + isaObj, bufferObj, ssf->ssf_error, sri->sinfo_stream); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_sctp_SctpResultContainer_SEND_FAILED); +} + +void handleAssocChange + (JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) { + jobject resultObj; + int state = 0; + + switch (sac->sac_state) { + case SCTP_COMM_UP : + state = sun_nio_ch_sctp_SctpAssocChange_SCTP_COMM_UP; + break; + case SCTP_COMM_LOST : + state = sun_nio_ch_sctp_SctpAssocChange_SCTP_COMM_LOST; + break; + case SCTP_RESTART : + state = sun_nio_ch_sctp_SctpAssocChange_SCTP_RESTART; + break; + case SCTP_SHUTDOWN_COMP : + state = sun_nio_ch_sctp_SctpAssocChange_SCTP_SHUTDOWN; + break; + case SCTP_CANT_STR_ASSOC : + state = sun_nio_ch_sctp_SctpAssocChange_SCTP_CANT_START; + } + + /* create SctpAssociationChanged */ + resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id, + state, sac->sac_outbound_streams, sac->sac_inbound_streams); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_sctp_SctpResultContainer_ASSOCIATION_CHANGED); +} + +void handleShutdown + (JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) { + /* create SctpShutdown */ + jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_sctp_SctpResultContainer_SHUTDOWN); +} + +void handlePeerAddrChange + (JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) { + int event = 0; + jobject addressObj, resultObj; + unsigned int state = spc->spc_state; + + switch (state) { + case SCTP_ADDR_AVAILABLE : + event = sun_nio_ch_sctp_SctpPeerAddrChange_SCTP_ADDR_AVAILABLE; + break; + case SCTP_ADDR_UNREACHABLE : + event = sun_nio_ch_sctp_SctpPeerAddrChange_SCTP_ADDR_UNREACHABLE; + break; + case SCTP_ADDR_REMOVED : + event = sun_nio_ch_sctp_SctpPeerAddrChange_SCTP_ADDR_REMOVED; + break; + case SCTP_ADDR_ADDED : + event = sun_nio_ch_sctp_SctpPeerAddrChange_SCTP_ADDR_ADDED; + break; + case SCTP_ADDR_MADE_PRIM : + event = sun_nio_ch_sctp_SctpPeerAddrChange_SCTP_ADDR_MADE_PRIM; +#ifdef __linux__ /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */ + break; + case SCTP_ADDR_CONFIRMED : + event = sun_nio_ch_sctp_SctpPeerAddrChange_SCTP_ADDR_CONFIRMED; +#endif /* __linux__ */ + } + + addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr); + + /* create SctpPeerAddressChanged */ + resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id, + addressObj, event); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_sctp_SctpResultContainer_PEER_ADDRESS_CHANGED); +} + +void handleUninteresting + (union sctp_notification *snp) { + //fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type); +} + +/** + * Handle notifications from the SCTP stack. + * Returns JNI_TRUE if the notification is one that is of interest to the + * Java API, otherwise JNI_FALSE. + */ +jboolean handleNotification + (JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp, + int read, jboolean isEOR, struct sockaddr* sap) { + switch (snp->sn_header.sn_type) { + case SCTP_SEND_FAILED: + handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed, + read, isEOR, sap); + return JNI_TRUE; + case SCTP_ASSOC_CHANGE: + handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change); + return JNI_TRUE; + case SCTP_SHUTDOWN_EVENT: + handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event); + return JNI_TRUE; + case SCTP_PEER_ADDR_CHANGE: + handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change); + return JNI_TRUE; + default : + /* the Java API is not interested in this event, maybe we are? */ + handleUninteresting(snp); + } + return JNI_FALSE; +} + +void handleMessage + (JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read, + jboolean isEOR, struct sockaddr* sap) { + jobject isa, resultObj; + struct controlData cdata[1]; + + if (read == 0) { + /* we reached EOF */ + read = -1; + } + + isa = SockAddrToInetSocketAddress(env, sap); + getControlData(msg, cdata); + + /* create SctpMessageInfoImpl */ + resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId, + isa, read, cdata->streamNumber, + isEOR ? JNI_TRUE : JNI_FALSE, + cdata->unordered, cdata->ppid); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_sctp_SctpResultContainer_MESSAGE); +} + +/* + * Class: sun_nio_ch_sctp_SctpChannelImpl + * Method: receive0 + * Signature: (ILsun/nio/ch/SctpResultContainer;JIZ)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_receive0 + (JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj, + jlong address, jint length, jboolean peek) { + SOCKADDR sa; + int sa_len = sizeof(sa); + ssize_t rv = 0; + jlong *addr = jlong_to_ptr(address); + struct iovec iov[1]; + struct msghdr msg[1]; + char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; + int flags = peek == JNI_TRUE ? MSG_PEEK : 0; + + /* Set up the msghdr structure for receiving */ + memset(msg, 0, sizeof (*msg)); + msg->msg_name = &sa; + msg->msg_namelen = sa_len; + iov->iov_base = addr; + iov->iov_len = length; + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = cbuf; + msg->msg_controllen = sizeof(cbuf); + msg->msg_flags = 0; + + do { + if ((rv = recvmsg(fd, msg, flags)) < 0) { + if (errno == EWOULDBLOCK) { + return IOS_UNAVAILABLE; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + +#ifdef __linux__ + } else if (errno == ENOTCONN) { + /* ENOTCONN when EOF reached */ + rv = 0; + /* there will be no control data */ + msg->msg_controllen = 0; +#endif /* __linux__ */ + + } else { + handleSocketError(env, errno); + return 0; + } + } + + if (msg->msg_flags & MSG_NOTIFICATION) { + char *bufp = (char*)addr; + union sctp_notification *snp; + + if (!(msg->msg_flags & MSG_EOR) && length < NOTIFICATION_BUFFER_SIZE) { + char buf[NOTIFICATION_BUFFER_SIZE]; + int rvSAVE = rv; + memcpy(buf, addr, rv); + iov->iov_base = buf + rv; + iov->iov_len = NOTIFICATION_BUFFER_SIZE - rv; + if ((rv = recvmsg(fd, msg, flags)) < 0) { + handleSocketError(env, errno); + return 0; + } + bufp = buf; + rv += rvSAVE; + } + snp = (union sctp_notification *) bufp; + if (handleNotification(env, fd, resultContainerObj, snp, rv, + (msg->msg_flags & MSG_EOR), + (struct sockaddr*)&sa ) == JNI_TRUE) { + /* We have received a notification that is of interest to + to the Java API. The appropriate notification will be + set in the result container. */ + return 0; + } + + // set iov back to addr, and reset msg_controllen + iov->iov_base = addr; + iov->iov_len = length; + msg->msg_control = cbuf; + msg->msg_controllen = sizeof(cbuf); + } + } while (msg->msg_flags & MSG_NOTIFICATION); + + handleMessage(env, resultContainerObj, msg, rv, + (msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa); + return rv; +} + +/* + * Class: sun_nio_ch_sctp_SctpChannelImpl + * Method: send0 + * Signature: (IJILjava/net/SocketAddress;IIZI)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_send0 + (JNIEnv *env, jclass klass, jint fd, jlong address, jint length, + jobject saTarget, jint assocId, jint streamNumber, jboolean unordered, + jint ppid) { + SOCKADDR sa; + int sa_len = sizeof(sa); + ssize_t rv = 0; + jlong *addr = jlong_to_ptr(address); + struct iovec iov[1]; + struct msghdr msg[1]; + int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo)); + char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; + struct controlData cdata[1]; + + /* SctpChannel: + * saTarget may contain the preferred address or NULL to use primary, + * assocId will always be -1 + * SctpMultiChannell: + * Setup new association, saTarget will contain address, assocId = -1 + * Association already existing, assocId != -1, saTarget = preferred addr + */ + if (saTarget != NULL /*&& assocId <= 0*/) { + + jobject targetAddress = (*env)->GetObjectField(env, saTarget, isa_addrID); + jint targetPort = (*env)->GetIntField(env, saTarget, isa_portID); + + if (NET_InetAddressToSockaddr(env, targetAddress, targetPort, + (struct sockaddr *)&sa, + &sa_len, JNI_TRUE) != 0) { + return IOS_THROWN; + } + } else { + memset(&sa, '\x0', sa_len); + sa_len = 0; + } + + /* Set up the msghdr structure for sending */ + memset(msg, 0, sizeof (*msg)); + memset(cbuf, 0, cbuf_size); + msg->msg_name = &sa; + msg->msg_namelen = sa_len; + iov->iov_base = addr; + iov->iov_len = length; + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = cbuf; + msg->msg_controllen = cbuf_size; + msg->msg_flags = 0; + + cdata->streamNumber = streamNumber; + cdata->assocId = assocId; + cdata->unordered = unordered; + cdata->ppid = ppid; + setControlData(msg, cdata); + + if ((rv = sendmsg(fd, msg, 0)) < 0) { + if (errno == EWOULDBLOCK) { + return IOS_UNAVAILABLE; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } else if (errno == EPIPE) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket is shutdown for writing"); + } else { + handleSocketError(env, errno); + return 0; + } + } + + return rv; +} + +/* + * Class: sun_nio_ch_sctp_SctpChannelImpl + * Method: checkConnect + * Signature: (Ljava/io/FileDescriptor;ZZ)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_checkConnect + (JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) { + return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this, + fdo, block, ready); +} +