+ if (out != NULL) {
+ clear_socket_error();
+
+ do {
+ memset(&data->rcvinfo, 0, sizeof(data->rcvinfo));
+ iov.iov_base = out;
+ iov.iov_len = outl;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = 512;
+ msg.msg_flags = 0;
+ n = recvmsg(b->num, &msg, 0);
+
+ if (n <= 0) {
+ if (n < 0)
+ ret = n;
+ break;
+ }
+
+ if (msg.msg_controllen > 0) {
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != IPPROTO_SCTP)
+ continue;
+# ifdef SCTP_RCVINFO
+ if (cmsg->cmsg_type == SCTP_RCVINFO) {
+ struct sctp_rcvinfo *rcvinfo;
+
+ rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
+ data->rcvinfo.rcv_sid = rcvinfo->rcv_sid;
+ data->rcvinfo.rcv_ssn = rcvinfo->rcv_ssn;
+ data->rcvinfo.rcv_flags = rcvinfo->rcv_flags;
+ data->rcvinfo.rcv_ppid = rcvinfo->rcv_ppid;
+ data->rcvinfo.rcv_tsn = rcvinfo->rcv_tsn;
+ data->rcvinfo.rcv_cumtsn = rcvinfo->rcv_cumtsn;
+ data->rcvinfo.rcv_context = rcvinfo->rcv_context;
+ }
+# endif
+# ifdef SCTP_SNDRCV
+ if (cmsg->cmsg_type == SCTP_SNDRCV) {
+ struct sctp_sndrcvinfo *sndrcvinfo;
+
+ sndrcvinfo =
+ (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ data->rcvinfo.rcv_sid = sndrcvinfo->sinfo_stream;
+ data->rcvinfo.rcv_ssn = sndrcvinfo->sinfo_ssn;
+ data->rcvinfo.rcv_flags = sndrcvinfo->sinfo_flags;
+ data->rcvinfo.rcv_ppid = sndrcvinfo->sinfo_ppid;
+ data->rcvinfo.rcv_tsn = sndrcvinfo->sinfo_tsn;
+ data->rcvinfo.rcv_cumtsn = sndrcvinfo->sinfo_cumtsn;
+ data->rcvinfo.rcv_context = sndrcvinfo->sinfo_context;
+ }
+# endif
+ }
+ }
+
+ if (msg.msg_flags & MSG_NOTIFICATION) {
+ snp = (union sctp_notification *)out;
+ if (snp->sn_header.sn_type == SCTP_SENDER_DRY_EVENT) {
+# ifdef SCTP_EVENT
+ struct sctp_event event;
+# else
+ struct sctp_event_subscribe event;
+ socklen_t eventsize;
+# endif
+ /*
+ * If a message has been delayed until the socket is dry,
+ * it can be sent now.
+ */
+ if (data->saved_message.length > 0) {
+ i = dgram_sctp_write(data->saved_message.bio,
+ data->saved_message.data,
+ data->saved_message.length);
+ if (i < 0) {
+ ret = i;
+ break;
+ }
+ OPENSSL_free(data->saved_message.data);
+ data->saved_message.data = NULL;
+ data->saved_message.length = 0;
+ }
+
+ /* disable sender dry event */
+# ifdef SCTP_EVENT
+ memset(&event, 0, sizeof(event));
+ event.se_assoc_id = 0;
+ event.se_type = SCTP_SENDER_DRY_EVENT;
+ event.se_on = 0;
+ i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
+ sizeof(struct sctp_event));
+ if (i < 0) {
+ ret = i;
+ break;
+ }
+# else
+ eventsize = sizeof(struct sctp_event_subscribe);
+ i = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+ &eventsize);
+ if (i < 0) {
+ ret = i;
+ break;
+ }
+
+ event.sctp_sender_dry_event = 0;
+
+ i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+ sizeof(struct sctp_event_subscribe));
+ if (i < 0) {
+ ret = i;
+ break;
+ }
+# endif
+ }
+# ifdef SCTP_AUTHENTICATION_EVENT
+ if (snp->sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
+ dgram_sctp_handle_auth_free_key_event(b, snp);
+# endif
+
+ if (data->handle_notifications != NULL)
+ data->handle_notifications(b, data->notification_context,
+ (void *)out);
+
+ memset(out, 0, outl);
+ } else
+ ret += n;
+ }
+ while ((msg.msg_flags & MSG_NOTIFICATION) && (msg.msg_flags & MSG_EOR)
+ && (ret < outl));
+
+ if (ret > 0 && !(msg.msg_flags & MSG_EOR)) {
+ /* Partial message read, this should never happen! */
+
+ /*
+ * The buffer was too small, this means the peer sent a message
+ * that was larger than allowed.
+ */
+ if (ret == outl)
+ return -1;
+
+ /*
+ * Test if socket buffer can handle max record size (2^14 + 2048
+ * + 13)
+ */
+ optlen = (socklen_t) sizeof(int);
+ ret = getsockopt(b->num, SOL_SOCKET, SO_RCVBUF, &optval, &optlen);
+ if (ret >= 0)
+ OPENSSL_assert(optval >= 18445);
+
+ /*
+ * Test if SCTP doesn't partially deliver below max record size
+ * (2^14 + 2048 + 13)
+ */
+ optlen = (socklen_t) sizeof(int);
+ ret =
+ getsockopt(b->num, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT,
+ &optval, &optlen);
+ if (ret >= 0)
+ OPENSSL_assert(optval >= 18445);
+
+ /*
+ * Partially delivered notification??? Probably a bug....
+ */
+ OPENSSL_assert(!(msg.msg_flags & MSG_NOTIFICATION));
+
+ /*
+ * Everything seems ok till now, so it's most likely a message
+ * dropped by PR-SCTP.
+ */
+ memset(out, 0, outl);
+ BIO_set_retry_read(b);
+ return -1;
+ }
+
+ BIO_clear_retry_flags(b);
+ if (ret < 0) {
+ if (BIO_dgram_should_retry(ret)) {
+ BIO_set_retry_read(b);
+ data->_errno = get_last_socket_error();
+ }
+ }
+
+ /* Test if peer uses SCTP-AUTH before continuing */
+ if (!data->peer_auth_tested) {
+ int ii, auth_data = 0, auth_forward = 0;
+ unsigned char *p;
+ struct sctp_authchunks *authchunks;
+
+ optlen =
+ (socklen_t) (sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t));
+ authchunks = OPENSSL_malloc(optlen);
+ if (authchunks == NULL) {
+ BIOerr(BIO_F_DGRAM_SCTP_READ, ERR_R_MALLOC_FAILURE);
+ return -1;
+ }
+ memset(authchunks, 0, optlen);
+ ii = getsockopt(b->num, IPPROTO_SCTP, SCTP_PEER_AUTH_CHUNKS,
+ authchunks, &optlen);
+
+ if (ii >= 0)
+ for (p = (unsigned char *)authchunks->gauth_chunks;
+ p < (unsigned char *)authchunks + optlen;
+ p += sizeof(uint8_t)) {
+ if (*p == OPENSSL_SCTP_DATA_CHUNK_TYPE)
+ auth_data = 1;
+ if (*p == OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE)
+ auth_forward = 1;
+ }
+
+ OPENSSL_free(authchunks);
+
+ if (!auth_data || !auth_forward) {
+ BIOerr(BIO_F_DGRAM_SCTP_READ, BIO_R_CONNECT_ERROR);
+ return -1;
+ }
+
+ data->peer_auth_tested = 1;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * dgram_sctp_write - send message on SCTP socket
+ * @b: BIO to write to
+ * @in: data to send
+ * @inl: amount of bytes in @in to send
+ *
+ * Returns -1 on error or the sent amount of bytes on success
+ */
+static int dgram_sctp_write(BIO *b, const char *in, int inl)
+{
+ int ret;
+ bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+ struct bio_dgram_sctp_sndinfo *sinfo = &(data->sndinfo);
+ struct bio_dgram_sctp_prinfo *pinfo = &(data->prinfo);
+ struct bio_dgram_sctp_sndinfo handshake_sinfo;
+ struct iovec iov[1];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+# if defined(SCTP_SNDINFO) && defined(SCTP_PRINFO)
+ char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
+ CMSG_SPACE(sizeof(struct sctp_prinfo))];
+ struct sctp_sndinfo *sndinfo;
+ struct sctp_prinfo *prinfo;
+# else
+ char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
+ struct sctp_sndrcvinfo *sndrcvinfo;
+# endif
+
+ clear_socket_error();
+
+ /*
+ * If we're send anything else than application data, disable all user
+ * parameters and flags.
+ */
+ if (in[0] != 23) {
+ memset(&handshake_sinfo, 0, sizeof(handshake_sinfo));
+# ifdef SCTP_SACK_IMMEDIATELY
+ handshake_sinfo.snd_flags = SCTP_SACK_IMMEDIATELY;
+# endif
+ sinfo = &handshake_sinfo;
+ }
+
+ /*
+ * If we have to send a shutdown alert message and the socket is not dry
+ * yet, we have to save it and send it as soon as the socket gets dry.
+ */
+ if (data->save_shutdown) {
+ ret = BIO_dgram_sctp_wait_for_dry(b);
+ if (ret < 0) {
+ return -1;
+ }
+ if (ret == 0) {
+ char *tmp;
+ data->saved_message.bio = b;
+ if ((tmp = OPENSSL_malloc(inl)) == NULL) {
+ BIOerr(BIO_F_DGRAM_SCTP_WRITE, ERR_R_MALLOC_FAILURE);
+ return -1;
+ }
+ OPENSSL_free(data->saved_message.data);
+ data->saved_message.data = tmp;
+ memcpy(data->saved_message.data, in, inl);
+ data->saved_message.length = inl;
+ return inl;
+ }
+ }
+
+ iov[0].iov_base = (char *)in;
+ iov[0].iov_len = inl;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (caddr_t) cmsgbuf;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+# if defined(SCTP_SNDINFO) && defined(SCTP_PRINFO)
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
+ sndinfo = (struct sctp_sndinfo *)CMSG_DATA(cmsg);
+ memset(sndinfo, 0, sizeof(*sndinfo));
+ sndinfo->snd_sid = sinfo->snd_sid;
+ sndinfo->snd_flags = sinfo->snd_flags;
+ sndinfo->snd_ppid = sinfo->snd_ppid;
+ sndinfo->snd_context = sinfo->snd_context;
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
+
+ cmsg =
+ (struct cmsghdr *)&cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndinfo))];
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_PRINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
+ prinfo = (struct sctp_prinfo *)CMSG_DATA(cmsg);
+ memset(prinfo, 0, sizeof(*prinfo));
+ prinfo->pr_policy = pinfo->pr_policy;
+ prinfo->pr_value = pinfo->pr_value;
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
+# else
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_level = IPPROTO_SCTP;
+ cmsg->cmsg_type = SCTP_SNDRCV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+ sndrcvinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
+ memset(sndrcvinfo, 0, sizeof(*sndrcvinfo));
+ sndrcvinfo->sinfo_stream = sinfo->snd_sid;
+ sndrcvinfo->sinfo_flags = sinfo->snd_flags;
+# ifdef __FreeBSD__
+ sndrcvinfo->sinfo_flags |= pinfo->pr_policy;
+# endif
+ sndrcvinfo->sinfo_ppid = sinfo->snd_ppid;
+ sndrcvinfo->sinfo_context = sinfo->snd_context;
+ sndrcvinfo->sinfo_timetolive = pinfo->pr_value;
+ msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
+# endif
+
+ ret = sendmsg(b->num, &msg, 0);
+
+ BIO_clear_retry_flags(b);
+ if (ret <= 0) {
+ if (BIO_dgram_should_retry(ret)) {
+ BIO_set_retry_write(b);
+ data->_errno = get_last_socket_error();
+ }
+ }
+ return (ret);
+}
+
+static long dgram_sctp_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+ long ret = 1;
+ bio_dgram_sctp_data *data = NULL;
+ socklen_t sockopt_len = 0;
+ struct sctp_authkeyid authkeyid;
+ struct sctp_authkey *authkey = NULL;
+
+ data = (bio_dgram_sctp_data *) b->ptr;
+
+ switch (cmd) {
+ case BIO_CTRL_DGRAM_QUERY_MTU:
+ /*
+ * Set to maximum (2^14) and ignore user input to enable transport
+ * protocol fragmentation. Returns always 2^14.
+ */
+ data->mtu = 16384;
+ ret = data->mtu;
+ break;
+ case BIO_CTRL_DGRAM_SET_MTU:
+ /*
+ * Set to maximum (2^14) and ignore input to enable transport
+ * protocol fragmentation. Returns always 2^14.
+ */
+ data->mtu = 16384;
+ ret = data->mtu;
+ break;
+ case BIO_CTRL_DGRAM_SET_CONNECTED:
+ case BIO_CTRL_DGRAM_CONNECT:
+ /* Returns always -1. */
+ ret = -1;
+ break;
+ case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
+ /*
+ * SCTP doesn't need the DTLS timer Returns always 1.
+ */
+ break;
+ case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
+ /*
+ * We allow transport protocol fragmentation so this is irrelevant
+ */
+ ret = 0;
+ break;
+ case BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE:
+ if (num > 0)
+ data->in_handshake = 1;
+ else
+ data->in_handshake = 0;
+
+ ret =
+ setsockopt(b->num, IPPROTO_SCTP, SCTP_NODELAY,
+ &data->in_handshake, sizeof(int));
+ break;
+ case BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY:
+ /*
+ * New shared key for SCTP AUTH. Returns 0 on success, -1 otherwise.
+ */
+
+ /* Get active key */
+ sockopt_len = sizeof(struct sctp_authkeyid);
+ ret =
+ getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, &authkeyid,
+ &sockopt_len);
+ if (ret < 0)
+ break;
+
+ /* Add new key */
+ sockopt_len = sizeof(struct sctp_authkey) + 64 * sizeof(uint8_t);
+ authkey = OPENSSL_malloc(sockopt_len);
+ if (authkey == NULL) {
+ ret = -1;
+ break;
+ }
+ memset(authkey, 0, sockopt_len);
+ authkey->sca_keynumber = authkeyid.scact_keynumber + 1;
+# ifndef __FreeBSD__
+ /*
+ * This field is missing in FreeBSD 8.2 and earlier, and FreeBSD 8.3
+ * and higher work without it.
+ */
+ authkey->sca_keylength = 64;
+# endif
+ memcpy(&authkey->sca_key[0], ptr, 64 * sizeof(uint8_t));
+
+ ret =
+ setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_KEY, authkey,
+ sockopt_len);
+ OPENSSL_free(authkey);
+ authkey = NULL;
+ if (ret < 0)
+ break;
+
+ /* Reset active key */
+ ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
+ &authkeyid, sizeof(struct sctp_authkeyid));
+ if (ret < 0)
+ break;
+
+ break;
+ case BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY:
+ /* Returns 0 on success, -1 otherwise. */
+
+ /* Get active key */
+ sockopt_len = sizeof(struct sctp_authkeyid);
+ ret =
+ getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, &authkeyid,
+ &sockopt_len);
+ if (ret < 0)
+ break;
+
+ /* Set active key */
+ authkeyid.scact_keynumber = authkeyid.scact_keynumber + 1;
+ ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
+ &authkeyid, sizeof(struct sctp_authkeyid));
+ if (ret < 0)
+ break;
+
+ /*
+ * CCS has been sent, so remember that and fall through to check if
+ * we need to deactivate an old key
+ */
+ data->ccs_sent = 1;
+
+ case BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD:
+ /* Returns 0 on success, -1 otherwise. */
+
+ /*
+ * Has this command really been called or is this just a
+ * fall-through?
+ */
+ if (cmd == BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD)
+ data->ccs_rcvd = 1;
+
+ /*
+ * CSS has been both, received and sent, so deactivate an old key
+ */
+ if (data->ccs_rcvd == 1 && data->ccs_sent == 1) {
+ /* Get active key */
+ sockopt_len = sizeof(struct sctp_authkeyid);
+ ret =
+ getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
+ &authkeyid, &sockopt_len);
+ if (ret < 0)
+ break;
+
+ /*
+ * Deactivate key or delete second last key if
+ * SCTP_AUTHENTICATION_EVENT is not available.
+ */
+ authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1;
+# ifdef SCTP_AUTH_DEACTIVATE_KEY
+ sockopt_len = sizeof(struct sctp_authkeyid);
+ ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DEACTIVATE_KEY,
+ &authkeyid, sockopt_len);
+ if (ret < 0)
+ break;
+# endif
+# ifndef SCTP_AUTHENTICATION_EVENT
+ if (authkeyid.scact_keynumber > 0) {
+ authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1;
+ ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY,
+ &authkeyid, sizeof(struct sctp_authkeyid));
+ if (ret < 0)
+ break;
+ }
+# endif
+
+ data->ccs_rcvd = 0;
+ data->ccs_sent = 0;
+ }
+ break;
+ case BIO_CTRL_DGRAM_SCTP_GET_SNDINFO:
+ /* Returns the size of the copied struct. */
+ if (num > (long)sizeof(struct bio_dgram_sctp_sndinfo))
+ num = sizeof(struct bio_dgram_sctp_sndinfo);
+
+ memcpy(ptr, &(data->sndinfo), num);
+ ret = num;
+ break;
+ case BIO_CTRL_DGRAM_SCTP_SET_SNDINFO:
+ /* Returns the size of the copied struct. */
+ if (num > (long)sizeof(struct bio_dgram_sctp_sndinfo))
+ num = sizeof(struct bio_dgram_sctp_sndinfo);
+
+ memcpy(&(data->sndinfo), ptr, num);
+ break;
+ case BIO_CTRL_DGRAM_SCTP_GET_RCVINFO:
+ /* Returns the size of the copied struct. */
+ if (num > (long)sizeof(struct bio_dgram_sctp_rcvinfo))
+ num = sizeof(struct bio_dgram_sctp_rcvinfo);
+
+ memcpy(ptr, &data->rcvinfo, num);
+
+ ret = num;
+ break;
+ case BIO_CTRL_DGRAM_SCTP_SET_RCVINFO:
+ /* Returns the size of the copied struct. */
+ if (num > (long)sizeof(struct bio_dgram_sctp_rcvinfo))
+ num = sizeof(struct bio_dgram_sctp_rcvinfo);
+
+ memcpy(&(data->rcvinfo), ptr, num);
+ break;
+ case BIO_CTRL_DGRAM_SCTP_GET_PRINFO:
+ /* Returns the size of the copied struct. */
+ if (num > (long)sizeof(struct bio_dgram_sctp_prinfo))
+ num = sizeof(struct bio_dgram_sctp_prinfo);
+
+ memcpy(ptr, &(data->prinfo), num);
+ ret = num;
+ break;
+ case BIO_CTRL_DGRAM_SCTP_SET_PRINFO:
+ /* Returns the size of the copied struct. */
+ if (num > (long)sizeof(struct bio_dgram_sctp_prinfo))
+ num = sizeof(struct bio_dgram_sctp_prinfo);
+
+ memcpy(&(data->prinfo), ptr, num);
+ break;
+ case BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN:
+ /* Returns always 1. */
+ if (num > 0)
+ data->save_shutdown = 1;
+ else
+ data->save_shutdown = 0;
+ break;
+
+ default:
+ /*
+ * Pass to default ctrl function to process SCTP unspecific commands
+ */
+ ret = dgram_ctrl(b, cmd, num, ptr);
+ break;
+ }
+ return (ret);
+}
+
+int BIO_dgram_sctp_notification_cb(BIO *b,
+ void (*handle_notifications) (BIO *bio,
+ void
+ *context,
+ void *buf),
+ void *context)
+{
+ bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+
+ if (handle_notifications != NULL) {
+ data->handle_notifications = handle_notifications;
+ data->notification_context = context;
+ } else
+ return -1;
+
+ return 0;
+}
+
+/*
+ * BIO_dgram_sctp_wait_for_dry - Wait for SCTP SENDER_DRY event
+ * @b: The BIO to check for the dry event
+ *
+ * Wait until the peer confirms all packets have been received, and so that
+ * our kernel doesn't have anything to send anymore. This is only received by
+ * the peer's kernel, not the application.
+ *
+ * Returns:
+ * -1 on error
+ * 0 when not dry yet
+ * 1 when dry
+ */
+int BIO_dgram_sctp_wait_for_dry(BIO *b)
+{
+ int is_dry = 0;
+ int sockflags = 0;
+ int n, ret;
+ union sctp_notification snp;
+ struct msghdr msg;
+ struct iovec iov;
+# ifdef SCTP_EVENT
+ struct sctp_event event;
+# else
+ struct sctp_event_subscribe event;
+ socklen_t eventsize;
+# endif
+ bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+
+ /* set sender dry event */
+# ifdef SCTP_EVENT
+ memset(&event, 0, sizeof(event));
+ event.se_assoc_id = 0;
+ event.se_type = SCTP_SENDER_DRY_EVENT;
+ event.se_on = 1;
+ ret =
+ setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
+ sizeof(struct sctp_event));
+# else
+ eventsize = sizeof(struct sctp_event_subscribe);
+ ret = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, &eventsize);
+ if (ret < 0)
+ return -1;
+
+ event.sctp_sender_dry_event = 1;
+
+ ret =
+ setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+ sizeof(struct sctp_event_subscribe));
+# endif
+ if (ret < 0)
+ return -1;
+
+ /* peek for notification */
+ memset(&snp, 0, sizeof(snp));
+ iov.iov_base = (char *)&snp;
+ iov.iov_len = sizeof(union sctp_notification);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ n = recvmsg(b->num, &msg, MSG_PEEK);
+ if (n <= 0) {
+ if ((n < 0) && (get_last_socket_error() != EAGAIN)
+ && (get_last_socket_error() != EWOULDBLOCK))
+ return -1;
+ else
+ return 0;
+ }
+
+ /* if we find a notification, process it and try again if necessary */
+ while (msg.msg_flags & MSG_NOTIFICATION) {
+ memset(&snp, 0, sizeof(snp));
+ iov.iov_base = (char *)&snp;
+ iov.iov_len = sizeof(union sctp_notification);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ n = recvmsg(b->num, &msg, 0);
+ if (n <= 0) {
+ if ((n < 0) && (get_last_socket_error() != EAGAIN)
+ && (get_last_socket_error() != EWOULDBLOCK))
+ return -1;
+ else
+ return is_dry;
+ }
+
+ if (snp.sn_header.sn_type == SCTP_SENDER_DRY_EVENT) {
+ is_dry = 1;
+
+ /* disable sender dry event */
+# ifdef SCTP_EVENT
+ memset(&event, 0, sizeof(event));
+ event.se_assoc_id = 0;
+ event.se_type = SCTP_SENDER_DRY_EVENT;
+ event.se_on = 0;
+ ret =
+ setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
+ sizeof(struct sctp_event));
+# else
+ eventsize = (socklen_t) sizeof(struct sctp_event_subscribe);
+ ret =
+ getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+ &eventsize);
+ if (ret < 0)
+ return -1;
+
+ event.sctp_sender_dry_event = 0;
+
+ ret =
+ setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
+ sizeof(struct sctp_event_subscribe));
+# endif
+ if (ret < 0)
+ return -1;
+ }
+# ifdef SCTP_AUTHENTICATION_EVENT
+ if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
+ dgram_sctp_handle_auth_free_key_event(b, &snp);
+# endif
+
+ if (data->handle_notifications != NULL)
+ data->handle_notifications(b, data->notification_context,
+ (void *)&snp);
+
+ /* found notification, peek again */
+ memset(&snp, 0, sizeof(snp));
+ iov.iov_base = (char *)&snp;
+ iov.iov_len = sizeof(union sctp_notification);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ /* if we have seen the dry already, don't wait */
+ if (is_dry) {
+ sockflags = fcntl(b->num, F_GETFL, 0);
+ fcntl(b->num, F_SETFL, O_NONBLOCK);
+ }
+
+ n = recvmsg(b->num, &msg, MSG_PEEK);
+
+ if (is_dry) {
+ fcntl(b->num, F_SETFL, sockflags);
+ }
+
+ if (n <= 0) {
+ if ((n < 0) && (get_last_socket_error() != EAGAIN)
+ && (get_last_socket_error() != EWOULDBLOCK))
+ return -1;
+ else
+ return is_dry;
+ }
+ }
+
+ /* read anything else */
+ return is_dry;
+}
+
+int BIO_dgram_sctp_msg_waiting(BIO *b)
+{
+ int n, sockflags;
+ union sctp_notification snp;
+ struct msghdr msg;
+ struct iovec iov;
+ bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
+
+ /* Check if there are any messages waiting to be read */
+ do {
+ memset(&snp, 0, sizeof(snp));
+ iov.iov_base = (char *)&snp;
+ iov.iov_len = sizeof(union sctp_notification);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ sockflags = fcntl(b->num, F_GETFL, 0);
+ fcntl(b->num, F_SETFL, O_NONBLOCK);
+ n = recvmsg(b->num, &msg, MSG_PEEK);
+ fcntl(b->num, F_SETFL, sockflags);
+
+ /* if notification, process and try again */
+ if (n > 0 && (msg.msg_flags & MSG_NOTIFICATION)) {
+# ifdef SCTP_AUTHENTICATION_EVENT
+ if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
+ dgram_sctp_handle_auth_free_key_event(b, &snp);
+# endif
+
+ memset(&snp, 0, sizeof(snp));
+ iov.iov_base = (char *)&snp;
+ iov.iov_len = sizeof(union sctp_notification);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ n = recvmsg(b->num, &msg, 0);
+
+ if (data->handle_notifications != NULL)
+ data->handle_notifications(b, data->notification_context,
+ (void *)&snp);
+ }
+
+ } while (n > 0 && (msg.msg_flags & MSG_NOTIFICATION));
+
+ /* Return 1 if there is a message to be read, return 0 otherwise. */
+ if (n > 0)
+ return 1;
+ else
+ return 0;
+}
+
+static int dgram_sctp_puts(BIO *bp, const char *str)
+{
+ int n, ret;
+
+ n = strlen(str);
+ ret = dgram_sctp_write(bp, str, n);
+ return (ret);
+}
+# endif