/*-
* Copyright (c) 2008, 2009, 2023 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andrew Doran.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)uipc_syscalls.c 8.6 (Berkeley) 2/14/95
*/
/* If the caller passed us stuff in mbufs, we must free them. */
to = (mp->msg_flags & MSG_NAMEMBUF) ? mp->msg_name : NULL;
control = (mp->msg_flags & MSG_CONTROLMBUF) ? mp->msg_control : NULL;
iovsz = mp->msg_iovlen * sizeof(struct iovec);
tiov = auio.uio_iov;
for (i = 0; i < auio.uio_iovcnt; i++, tiov++) {
/*
* Writes return ssize_t because -1 is returned on error.
* Therefore, we must restrict the length to SSIZE_MAX to
* avoid garbage return values.
*/
auio.uio_resid += tiov->iov_len;
if (tiov->iov_len > SSIZE_MAX || auio.uio_resid > SSIZE_MAX) {
error = SET_ERROR(EINVAL);
goto bad;
}
}
if (mp->msg_name && to == NULL) {
error = sockargs(&to, mp->msg_name, mp->msg_namelen,
UIO_USERSPACE, MT_SONAME);
if (error)
goto bad;
}
if (mp->msg_control) {
if (mp->msg_controllen < CMSG_ALIGN(sizeof(struct cmsghdr))) {
error = SET_ERROR(EINVAL);
goto bad;
}
if (control == NULL) {
error = sockargs(&control, mp->msg_control,
mp->msg_controllen, UIO_USERSPACE, MT_CONTROL);
if (error)
goto bad;
}
}
if (mp->msg_name)
MCLAIM(to, so->so_mowner);
if (mp->msg_control)
MCLAIM(control, so->so_mowner);
if (to) {
sa = mtod(to, struct sockaddr *);
}
len = auio.uio_resid;
error = (*so->so_send)(so, sa, &auio, NULL, control, flags, l);
/* Protocol is responsible for freeing 'control' */
control = NULL;
if (error) {
if (auio.uio_resid != len && (error == ERESTART ||
error == EINTR || error == EWOULDBLOCK))
error = 0;
if (error == EPIPE && (fp->f_flag & FNOSIGPIPE) == 0 &&
(flags & MSG_NOSIGNAL) == 0) {
mutex_enter(&proc_lock);
psignal(l->l_proc, SIGPIPE);
mutex_exit(&proc_lock);
}
}
if (error == 0)
*retsize = len - auio.uio_resid;
/*
* If we succeeded at least once, return 0.
*/
if (dg)
return 0;
return error;
}
/*
* Adjust for a truncated SCM_RIGHTS control message.
* This means closing any file descriptors that aren't present
* in the returned buffer.
* m is the mbuf holding the (already externalized) SCM_RIGHTS message.
*/
static void
free_rights(struct mbuf *m)
{
struct cmsghdr *cm;
int *fdv;
unsigned int nfds, i;
KASSERT(sizeof(*cm) <= m->m_len);
cm = mtod(m, struct cmsghdr *);
while (control != NULL) {
cmsg = mtod(control, struct cmsghdr *);
if (control == uncopied)
do_free_rights = true;
if (do_free_rights && cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS)
free_rights(control);
next = control->m_next;
m_free(control);
control = next;
}
}
/* Copy socket control/CMSG data to user buffer, frees the mbuf */
int
copyout_msg_control(struct lwp *l, struct msghdr *mp, struct mbuf *control)
{
int i, len, error = 0;
struct cmsghdr *cmsg;
struct mbuf *m;
char *q;
len = mp->msg_controllen;
if (len <= 0 || control == 0) {
mp->msg_controllen = 0;
free_control_mbuf(l, control, control);
return 0;
}
q = (char *)mp->msg_control;
for (m = control; m != NULL; ) {
cmsg = mtod(m, struct cmsghdr *);
i = m->m_len;
if (len < i) {
mp->msg_flags |= MSG_CTRUNC;
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS)
/* Do not truncate me ... */
break;
i = len;
}
error = copyout(mtod(m, void *), q, i);
ktrkuser(mbuftypes[MT_CONTROL], cmsg, cmsg->cmsg_len);
if (error != 0) {
/* We must free all the SCM_RIGHTS */
m = control;
break;
}
m = m->m_next;
if (m)
i = ALIGN(i);
q += i;
len -= i;
if (len <= 0)
break;
}
tiov = auio.uio_iov;
for (i = 0; i < auio.uio_iovcnt; i++, tiov++) {
/*
* Reads return ssize_t because -1 is returned on error.
* Therefore we must restrict the length to SSIZE_MAX to
* avoid garbage return values.
*/
auio.uio_resid += tiov->iov_len;
if (tiov->iov_len > SSIZE_MAX || auio.uio_resid > SSIZE_MAX) {
error = SET_ERROR(EINVAL);
goto out;
}
}
if (SCARG(uap, timeout)) {
getnanotime(&now);
if (timespeccmp(&ts, &now, <))
break;
}
if (flags & MSG_WAITFORONE)
flags |= MSG_DONTWAIT;
}
if (from != NULL)
m_free(from);
*retval = dg;
/*
* If we succeeded at least once, return 0, hopefully so->so_rerror
* will catch it next time.
*/
if (error && dg > 0) {
so->so_rerror = error;
error = 0;
}
/* make sure the descriptors are uni-directional */
rf->f_type = rf->f_type & ~(FWRITE);
wf->f_type = wf->f_type & ~(FREAD);
/* remember this socket pair implements a pipe */
rso->so_state |= SS_ISAPIPE;
wso->so_state |= SS_ISAPIPE;
solock(wso);
/*
* Pipes must be readable when there is at least 1
* byte of data available in the receive buffer.
*
* Pipes must be writable when there is space for
* at least PIPE_BUF bytes in the send buffer.
* If we're increasing the low water mark for the
* send buffer, then mimic how soreserve() would
* have set the high water mark.
*/
rso->so_rcv.sb_lowat = 1;
if (wso->so_snd.sb_lowat < PIPE_BUF) {
wso->so_snd.sb_hiwat = PIPE_BUF * 2;
}
wso->so_snd.sb_lowat = PIPE_BUF;
error = unp_connect2(wso, rso);
sounlock(wso);
static int
sockargs_sb(struct sockaddr_big *sb, const void *name, socklen_t buflen)
{
int error;
/*
* We can't allow socket names > UCHAR_MAX in length, since that
* will overflow sb_len. Further no reasonable buflen is <=
* offsetof(sockaddr_big, sb_data) since it shall be at least
* the size of the preamble sb_len and sb_family members.
*/
if (buflen > UCHAR_MAX ||
buflen <= offsetof(struct sockaddr_big, sb_data))
return SET_ERROR(EINVAL);
error = copyin(name, (void *)sb, buflen);
if (error)
return error;
ktrkuser(mbuftypes[MT_SONAME], sb, buflen);
#if BYTE_ORDER != BIG_ENDIAN
/*
* 4.3BSD compat thing - need to stay, since bind(2),
* connect(2), sendto(2) were not versioned for COMPAT_43.
*/
if (sb->sb_family == 0 && sb->sb_len < AF_MAX)
sb->sb_family = sb->sb_len;
#endif
sb->sb_len = buflen;
return 0;
}
/*
* XXX In a perfect world, we wouldn't pass around socket control
* XXX arguments in mbufs, and this could go away.
*/
int
sockargs(struct mbuf **mp, const void *bf, size_t buflen, enum uio_seg seg,
int type)
{
struct mbuf *m;
int error;
/*
* We can't allow socket names > UCHAR_MAX in length, since that
* will overflow sa_len. Control data more than a page size in
* length is just too much.
*/
if (buflen > (type == MT_SONAME ? UCHAR_MAX : PAGE_SIZE))
return SET_ERROR(EINVAL);
/*
* length must greater than sizeof(sa_family) + sizeof(sa_len)
*/
if (type == MT_SONAME && buflen <= 2)
return SET_ERROR(EINVAL);
/* Allocate an mbuf to hold the arguments. */
m = m_get(M_WAIT, type);
/* can't claim. don't who to assign it to. */
if (buflen > MLEN) {
/*
* Won't fit into a regular mbuf, so we allocate just
* enough external storage to hold the argument.
*/
MEXTMALLOC(m, buflen, M_WAITOK);
}
m->m_len = buflen;
if (seg == UIO_USERSPACE) {
error = copyin(bf, mtod(m, void *), buflen);
if (error) {
(void)m_free(m);
return error;
}
} else {
memcpy(mtod(m, void *), bf, buflen);
}
*mp = m;
switch (type) {
case MT_SONAME:
ktrkuser(mbuftypes[type], mtod(m, void *), buflen);
struct sockaddr *sa = mtod(m, struct sockaddr *);
#if BYTE_ORDER != BIG_ENDIAN
/*
* 4.3BSD compat thing - need to stay, since bind(2),
* connect(2), sendto(2) were not versioned for COMPAT_43.
*/
if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
sa->sa_family = sa->sa_len;
#endif
sa->sa_len = buflen;
return 0;
case MT_CONTROL:
if (!KTRPOINT(curproc, KTR_USER))
return 0;
int
do_sys_peeloff(struct socket *head, void *data)
{
#ifdef SCTP
/*file_t *lfp = NULL;*/
file_t *nfp = NULL;
int error;
struct socket *so;
int fd;
uint32_t name;
/*short fflag;*/ /* type must match fp->f_flag */
name = *(uint32_t *) data;
error = sctp_can_peel_off(head, name);
if (error) {
printf("peeloff failed\n");
return error;
}
/*
* At this point we know we do have a assoc to pull
* we proceed to get the fd setup. This may block
* but that is ok.
*/
error = fd_allocfile(&nfp, &fd);
if (error) {
/*
* Probably ran out of file descriptors. Put the
* unaccepted connection back onto the queue and
* do another wakeup so some other process might
* have a chance at it.
*/
return error;
}
*(int *) data = fd;
so = sctp_get_peeloff(head, name, &error);
if (so == NULL) {
/*
* Either someone else peeled it off OR
* we can't get a socket.
* close the new descriptor, assuming someone hasn't ripped it
* out from under us.
*/
mutex_enter(&nfp->f_lock);
nfp->f_count++;
mutex_exit(&nfp->f_lock);
fd_abort(curlwp->l_proc, nfp, fd);
return error;
}
so->so_state &= ~SS_NOFDREF;
so->so_state &= ~SS_ISCONNECTING;
so->so_head = NULL;
so->so_cred = kauth_cred_hold(head->so_cred);
nfp->f_socket = so;
nfp->f_flag = FREAD|FWRITE;
nfp->f_ops = &socketops;
nfp->f_type = DTYPE_SOCKET;