#define _XPG4_2
#define __EXTENSIONS__
#include <assert.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stropts.h>
#include <stdio.h>
#include <strings.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
union sockaddr_storage_v6 {
struct sockaddr_in in;
struct sockaddr_in6 in6;
};
int
sctp_bindx(int sock, void *addrs, int addrcnt, int flags)
{
socklen_t sz;
if (addrs == NULL || addrcnt == 0) {
errno = EINVAL;
return (-1);
}
switch (((struct sockaddr *)addrs)->sa_family) {
case AF_INET:
sz = sizeof (struct sockaddr_in);
break;
case AF_INET6:
sz = sizeof (struct sockaddr_in6);
break;
default:
errno = EAFNOSUPPORT;
return (-1);
}
switch (flags) {
case SCTP_BINDX_ADD_ADDR:
return (setsockopt(sock, IPPROTO_SCTP, SCTP_ADD_ADDR, addrs,
sz * addrcnt));
case SCTP_BINDX_REM_ADDR:
return (setsockopt(sock, IPPROTO_SCTP, SCTP_REM_ADDR, addrs,
sz * addrcnt));
default:
errno = EINVAL;
return (-1);
}
}
int
sctp_getpaddrs(int sock, sctp_assoc_t id, void **addrs)
{
uint32_t naddrs;
socklen_t bufsz;
struct sctpopt opt;
if (addrs == NULL) {
errno = EINVAL;
return (-1);
}
*addrs = NULL;
opt.sopt_aid = id;
opt.sopt_name = SCTP_GET_NPADDRS;
opt.sopt_val = (caddr_t)&naddrs;
opt.sopt_len = sizeof (naddrs);
if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
return (-1);
}
if (naddrs == 0)
return (0);
bufsz = sizeof (union sockaddr_storage_v6) * naddrs;
if ((*addrs = malloc(bufsz)) == NULL) {
return (-1);
}
opt.sopt_name = SCTP_GET_PADDRS;
opt.sopt_val = *addrs;
opt.sopt_len = bufsz;
if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
free(*addrs);
*addrs = NULL;
return (-1);
}
switch (((struct sockaddr *)*addrs)->sa_family) {
case AF_INET:
naddrs = opt.sopt_len / sizeof (struct sockaddr_in);
break;
case AF_INET6:
naddrs = opt.sopt_len / sizeof (struct sockaddr_in6);
break;
}
return (naddrs);
}
void
sctp_freepaddrs(void *addrs)
{
free(addrs);
}
int
sctp_getladdrs(int sock, sctp_assoc_t id, void **addrs)
{
uint32_t naddrs;
socklen_t bufsz;
struct sctpopt opt;
if (addrs == NULL) {
errno = EINVAL;
return (-1);
}
*addrs = NULL;
opt.sopt_aid = id;
opt.sopt_name = SCTP_GET_NLADDRS;
opt.sopt_val = (caddr_t)&naddrs;
opt.sopt_len = sizeof (naddrs);
if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
return (-1);
}
if (naddrs == 0)
return (0);
bufsz = sizeof (union sockaddr_storage_v6) * naddrs;
if ((*addrs = malloc(bufsz)) == NULL) {
return (-1);
}
opt.sopt_name = SCTP_GET_LADDRS;
opt.sopt_val = *addrs;
opt.sopt_len = bufsz;
if (ioctl(sock, SIOCSCTPGOPT, &opt) == -1) {
free(*addrs);
*addrs = NULL;
return (-1);
}
switch (((struct sockaddr *)*addrs)->sa_family) {
case AF_INET:
naddrs = opt.sopt_len / sizeof (struct sockaddr_in);
break;
case AF_INET6:
naddrs = opt.sopt_len / sizeof (struct sockaddr_in6);
break;
}
return (naddrs);
}
void
sctp_freeladdrs(void *addrs)
{
free(addrs);
}
int
sctp_opt_info(int sock, sctp_assoc_t id, int opt, void *arg, socklen_t *len)
{
struct sctpopt sopt;
sopt.sopt_aid = id;
sopt.sopt_name = opt;
sopt.sopt_val = arg;
sopt.sopt_len = *len;
if (ioctl(sock, SIOCSCTPGOPT, &sopt) == -1) {
return (-1);
}
*len = sopt.sopt_len;
return (0);
}
int
sctp_peeloff(int sock, sctp_assoc_t id)
{
int fd;
fd = id;
if (ioctl(sock, SIOCSCTPPEELOFF, &fd) == -1) {
return (-1);
}
return (fd);
}
ssize_t
sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags)
{
struct msghdr hdr;
struct iovec iov;
struct cmsghdr *cmsg;
char cinmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
int err;
hdr.msg_name = from;
hdr.msg_namelen = (fromlen != NULL) ? *fromlen : 0;
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
if (sinfo != NULL) {
hdr.msg_control = (void *)_CMSG_HDR_ALIGN(cinmsg);
hdr.msg_controllen = sizeof (cinmsg) -
(_CMSG_HDR_ALIGN(cinmsg) - (uintptr_t)cinmsg);
} else {
hdr.msg_control = NULL;
hdr.msg_controllen = 0;
}
iov.iov_base = msg;
iov.iov_len = len;
err = recvmsg(s, &hdr, msg_flags == NULL ? 0 : *msg_flags);
if (err == -1) {
return (-1);
}
if (fromlen != NULL) {
*fromlen = hdr.msg_namelen;
}
if (msg_flags != NULL) {
*msg_flags = hdr.msg_flags;
}
if (sinfo != NULL) {
for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_SCTP &&
cmsg->cmsg_type == SCTP_SNDRCV) {
bcopy(CMSG_DATA(cmsg), sinfo, sizeof (*sinfo));
break;
}
}
}
return (err);
}
static ssize_t
sctp_send_common(int s, const void *msg, size_t len, const struct sockaddr *to,
socklen_t tolen, uint32_t ppid, uint32_t sinfo_flags, uint16_t stream_no,
uint32_t timetolive, uint32_t context, sctp_assoc_t aid, int flags)
{
struct msghdr hdr;
struct iovec iov;
struct sctp_sndrcvinfo *sinfo;
struct cmsghdr *cmsg;
char coutmsg[sizeof (*sinfo) + sizeof (*cmsg) + _CMSG_HDR_ALIGNMENT];
hdr.msg_name = (caddr_t)to;
hdr.msg_namelen = tolen;
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
hdr.msg_control = (void *)_CMSG_HDR_ALIGN(coutmsg);
hdr.msg_controllen = sizeof (*cmsg) + sizeof (*sinfo);
iov.iov_len = len;
iov.iov_base = (caddr_t)msg;
cmsg = CMSG_FIRSTHDR(&hdr);
cmsg->cmsg_level = IPPROTO_SCTP;
cmsg->cmsg_type = SCTP_SNDRCV;
cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sinfo);
sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
sinfo->sinfo_stream = stream_no;
sinfo->sinfo_ssn = 0;
sinfo->sinfo_flags = sinfo_flags;
sinfo->sinfo_ppid = ppid;
sinfo->sinfo_context = context;
sinfo->sinfo_timetolive = timetolive;
sinfo->sinfo_tsn = 0;
sinfo->sinfo_cumtsn = 0;
sinfo->sinfo_assoc_id = aid;
return (sendmsg(s, &hdr, flags));
}
ssize_t
sctp_send(int s, const void *msg, size_t len,
const struct sctp_sndrcvinfo *sinfo, int flags)
{
if (sinfo == NULL) {
errno = EINVAL;
return (-1);
}
return (sctp_send_common(s, msg, len, NULL, 0, sinfo->sinfo_ppid,
sinfo->sinfo_flags, sinfo->sinfo_stream, sinfo->sinfo_timetolive,
sinfo->sinfo_context, sinfo->sinfo_assoc_id, flags));
}
ssize_t
sctp_sendmsg(int s, const void *msg, size_t len, const struct sockaddr *to,
socklen_t tolen, uint32_t ppid, uint32_t flags, uint16_t stream_no,
uint32_t timetolive, uint32_t context)
{
return (sctp_send_common(s, msg, len, to, tolen, ppid, flags,
stream_no, timetolive, context, 0, 0));
}