Esempio n. 1
0
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);
}
Esempio n. 2
0
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);
}