ssize_t sctp_sendx(int sd, const void *msg, size_t msg_len, struct sockaddr *addrs, int addrcnt, struct sctp_sndrcvinfo *sinfo, int flags) { struct sctp_sndrcvinfo __sinfo; ssize_t ret; int i, cnt, *aa, saved_errno; char *buf; int add_len, len, no_end_cx = 0; struct sockaddr *at; if (addrs == NULL) { errno = EINVAL; return (-1); } #ifdef SYS_sctp_generic_sendmsg if (addrcnt == 1) { socklen_t l; /* * Quick way, we don't need to do a connectx so lets use the * syscall directly. */ l = addrs->sa_len; return (syscall(SYS_sctp_generic_sendmsg, sd, msg, msg_len, addrs, l, sinfo, flags)); } #endif len = sizeof(int); at = addrs; cnt = 0; /* validate all the addresses and get the size */ for (i = 0; i < addrcnt; i++) { if (at->sa_family == AF_INET) { add_len = sizeof(struct sockaddr_in); } else if (at->sa_family == AF_INET6) { add_len = sizeof(struct sockaddr_in6); } else { errno = EINVAL; return (-1); } len += add_len; at = (struct sockaddr *)((caddr_t)at + add_len); cnt++; } /* do we have any? */ if (cnt == 0) { errno = EINVAL; return (-1); } buf = malloc(len); if (buf == NULL) { return (-1); } aa = (int *)buf; *aa = cnt; aa++; memcpy((caddr_t)aa, addrs, (len - sizeof(int))); ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_DELAYED, (void *)buf, (socklen_t) len); free(buf); if (ret != 0) { if (errno == EALREADY) { no_end_cx = 1; goto continue_send; } return (ret); } continue_send: if (sinfo == NULL) { sinfo = &__sinfo; memset(&__sinfo, 0, sizeof(__sinfo)); } sinfo->sinfo_assoc_id = sctp_getassocid(sd, addrs); if (sinfo->sinfo_assoc_id == 0) { printf("Huh, can't get associd? TSNH!\n"); (void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs, (socklen_t) addrs->sa_len); errno = ENOENT; return (-1); } ret = sctp_send(sd, msg, msg_len, sinfo, flags); saved_errno = errno; if (no_end_cx == 0) (void)setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X_COMPLETE, (void *)addrs, (socklen_t) addrs->sa_len); errno = saved_errno; return (ret); }
ssize_t sctp_sendv(int sd, const struct iovec *iov, int iovcnt, struct sockaddr *addrs, int addrcnt, void *info, socklen_t infolen, unsigned int infotype, int flags) { ssize_t ret; int i; socklen_t addr_len; struct msghdr msg; in_port_t port; struct sctp_sendv_spa *spa_info; struct cmsghdr *cmsg; char *cmsgbuf; struct sockaddr *addr; struct sockaddr_in *addr_in; struct sockaddr_in6 *addr_in6; sctp_assoc_t *assoc_id; if ((addrcnt < 0) || (iovcnt < 0) || ((addrs == NULL) && (addrcnt > 0)) || ((addrs != NULL) && (addrcnt == 0)) || ((iov == NULL) && (iovcnt > 0)) || ((iov != NULL) && (iovcnt == 0))) { errno = EINVAL; return (-1); } cmsgbuf = malloc(CMSG_SPACE(sizeof(struct sctp_sndinfo)) + CMSG_SPACE(sizeof(struct sctp_prinfo)) + CMSG_SPACE(sizeof(struct sctp_authinfo)) + (size_t)addrcnt * CMSG_SPACE(sizeof(struct in6_addr))); if (cmsgbuf == NULL) { errno = ENOMEM; return (-1); } assoc_id = NULL; msg.msg_control = cmsgbuf; msg.msg_controllen = 0; cmsg = (struct cmsghdr *)cmsgbuf; switch (infotype) { case SCTP_SENDV_NOINFO: if ((infolen != 0) || (info != NULL)) { free(cmsgbuf); errno = EINVAL; return (-1); } break; case SCTP_SENDV_SNDINFO: if ((info == NULL) || (infolen < sizeof(struct sctp_sndinfo))) { free(cmsgbuf); errno = EINVAL; return (-1); } cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_SNDINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo)); memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_sndinfo)); msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo))); assoc_id = &(((struct sctp_sndinfo *)info)->snd_assoc_id); break; case SCTP_SENDV_PRINFO: if ((info == NULL) || (infolen < sizeof(struct sctp_prinfo))) { free(cmsgbuf); errno = EINVAL; return (-1); } cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_PRINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo)); memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_prinfo)); msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo))); break; case SCTP_SENDV_AUTHINFO: if ((info == NULL) || (infolen < sizeof(struct sctp_authinfo))) { free(cmsgbuf); errno = EINVAL; return (-1); } cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_AUTHINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo)); memcpy(CMSG_DATA(cmsg), info, sizeof(struct sctp_authinfo)); msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo))); break; case SCTP_SENDV_SPA: if ((info == NULL) || (infolen < sizeof(struct sctp_sendv_spa))) { free(cmsgbuf); errno = EINVAL; return (-1); } spa_info = (struct sctp_sendv_spa *)info; if (spa_info->sendv_flags & SCTP_SEND_SNDINFO_VALID) { cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_SNDINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo)); memcpy(CMSG_DATA(cmsg), &spa_info->sendv_sndinfo, sizeof(struct sctp_sndinfo)); msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_sndinfo))); assoc_id = &(spa_info->sendv_sndinfo.snd_assoc_id); } if (spa_info->sendv_flags & SCTP_SEND_PRINFO_VALID) { cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_PRINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo)); memcpy(CMSG_DATA(cmsg), &spa_info->sendv_prinfo, sizeof(struct sctp_prinfo)); msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_prinfo))); } if (spa_info->sendv_flags & SCTP_SEND_AUTHINFO_VALID) { cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_AUTHINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_authinfo)); memcpy(CMSG_DATA(cmsg), &spa_info->sendv_authinfo, sizeof(struct sctp_authinfo)); msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_authinfo)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct sctp_authinfo))); } break; default: free(cmsgbuf); errno = EINVAL; return (-1); } addr = addrs; msg.msg_name = NULL; msg.msg_namelen = 0; for (i = 0; i < addrcnt; i++) { switch (addr->sa_family) { case AF_INET: addr_len = (socklen_t) sizeof(struct sockaddr_in); addr_in = (struct sockaddr_in *)addr; if (addr_in->sin_len != addr_len) { free(cmsgbuf); errno = EINVAL; return (-1); } if (i == 0) { port = addr_in->sin_port; } else { if (port == addr_in->sin_port) { cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_DSTADDRV4; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); memcpy(CMSG_DATA(cmsg), &addr_in->sin_addr, sizeof(struct in_addr)); msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in_addr))); } else { free(cmsgbuf); errno = EINVAL; return (-1); } } break; case AF_INET6: addr_len = (socklen_t) sizeof(struct sockaddr_in6); addr_in6 = (struct sockaddr_in6 *)addr; if (addr_in6->sin6_len != addr_len) { free(cmsgbuf); errno = EINVAL; return (-1); } if (i == 0) { port = addr_in6->sin6_port; } else { if (port == addr_in6->sin6_port) { cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_DSTADDRV6; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr)); memcpy(CMSG_DATA(cmsg), &addr_in6->sin6_addr, sizeof(struct in6_addr)); msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_addr)); cmsg = (struct cmsghdr *)((caddr_t)cmsg + CMSG_SPACE(sizeof(struct in6_addr))); } else { free(cmsgbuf); errno = EINVAL; return (-1); } } break; default: free(cmsgbuf); errno = EINVAL; return (-1); } if (i == 0) { msg.msg_name = addr; msg.msg_namelen = addr_len; } addr = (struct sockaddr *)((caddr_t)addr + addr_len); } if (msg.msg_controllen == 0) { msg.msg_control = NULL; } msg.msg_iov = (struct iovec *)iov; msg.msg_iovlen = iovcnt; msg.msg_flags = 0; ret = sendmsg(sd, &msg, flags); free(cmsgbuf); if ((ret >= 0) && (addrs != NULL) && (assoc_id != NULL)) { *assoc_id = sctp_getassocid(sd, addrs); } return (ret); }