Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/native/sun/nio/ch/sctp/SctpChannelImpl.c
32301 views
/*1* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <stdlib.h>26#include <string.h>27#include "Sctp.h"2829#include "jni.h"30#include "nio_util.h"31#include "nio.h"32#include "net_util.h"33#include "net_util_md.h"34#include "sun_nio_ch_sctp_SctpNet.h"35#include "sun_nio_ch_sctp_SctpChannelImpl.h"36#include "sun_nio_ch_sctp_AssociationChange.h"37#include "sun_nio_ch_sctp_ResultContainer.h"38#include "sun_nio_ch_sctp_PeerAddrChange.h"3940static int SCTP_NOTIFICATION_SIZE = sizeof(union sctp_notification);4142#define MESSAGE_IMPL_CLASS "sun/nio/ch/sctp/MessageInfoImpl"43#define RESULT_CONTAINER_CLASS "sun/nio/ch/sctp/ResultContainer"44#define SEND_FAILED_CLASS "sun/nio/ch/sctp/SendFailed"45#define ASSOC_CHANGE_CLASS "sun/nio/ch/sctp/AssociationChange"46#define PEER_CHANGE_CLASS "sun/nio/ch/sctp/PeerAddrChange"47#define SHUTDOWN_CLASS "sun/nio/ch/sctp/Shutdown"4849struct controlData {50int assocId;51unsigned short streamNumber;52jboolean unordered;53unsigned int ppid;54};5556static jclass smi_class; /* sun.nio.ch.sctp.MessageInfoImpl */57static jmethodID smi_ctrID; /* sun.nio.ch.sctp.MessageInfoImpl.<init> */58static jfieldID src_valueID; /* sun.nio.ch.sctp.ResultContainer.value */59static jfieldID src_typeID; /* sun.nio.ch.sctp.ResultContainer.type */60static jclass ssf_class; /* sun.nio.ch.sctp.SendFailed */61static jmethodID ssf_ctrID; /* sun.nio.ch.sctp.SendFailed.<init> */62static jclass sac_class; /* sun.nio.ch.sctp.AssociationChange */63static jmethodID sac_ctrID; /* sun.nio.ch.sctp.AssociationChange.<init> */64static jclass spc_class; /* sun.nio.ch.sctp.PeerAddressChanged */65static jmethodID spc_ctrID; /* sun.nio.ch.sctp.PeerAddressChanged.<init> */66static jclass ss_class; /* sun.nio.ch.sctp.Shutdown */67static jmethodID ss_ctrID; /* sun.nio.ch.sctp.Shutdown.<init> */6869/* defined in SctpNet.c */70jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr);7172jint handleSocketError(JNIEnv *env, jint errorValue);7374/* use SocketChannelImpl's checkConnect implementation */75extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env,76jobject this, jobject fdo, jboolean block, jboolean ready);7778/*79* Class: sun_nio_ch_sctp_SctpChannelImpl80* Method: initIDs81* Signature: ()V82*/83JNIEXPORT void JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_initIDs84(JNIEnv *env, jclass klass) {85jclass cls;8687/* MessageInfoImpl */88cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS);89CHECK_NULL(cls);90smi_class = (*env)->NewGlobalRef(env, cls);91CHECK_NULL(smi_class);92smi_ctrID = (*env)->GetMethodID(env, cls, "<init>",93"(ILjava/net/SocketAddress;IIZZI)V");94CHECK_NULL(smi_ctrID);9596/* ResultContainer */97cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS);98CHECK_NULL(cls);99src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;");100CHECK_NULL(src_valueID);101src_typeID = (*env)->GetFieldID(env, cls, "type", "I");102CHECK_NULL(src_typeID);103104/* SendFailed */105cls = (*env)->FindClass(env, SEND_FAILED_CLASS);106CHECK_NULL(cls);107ssf_class = (*env)->NewGlobalRef(env, cls);108CHECK_NULL(ssf_class);109ssf_ctrID = (*env)->GetMethodID(env, cls, "<init>",110"(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V");111CHECK_NULL(ssf_ctrID);112113/* AssociationChange */114cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS);115CHECK_NULL(cls);116sac_class = (*env)->NewGlobalRef(env, cls);117CHECK_NULL(sac_class);118sac_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(IIII)V");119CHECK_NULL(sac_ctrID);120121/* PeerAddrChange */122cls = (*env)->FindClass(env, PEER_CHANGE_CLASS);123CHECK_NULL(cls);124spc_class = (*env)->NewGlobalRef(env, cls);125CHECK_NULL(spc_class);126spc_ctrID = (*env)->GetMethodID(env, cls, "<init>",127"(ILjava/net/SocketAddress;I)V");128CHECK_NULL(spc_ctrID);129130/* Shutdown */131cls = (*env)->FindClass(env, SHUTDOWN_CLASS);132CHECK_NULL(cls);133ss_class = (*env)->NewGlobalRef(env, cls);134CHECK_NULL(ss_class);135ss_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(I)V");136CHECK_NULL(ss_ctrID);137}138139void getControlData140(struct msghdr* msg, struct controlData* cdata) {141struct cmsghdr* cmsg;142143for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {144if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) {145struct sctp_sndrcvinfo *sri;146147sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg);148cdata->assocId = sri->sinfo_assoc_id;149cdata->streamNumber = sri->sinfo_stream;150cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE :151JNI_FALSE;152cdata->ppid = ntohl(sri->sinfo_ppid);153154return;155}156}157return;158}159160void setControlData161(struct msghdr* msg, struct controlData* cdata) {162struct cmsghdr* cmsg;163struct sctp_sndrcvinfo *sri;164165cmsg = CMSG_FIRSTHDR(msg);166cmsg->cmsg_level = IPPROTO_SCTP;167cmsg->cmsg_type = SCTP_SNDRCV;168cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));169170/* Initialize the payload */171sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);172memset(sri, 0, sizeof (*sri));173174if (cdata->streamNumber > 0) {175sri->sinfo_stream = cdata->streamNumber;176}177if (cdata->assocId > 0) {178sri->sinfo_assoc_id = cdata->assocId;179}180if (cdata->unordered == JNI_TRUE) {181sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED;182}183184if (cdata->ppid > 0) {185sri->sinfo_ppid = htonl(cdata->ppid);186}187188/* Sum of the length of all control messages in the buffer. */189msg->msg_controllen = cmsg->cmsg_len;190}191192// TODO: test: can create send failed without any data? if so need to193// update API so that buffer can be null if no data.194void handleSendFailed195(JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf,196int read, jboolean isEOR, struct sockaddr* sap) {197jobject bufferObj = NULL, resultObj, isaObj;198char *addressP;199struct sctp_sndrcvinfo *sri;200int remaining, dataLength;201202/* the actual undelivered message data is directly after the ssf */203int dataOffset = sizeof(struct sctp_send_failed);204205sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info;206207/* the number of bytes remaining to be read in the sctp_send_failed notif*/208remaining = ssf->ssf_length - read;209210/* the size of the actual undelivered message */211dataLength = ssf->ssf_length - dataOffset;212213/* retrieved address from sockaddr */214isaObj = SockAddrToInetSocketAddress(env, sap);215CHECK_NULL(isaObj);216217/* data retrieved from sff_data */218if (dataLength > 0) {219struct iovec iov[1];220struct msghdr msg[1];221int rv, alreadyRead;222char *dataP = (char*) ssf;223dataP += dataOffset;224225if ((addressP = malloc(dataLength)) == NULL) {226JNU_ThrowOutOfMemoryError(env, "handleSendFailed");227return;228}229230memset(msg, 0, sizeof (*msg));231msg->msg_iov = iov;232msg->msg_iovlen = 1;233234bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength);235CHECK_NULL(bufferObj);236237alreadyRead = read - dataOffset;238if (alreadyRead > 0) {239memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead);240iov->iov_base = addressP + alreadyRead;241iov->iov_len = dataLength - alreadyRead;242} else {243iov->iov_base = addressP;244iov->iov_len = dataLength;245}246247if (remaining > 0) {248if ((rv = recvmsg(fd, msg, 0)) < 0) {249handleSocketError(env, errno);250return;251}252253if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) {254//TODO: assert false: "should not reach here";255return;256}257// TODO: Set and document (in API) buffers position.258}259}260261/* create SendFailed */262resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id,263isaObj, bufferObj, ssf->ssf_error, sri->sinfo_stream);264CHECK_NULL(resultObj);265(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);266(*env)->SetIntField(env, resultContainerObj, src_typeID,267sun_nio_ch_sctp_ResultContainer_SEND_FAILED);268}269270void handleAssocChange271(JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) {272jobject resultObj;273int state = 0;274275switch (sac->sac_state) {276case SCTP_COMM_UP :277state = sun_nio_ch_sctp_AssociationChange_SCTP_COMM_UP;278break;279case SCTP_COMM_LOST :280state = sun_nio_ch_sctp_AssociationChange_SCTP_COMM_LOST;281break;282case SCTP_RESTART :283state = sun_nio_ch_sctp_AssociationChange_SCTP_RESTART;284break;285case SCTP_SHUTDOWN_COMP :286state = sun_nio_ch_sctp_AssociationChange_SCTP_SHUTDOWN;287break;288case SCTP_CANT_STR_ASSOC :289state = sun_nio_ch_sctp_AssociationChange_SCTP_CANT_START;290}291292/* create AssociationChange */293resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id,294state, sac->sac_outbound_streams, sac->sac_inbound_streams);295CHECK_NULL(resultObj);296(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);297(*env)->SetIntField(env, resultContainerObj, src_typeID,298sun_nio_ch_sctp_ResultContainer_ASSOCIATION_CHANGED);299}300301void handleShutdown302(JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) {303/* create Shutdown */304jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id);305CHECK_NULL(resultObj);306(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);307(*env)->SetIntField(env, resultContainerObj, src_typeID,308sun_nio_ch_sctp_ResultContainer_SHUTDOWN);309}310311void handlePeerAddrChange312(JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) {313int event = 0;314jobject addressObj, resultObj;315unsigned int state = spc->spc_state;316317switch (state) {318case SCTP_ADDR_AVAILABLE :319event = sun_nio_ch_sctp_PeerAddrChange_SCTP_ADDR_AVAILABLE;320break;321case SCTP_ADDR_UNREACHABLE :322event = sun_nio_ch_sctp_PeerAddrChange_SCTP_ADDR_UNREACHABLE;323break;324case SCTP_ADDR_REMOVED :325event = sun_nio_ch_sctp_PeerAddrChange_SCTP_ADDR_REMOVED;326break;327case SCTP_ADDR_ADDED :328event = sun_nio_ch_sctp_PeerAddrChange_SCTP_ADDR_ADDED;329break;330case SCTP_ADDR_MADE_PRIM :331event = sun_nio_ch_sctp_PeerAddrChange_SCTP_ADDR_MADE_PRIM;332#ifdef __linux__ /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */333break;334case SCTP_ADDR_CONFIRMED :335event = sun_nio_ch_sctp_PeerAddrChange_SCTP_ADDR_CONFIRMED;336#endif /* __linux__ */337}338339addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr);340CHECK_NULL(addressObj);341342/* create PeerAddressChanged */343resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id,344addressObj, event);345CHECK_NULL(resultObj);346(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);347(*env)->SetIntField(env, resultContainerObj, src_typeID,348sun_nio_ch_sctp_ResultContainer_PEER_ADDRESS_CHANGED);349}350351void handleUninteresting352(union sctp_notification *snp) {353//fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type);354}355356/**357* Handle notifications from the SCTP stack.358* Returns JNI_TRUE if the notification is one that is of interest to the359* Java API, otherwise JNI_FALSE.360*/361jboolean handleNotification362(JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp,363int read, jboolean isEOR, struct sockaddr* sap) {364switch (snp->sn_header.sn_type) {365case SCTP_SEND_FAILED:366handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed,367read, isEOR, sap);368return JNI_TRUE;369case SCTP_ASSOC_CHANGE:370handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change);371return JNI_TRUE;372case SCTP_SHUTDOWN_EVENT:373handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event);374return JNI_TRUE;375case SCTP_PEER_ADDR_CHANGE:376handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change);377return JNI_TRUE;378default :379/* the Java API is not interested in this event, maybe we are? */380handleUninteresting(snp);381}382return JNI_FALSE;383}384385void handleMessage386(JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read,387jboolean isEOR, struct sockaddr* sap) {388jobject isa, resultObj;389struct controlData cdata[1];390391if (read == 0) {392/* we reached EOF */393read = -1;394}395396isa = SockAddrToInetSocketAddress(env, sap);397CHECK_NULL(isa);398getControlData(msg, cdata);399400/* create MessageInfoImpl */401resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId,402isa, read, cdata->streamNumber,403isEOR ? JNI_TRUE : JNI_FALSE,404cdata->unordered, cdata->ppid);405CHECK_NULL(resultObj);406(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);407(*env)->SetIntField(env, resultContainerObj, src_typeID,408sun_nio_ch_sctp_ResultContainer_MESSAGE);409}410411/*412* Class: sun_nio_ch_sctp_SctpChannelImpl413* Method: receive0414* Signature: (ILsun/nio/ch/sctp/ResultContainer;JIZ)I415*/416JNIEXPORT jint JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_receive0417(JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj,418jlong address, jint length, jboolean peek) {419SOCKADDR sa;420int sa_len = sizeof(sa);421ssize_t rv = 0;422jlong *addr = jlong_to_ptr(address);423struct iovec iov[1];424struct msghdr msg[1];425char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];426int flags = peek == JNI_TRUE ? MSG_PEEK : 0;427428/* Set up the msghdr structure for receiving */429memset(msg, 0, sizeof (*msg));430msg->msg_name = &sa;431msg->msg_namelen = sa_len;432iov->iov_base = addr;433iov->iov_len = length;434msg->msg_iov = iov;435msg->msg_iovlen = 1;436msg->msg_control = cbuf;437msg->msg_controllen = sizeof(cbuf);438msg->msg_flags = 0;439440do {441if ((rv = recvmsg(fd, msg, flags)) < 0) {442if (errno == EWOULDBLOCK) {443return IOS_UNAVAILABLE;444} else if (errno == EINTR) {445return IOS_INTERRUPTED;446447#ifdef __linux__448} else if (errno == ENOTCONN) {449/* ENOTCONN when EOF reached */450rv = 0;451/* there will be no control data */452msg->msg_controllen = 0;453#endif /* __linux__ */454455} else {456handleSocketError(env, errno);457return 0;458}459}460461if (msg->msg_flags & MSG_NOTIFICATION) {462char *bufp = (char*)addr;463union sctp_notification *snp;464jboolean allocated = JNI_FALSE;465466if (!(msg->msg_flags & MSG_EOR) && length < SCTP_NOTIFICATION_SIZE) {467char* newBuf;468int rvSAVE = rv;469470if ((newBuf = malloc(SCTP_NOTIFICATION_SIZE)) == NULL) {471JNU_ThrowOutOfMemoryError(env, "Out of native heap space.");472return -1;473}474allocated = JNI_TRUE;475476memcpy(newBuf, addr, rv);477iov->iov_base = newBuf + rv;478iov->iov_len = SCTP_NOTIFICATION_SIZE - rv;479if ((rv = recvmsg(fd, msg, flags)) < 0) {480handleSocketError(env, errno);481return 0;482}483bufp = newBuf;484rv += rvSAVE;485}486#ifdef __sparc487else if ((intptr_t)addr & 0x3) {488/* the given buffer is not 4 byte aligned */489char* newBuf;490if ((newBuf = malloc(SCTP_NOTIFICATION_SIZE)) == NULL) {491JNU_ThrowOutOfMemoryError(env, "Out of native heap space.");492return -1;493}494allocated = JNI_TRUE;495496memcpy(newBuf, addr, rv);497bufp = newBuf;498}499#endif500snp = (union sctp_notification *) bufp;501if (handleNotification(env, fd, resultContainerObj, snp, rv,502(msg->msg_flags & MSG_EOR),503(struct sockaddr*)&sa ) == JNI_TRUE) {504/* We have received a notification that is of interest to505to the Java API. The appropriate notification will be506set in the result container. */507if (allocated == JNI_TRUE) {508free(bufp);509}510return 0;511}512513if (allocated == JNI_TRUE) {514free(bufp);515}516517// set iov back to addr, and reset msg_controllen518iov->iov_base = addr;519iov->iov_len = length;520msg->msg_control = cbuf;521msg->msg_controllen = sizeof(cbuf);522}523} while (msg->msg_flags & MSG_NOTIFICATION);524525handleMessage(env, resultContainerObj, msg, rv,526(msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa);527return rv;528}529530/*531* Class: sun_nio_ch_sctp_SctpChannelImpl532* Method: send0533* Signature: (IJILjava/net/InetAddress;IIIZI)I534*/535JNIEXPORT jint JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_send0536(JNIEnv *env, jclass klass, jint fd, jlong address, jint length,537jobject targetAddress, jint targetPort, jint assocId, jint streamNumber,538jboolean unordered, jint ppid) {539SOCKADDR sa;540int sa_len = sizeof(sa);541ssize_t rv = 0;542jlong *addr = jlong_to_ptr(address);543struct iovec iov[1];544struct msghdr msg[1];545int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));546char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];547struct controlData cdata[1];548549/* SctpChannel:550* targetAddress may contain the preferred address or NULL to use primary,551* assocId will always be -1552* SctpMultiChannell:553* Setup new association, targetAddress will contain address, assocId = -1554* Association already existing, assocId != -1, targetAddress = preferred addr555*/556if (targetAddress != NULL /*&& assocId <= 0*/) {557if (NET_InetAddressToSockaddr(env, targetAddress, targetPort,558(struct sockaddr *)&sa,559&sa_len, JNI_TRUE) != 0) {560return IOS_THROWN;561}562} else {563memset(&sa, '\x0', sa_len);564sa_len = 0;565}566567/* Set up the msghdr structure for sending */568memset(msg, 0, sizeof (*msg));569memset(cbuf, 0, cbuf_size);570msg->msg_name = &sa;571msg->msg_namelen = sa_len;572iov->iov_base = addr;573iov->iov_len = length;574msg->msg_iov = iov;575msg->msg_iovlen = 1;576msg->msg_control = cbuf;577msg->msg_controllen = cbuf_size;578msg->msg_flags = 0;579580cdata->streamNumber = streamNumber;581cdata->assocId = assocId;582cdata->unordered = unordered;583cdata->ppid = ppid;584setControlData(msg, cdata);585586if ((rv = sendmsg(fd, msg, 0)) < 0) {587if (errno == EWOULDBLOCK) {588return IOS_UNAVAILABLE;589} else if (errno == EINTR) {590return IOS_INTERRUPTED;591} else if (errno == EPIPE) {592JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",593"Socket is shutdown for writing");594} else {595handleSocketError(env, errno);596return 0;597}598}599600return rv;601}602603/*604* Class: sun_nio_ch_sctp_SctpChannelImpl605* Method: checkConnect606* Signature: (Ljava/io/FileDescriptor;ZZ)I607*/608JNIEXPORT jint JNICALL Java_sun_nio_ch_sctp_SctpChannelImpl_checkConnect609(JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) {610return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this,611fdo, block, ready);612}613614615