e07f0b9362
copyin() or copyout(). uvm_useracc() tells us whether the mapping permissions allow access to the desired part of an address space, and many callers assume that this is the same as knowing whether an attempt to access that part of the address space will succeed. however, access to user space can fail for reasons other than insufficient permission, most notably that paging in any non-resident data can fail due to i/o errors. most of the callers of uvm_useracc() make the above incorrect assumption. the rest are all misguided optimizations, which optimize for the case where an operation will fail. we'd rather optimize for operations succeeding, in which case we should just attempt the access and handle failures due to insufficient permissions the same way we handle i/o errors. since there appear to be no good uses of uvm_useracc(), we'll just remove it.
1114 lines
26 KiB
C
1114 lines
26 KiB
C
/* $NetBSD: uipc_syscalls.c,v 1.84 2003/11/13 03:09:30 chs Exp $ */
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: uipc_syscalls.c,v 1.84 2003/11/13 03:09:30 chs Exp $");
|
|
|
|
#include "opt_ktrace.h"
|
|
#include "opt_pipe.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/filedesc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/file.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/un.h>
|
|
#ifdef KTRACE
|
|
#include <sys/ktrace.h>
|
|
#endif
|
|
#include <sys/event.h>
|
|
|
|
#include <sys/mount.h>
|
|
#include <sys/sa.h>
|
|
#include <sys/syscallargs.h>
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
/*
|
|
* System call interface to the socket abstraction.
|
|
*/
|
|
extern struct fileops socketops;
|
|
|
|
int
|
|
sys_socket(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_socket_args /* {
|
|
syscallarg(int) domain;
|
|
syscallarg(int) type;
|
|
syscallarg(int) protocol;
|
|
} */ *uap = v;
|
|
|
|
struct proc *p;
|
|
struct filedesc *fdp;
|
|
struct socket *so;
|
|
struct file *fp;
|
|
int fd, error;
|
|
|
|
p = l->l_proc;
|
|
fdp = p->p_fd;
|
|
/* falloc() will use the desciptor for us */
|
|
if ((error = falloc(p, &fp, &fd)) != 0)
|
|
return (error);
|
|
fp->f_flag = FREAD|FWRITE;
|
|
fp->f_type = DTYPE_SOCKET;
|
|
fp->f_ops = &socketops;
|
|
error = socreate(SCARG(uap, domain), &so, SCARG(uap, type),
|
|
SCARG(uap, protocol));
|
|
if (error) {
|
|
FILE_UNUSE(fp, p);
|
|
fdremove(fdp, fd);
|
|
ffree(fp);
|
|
} else {
|
|
fp->f_data = (caddr_t)so;
|
|
FILE_SET_MATURE(fp);
|
|
FILE_UNUSE(fp, p);
|
|
*retval = fd;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sys_bind(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_bind_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(const struct sockaddr *) name;
|
|
syscallarg(unsigned int) namelen;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
struct mbuf *nam;
|
|
int error;
|
|
|
|
p = l->l_proc;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen),
|
|
MT_SONAME);
|
|
if (error) {
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
MCLAIM(nam, ((struct socket *)fp->f_data)->so_mowner);
|
|
error = sobind((struct socket *)fp->f_data, nam, p);
|
|
m_freem(nam);
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sys_listen(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_listen_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(int) backlog;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
int error;
|
|
|
|
p = l->l_proc;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
error = solisten((struct socket *)fp->f_data, SCARG(uap, backlog));
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
sys_accept(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_accept_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(struct sockaddr *) name;
|
|
syscallarg(unsigned int *) anamelen;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct filedesc *fdp;
|
|
struct file *fp;
|
|
struct mbuf *nam;
|
|
unsigned int namelen;
|
|
int error, s, fd;
|
|
struct socket *so;
|
|
int fflag;
|
|
|
|
p = l->l_proc;
|
|
fdp = p->p_fd;
|
|
if (SCARG(uap, name) && (error = copyin(SCARG(uap, anamelen),
|
|
&namelen, sizeof(namelen))))
|
|
return (error);
|
|
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(fdp, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
s = splsoftnet();
|
|
so = (struct socket *)fp->f_data;
|
|
FILE_UNUSE(fp, p);
|
|
if (!(so->so_proto->pr_flags & PR_LISTEN)) {
|
|
splx(s);
|
|
return (EOPNOTSUPP);
|
|
}
|
|
if ((so->so_options & SO_ACCEPTCONN) == 0) {
|
|
splx(s);
|
|
return (EINVAL);
|
|
}
|
|
if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
|
|
splx(s);
|
|
return (EWOULDBLOCK);
|
|
}
|
|
while (so->so_qlen == 0 && so->so_error == 0) {
|
|
if (so->so_state & SS_CANTRCVMORE) {
|
|
so->so_error = ECONNABORTED;
|
|
break;
|
|
}
|
|
error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
|
|
netcon, 0);
|
|
if (error) {
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
}
|
|
if (so->so_error) {
|
|
error = so->so_error;
|
|
so->so_error = 0;
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
fflag = fp->f_flag;
|
|
/* falloc() will use the descriptor for us */
|
|
if ((error = falloc(p, &fp, &fd)) != 0) {
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
*retval = fd;
|
|
|
|
/* connection has been removed from the listen queue */
|
|
KNOTE(&so->so_rcv.sb_sel.sel_klist, 0);
|
|
|
|
{ struct socket *aso = TAILQ_FIRST(&so->so_q);
|
|
if (soqremque(aso, 1) == 0)
|
|
panic("accept");
|
|
so = aso;
|
|
}
|
|
fp->f_type = DTYPE_SOCKET;
|
|
fp->f_flag = fflag;
|
|
fp->f_ops = &socketops;
|
|
fp->f_data = (caddr_t)so;
|
|
FILE_UNUSE(fp, p);
|
|
nam = m_get(M_WAIT, MT_SONAME);
|
|
if ((error = soaccept(so, nam)) == 0 && SCARG(uap, name)) {
|
|
if (namelen > nam->m_len)
|
|
namelen = nam->m_len;
|
|
/* SHOULD COPY OUT A CHAIN HERE */
|
|
if ((error = copyout(mtod(nam, caddr_t),
|
|
(caddr_t)SCARG(uap, name), namelen)) == 0)
|
|
error = copyout((caddr_t)&namelen,
|
|
(caddr_t)SCARG(uap, anamelen),
|
|
sizeof(*SCARG(uap, anamelen)));
|
|
}
|
|
/* if an error occurred, free the file descriptor */
|
|
if (error) {
|
|
fdremove(fdp, fd);
|
|
ffree(fp);
|
|
}
|
|
m_freem(nam);
|
|
splx(s);
|
|
FILE_SET_MATURE(fp);
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sys_connect(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_connect_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(const struct sockaddr *) name;
|
|
syscallarg(unsigned int) namelen;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
struct socket *so;
|
|
struct mbuf *nam;
|
|
int error, s;
|
|
|
|
p = l->l_proc;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
so = (struct socket *)fp->f_data;
|
|
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
|
|
error = EALREADY;
|
|
goto out;
|
|
}
|
|
error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen),
|
|
MT_SONAME);
|
|
if (error)
|
|
goto out;
|
|
MCLAIM(nam, so->so_mowner);
|
|
error = soconnect(so, nam);
|
|
if (error)
|
|
goto bad;
|
|
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
|
|
m_freem(nam);
|
|
error = EINPROGRESS;
|
|
goto out;
|
|
}
|
|
s = splsoftnet();
|
|
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
|
|
error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
|
|
netcon, 0);
|
|
if (error)
|
|
break;
|
|
}
|
|
if (error == 0) {
|
|
error = so->so_error;
|
|
so->so_error = 0;
|
|
}
|
|
splx(s);
|
|
bad:
|
|
so->so_state &= ~SS_ISCONNECTING;
|
|
m_freem(nam);
|
|
if (error == ERESTART)
|
|
error = EINTR;
|
|
out:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
sys_socketpair(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_socketpair_args /* {
|
|
syscallarg(int) domain;
|
|
syscallarg(int) type;
|
|
syscallarg(int) protocol;
|
|
syscallarg(int *) rsv;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct filedesc *fdp;
|
|
struct file *fp1, *fp2;
|
|
struct socket *so1, *so2;
|
|
int fd, error, sv[2];
|
|
|
|
p = l->l_proc;
|
|
fdp = p->p_fd;
|
|
error = socreate(SCARG(uap, domain), &so1, SCARG(uap, type),
|
|
SCARG(uap, protocol));
|
|
if (error)
|
|
return (error);
|
|
error = socreate(SCARG(uap, domain), &so2, SCARG(uap, type),
|
|
SCARG(uap, protocol));
|
|
if (error)
|
|
goto free1;
|
|
/* falloc() will use the descriptor for us */
|
|
if ((error = falloc(p, &fp1, &fd)) != 0)
|
|
goto free2;
|
|
sv[0] = fd;
|
|
fp1->f_flag = FREAD|FWRITE;
|
|
fp1->f_type = DTYPE_SOCKET;
|
|
fp1->f_ops = &socketops;
|
|
fp1->f_data = (caddr_t)so1;
|
|
if ((error = falloc(p, &fp2, &fd)) != 0)
|
|
goto free3;
|
|
fp2->f_flag = FREAD|FWRITE;
|
|
fp2->f_type = DTYPE_SOCKET;
|
|
fp2->f_ops = &socketops;
|
|
fp2->f_data = (caddr_t)so2;
|
|
sv[1] = fd;
|
|
if ((error = soconnect2(so1, so2)) != 0)
|
|
goto free4;
|
|
if (SCARG(uap, type) == SOCK_DGRAM) {
|
|
/*
|
|
* Datagram socket connection is asymmetric.
|
|
*/
|
|
if ((error = soconnect2(so2, so1)) != 0)
|
|
goto free4;
|
|
}
|
|
error = copyout((caddr_t)sv, (caddr_t)SCARG(uap, rsv),
|
|
2 * sizeof(int));
|
|
FILE_SET_MATURE(fp1);
|
|
FILE_SET_MATURE(fp2);
|
|
FILE_UNUSE(fp1, p);
|
|
FILE_UNUSE(fp2, p);
|
|
return (error);
|
|
free4:
|
|
FILE_UNUSE(fp2, p);
|
|
ffree(fp2);
|
|
fdremove(fdp, sv[1]);
|
|
free3:
|
|
FILE_UNUSE(fp1, p);
|
|
ffree(fp1);
|
|
fdremove(fdp, sv[0]);
|
|
free2:
|
|
(void)soclose(so2);
|
|
free1:
|
|
(void)soclose(so1);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
sys_sendto(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_sendto_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(const void *) buf;
|
|
syscallarg(size_t) len;
|
|
syscallarg(int) flags;
|
|
syscallarg(const struct sockaddr *) to;
|
|
syscallarg(unsigned int) tolen;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct msghdr msg;
|
|
struct iovec aiov;
|
|
|
|
p = l->l_proc;
|
|
msg.msg_name = (caddr_t)SCARG(uap, to); /* XXX kills const */
|
|
msg.msg_namelen = SCARG(uap, tolen);
|
|
msg.msg_iov = &aiov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = 0;
|
|
msg.msg_flags = 0;
|
|
aiov.iov_base = (char *)SCARG(uap, buf); /* XXX kills const */
|
|
aiov.iov_len = SCARG(uap, len);
|
|
return (sendit(p, SCARG(uap, s), &msg, SCARG(uap, flags), retval));
|
|
}
|
|
|
|
int
|
|
sys_sendmsg(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_sendmsg_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(const struct msghdr *) msg;
|
|
syscallarg(int) flags;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct msghdr msg;
|
|
struct iovec aiov[UIO_SMALLIOV], *iov;
|
|
int error;
|
|
|
|
error = copyin(SCARG(uap, msg), (caddr_t)&msg, sizeof(msg));
|
|
if (error)
|
|
return (error);
|
|
if ((unsigned int)msg.msg_iovlen > UIO_SMALLIOV) {
|
|
if ((unsigned int)msg.msg_iovlen > IOV_MAX)
|
|
return (EMSGSIZE);
|
|
iov = malloc(sizeof(struct iovec) * msg.msg_iovlen,
|
|
M_IOV, M_WAITOK);
|
|
} else
|
|
iov = aiov;
|
|
if ((unsigned int)msg.msg_iovlen > 0) {
|
|
error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
|
|
(size_t)(msg.msg_iovlen * sizeof(struct iovec)));
|
|
if (error)
|
|
goto done;
|
|
}
|
|
msg.msg_iov = iov;
|
|
msg.msg_flags = 0;
|
|
p = l->l_proc;
|
|
error = sendit(p, SCARG(uap, s), &msg, SCARG(uap, flags), retval);
|
|
done:
|
|
if (iov != aiov)
|
|
free(iov, M_IOV);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
sendit(struct proc *p, int s, struct msghdr *mp, int flags, register_t *retsize)
|
|
{
|
|
struct file *fp;
|
|
struct uio auio;
|
|
struct iovec *iov;
|
|
int i, len, error;
|
|
struct mbuf *to, *control;
|
|
struct socket *so;
|
|
#ifdef KTRACE
|
|
struct iovec *ktriov;
|
|
#endif
|
|
|
|
#ifdef KTRACE
|
|
ktriov = NULL;
|
|
#endif
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, s, &fp)) != 0)
|
|
return (error);
|
|
so = (struct socket *)fp->f_data;
|
|
auio.uio_iov = mp->msg_iov;
|
|
auio.uio_iovcnt = mp->msg_iovlen;
|
|
auio.uio_segflg = UIO_USERSPACE;
|
|
auio.uio_rw = UIO_WRITE;
|
|
auio.uio_procp = p;
|
|
auio.uio_offset = 0; /* XXX */
|
|
auio.uio_resid = 0;
|
|
iov = mp->msg_iov;
|
|
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
|
|
#if 0
|
|
/* cannot happen; iov_len is unsigned */
|
|
if (iov->iov_len < 0) {
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
#endif
|
|
/*
|
|
* 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 += iov->iov_len;
|
|
if (iov->iov_len > SSIZE_MAX || auio.uio_resid > SSIZE_MAX) {
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
if (mp->msg_name) {
|
|
error = sockargs(&to, mp->msg_name, mp->msg_namelen,
|
|
MT_SONAME);
|
|
if (error)
|
|
goto out;
|
|
MCLAIM(to, so->so_mowner);
|
|
} else
|
|
to = 0;
|
|
if (mp->msg_control) {
|
|
if (mp->msg_controllen < sizeof(struct cmsghdr)) {
|
|
error = EINVAL;
|
|
goto bad;
|
|
}
|
|
error = sockargs(&control, mp->msg_control,
|
|
mp->msg_controllen, MT_CONTROL);
|
|
if (error)
|
|
goto bad;
|
|
MCLAIM(control, so->so_mowner);
|
|
} else
|
|
control = 0;
|
|
#ifdef KTRACE
|
|
if (KTRPOINT(p, KTR_GENIO)) {
|
|
int iovlen = auio.uio_iovcnt * sizeof(struct iovec);
|
|
|
|
ktriov = malloc(iovlen, M_TEMP, M_WAITOK);
|
|
memcpy((caddr_t)ktriov, (caddr_t)auio.uio_iov, iovlen);
|
|
}
|
|
#endif
|
|
len = auio.uio_resid;
|
|
error = (*so->so_send)(so, to, &auio, NULL, control, flags);
|
|
if (error) {
|
|
if (auio.uio_resid != len && (error == ERESTART ||
|
|
error == EINTR || error == EWOULDBLOCK))
|
|
error = 0;
|
|
if (error == EPIPE)
|
|
psignal(p, SIGPIPE);
|
|
}
|
|
if (error == 0)
|
|
*retsize = len - auio.uio_resid;
|
|
#ifdef KTRACE
|
|
if (ktriov != NULL) {
|
|
if (error == 0)
|
|
ktrgenio(p, s, UIO_WRITE, ktriov, *retsize, error);
|
|
free(ktriov, M_TEMP);
|
|
}
|
|
#endif
|
|
bad:
|
|
if (to)
|
|
m_freem(to);
|
|
out:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
sys_recvfrom(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_recvfrom_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(void *) buf;
|
|
syscallarg(size_t) len;
|
|
syscallarg(int) flags;
|
|
syscallarg(struct sockaddr *) from;
|
|
syscallarg(unsigned int *) fromlenaddr;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct msghdr msg;
|
|
struct iovec aiov;
|
|
int error;
|
|
|
|
if (SCARG(uap, fromlenaddr)) {
|
|
error = copyin((caddr_t)SCARG(uap, fromlenaddr),
|
|
(caddr_t)&msg.msg_namelen,
|
|
sizeof(msg.msg_namelen));
|
|
if (error)
|
|
return (error);
|
|
} else
|
|
msg.msg_namelen = 0;
|
|
msg.msg_name = (caddr_t)SCARG(uap, from);
|
|
msg.msg_iov = &aiov;
|
|
msg.msg_iovlen = 1;
|
|
aiov.iov_base = SCARG(uap, buf);
|
|
aiov.iov_len = SCARG(uap, len);
|
|
msg.msg_control = 0;
|
|
msg.msg_flags = SCARG(uap, flags);
|
|
p = l->l_proc;
|
|
return (recvit(p, SCARG(uap, s), &msg,
|
|
(caddr_t)SCARG(uap, fromlenaddr), retval));
|
|
}
|
|
|
|
int
|
|
sys_recvmsg(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_recvmsg_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(struct msghdr *) msg;
|
|
syscallarg(int) flags;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct msghdr msg;
|
|
struct iovec aiov[UIO_SMALLIOV], *uiov, *iov;
|
|
int error;
|
|
|
|
error = copyin((caddr_t)SCARG(uap, msg), (caddr_t)&msg,
|
|
sizeof(msg));
|
|
if (error)
|
|
return (error);
|
|
if ((unsigned int)msg.msg_iovlen > UIO_SMALLIOV) {
|
|
if ((unsigned int)msg.msg_iovlen > IOV_MAX)
|
|
return (EMSGSIZE);
|
|
iov = malloc(sizeof(struct iovec) * msg.msg_iovlen,
|
|
M_IOV, M_WAITOK);
|
|
} else
|
|
iov = aiov;
|
|
if ((unsigned int)msg.msg_iovlen > 0) {
|
|
error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
|
|
(size_t)(msg.msg_iovlen * sizeof(struct iovec)));
|
|
if (error)
|
|
goto done;
|
|
}
|
|
uiov = msg.msg_iov;
|
|
msg.msg_iov = iov;
|
|
msg.msg_flags = SCARG(uap, flags);
|
|
p = l->l_proc;
|
|
if ((error = recvit(p, SCARG(uap, s), &msg, (caddr_t)0, retval)) == 0) {
|
|
msg.msg_iov = uiov;
|
|
error = copyout((caddr_t)&msg, (caddr_t)SCARG(uap, msg),
|
|
sizeof(msg));
|
|
}
|
|
done:
|
|
if (iov != aiov)
|
|
free(iov, M_IOV);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
recvit(struct proc *p, int s, struct msghdr *mp, caddr_t namelenp,
|
|
register_t *retsize)
|
|
{
|
|
struct file *fp;
|
|
struct uio auio;
|
|
struct iovec *iov;
|
|
int i, len, error;
|
|
struct mbuf *from, *control;
|
|
struct socket *so;
|
|
#ifdef KTRACE
|
|
struct iovec *ktriov;
|
|
#endif
|
|
|
|
from = 0;
|
|
control = 0;
|
|
#ifdef KTRACE
|
|
ktriov = NULL;
|
|
#endif
|
|
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, s, &fp)) != 0)
|
|
return (error);
|
|
so = (struct socket *)fp->f_data;
|
|
auio.uio_iov = mp->msg_iov;
|
|
auio.uio_iovcnt = mp->msg_iovlen;
|
|
auio.uio_segflg = UIO_USERSPACE;
|
|
auio.uio_rw = UIO_READ;
|
|
auio.uio_procp = p;
|
|
auio.uio_offset = 0; /* XXX */
|
|
auio.uio_resid = 0;
|
|
iov = mp->msg_iov;
|
|
for (i = 0; i < mp->msg_iovlen; i++, iov++) {
|
|
#if 0
|
|
/* cannot happen iov_len is unsigned */
|
|
if (iov->iov_len < 0) {
|
|
error = EINVAL;
|
|
goto out1;
|
|
}
|
|
#endif
|
|
/*
|
|
* 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 += iov->iov_len;
|
|
if (iov->iov_len > SSIZE_MAX || auio.uio_resid > SSIZE_MAX) {
|
|
error = EINVAL;
|
|
goto out1;
|
|
}
|
|
}
|
|
#ifdef KTRACE
|
|
if (KTRPOINT(p, KTR_GENIO)) {
|
|
int iovlen = auio.uio_iovcnt * sizeof(struct iovec);
|
|
|
|
ktriov = malloc(iovlen, M_TEMP, M_WAITOK);
|
|
memcpy((caddr_t)ktriov, (caddr_t)auio.uio_iov, iovlen);
|
|
}
|
|
#endif
|
|
len = auio.uio_resid;
|
|
error = (*so->so_receive)(so, &from, &auio, NULL,
|
|
mp->msg_control ? &control : NULL, &mp->msg_flags);
|
|
if (error) {
|
|
if (auio.uio_resid != len && (error == ERESTART ||
|
|
error == EINTR || error == EWOULDBLOCK))
|
|
error = 0;
|
|
}
|
|
#ifdef KTRACE
|
|
if (ktriov != NULL) {
|
|
if (error == 0)
|
|
ktrgenio(p, s, UIO_READ, ktriov,
|
|
len - auio.uio_resid, error);
|
|
free(ktriov, M_TEMP);
|
|
}
|
|
#endif
|
|
if (error)
|
|
goto out;
|
|
*retsize = len - auio.uio_resid;
|
|
if (mp->msg_name) {
|
|
len = mp->msg_namelen;
|
|
if (len <= 0 || from == 0)
|
|
len = 0;
|
|
else {
|
|
if (len > from->m_len)
|
|
len = from->m_len;
|
|
/* else if len < from->m_len ??? */
|
|
error = copyout(mtod(from, caddr_t),
|
|
(caddr_t)mp->msg_name, (unsigned)len);
|
|
if (error)
|
|
goto out;
|
|
}
|
|
mp->msg_namelen = len;
|
|
if (namelenp &&
|
|
(error = copyout((caddr_t)&len, namelenp, sizeof(int))))
|
|
goto out;
|
|
}
|
|
if (mp->msg_control) {
|
|
len = mp->msg_controllen;
|
|
if (len <= 0 || control == 0)
|
|
len = 0;
|
|
else {
|
|
struct mbuf *m = control;
|
|
caddr_t p = (caddr_t)mp->msg_control;
|
|
|
|
do {
|
|
i = m->m_len;
|
|
if (len < i) {
|
|
mp->msg_flags |= MSG_CTRUNC;
|
|
i = len;
|
|
}
|
|
error = copyout(mtod(m, caddr_t), p,
|
|
(unsigned)i);
|
|
if (m->m_next)
|
|
i = ALIGN(i);
|
|
p += i;
|
|
len -= i;
|
|
if (error != 0 || len <= 0)
|
|
break;
|
|
} while ((m = m->m_next) != NULL);
|
|
len = p - (caddr_t)mp->msg_control;
|
|
}
|
|
mp->msg_controllen = len;
|
|
}
|
|
out:
|
|
if (from)
|
|
m_freem(from);
|
|
if (control)
|
|
m_freem(control);
|
|
out1:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sys_shutdown(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_shutdown_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(int) how;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
int error;
|
|
|
|
p = l->l_proc;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
error = soshutdown((struct socket *)fp->f_data, SCARG(uap, how));
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sys_setsockopt(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_setsockopt_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(int) level;
|
|
syscallarg(int) name;
|
|
syscallarg(const void *) val;
|
|
syscallarg(unsigned int) valsize;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
struct mbuf *m;
|
|
struct socket *so;
|
|
int error;
|
|
unsigned int len;
|
|
|
|
p = l->l_proc;
|
|
m = NULL;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
so = (struct socket *)fp->f_data;
|
|
len = SCARG(uap, valsize);
|
|
if (len > MCLBYTES) {
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
if (SCARG(uap, val)) {
|
|
m = m_get(M_WAIT, MT_SOOPTS);
|
|
MCLAIM(m, so->so_mowner);
|
|
if (len > MLEN)
|
|
m_clget(m, M_WAIT);
|
|
error = copyin(SCARG(uap, val), mtod(m, caddr_t), len);
|
|
if (error) {
|
|
(void) m_free(m);
|
|
goto out;
|
|
}
|
|
m->m_len = SCARG(uap, valsize);
|
|
}
|
|
error = sosetopt(so, SCARG(uap, level), SCARG(uap, name), m);
|
|
out:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sys_getsockopt(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_getsockopt_args /* {
|
|
syscallarg(int) s;
|
|
syscallarg(int) level;
|
|
syscallarg(int) name;
|
|
syscallarg(void *) val;
|
|
syscallarg(unsigned int *) avalsize;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
struct mbuf *m;
|
|
unsigned int op, i, valsize;
|
|
int error;
|
|
|
|
p = l->l_proc;
|
|
m = NULL;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
|
|
return (error);
|
|
if (SCARG(uap, val)) {
|
|
error = copyin((caddr_t)SCARG(uap, avalsize),
|
|
(caddr_t)&valsize, sizeof(valsize));
|
|
if (error)
|
|
goto out;
|
|
} else
|
|
valsize = 0;
|
|
if ((error = sogetopt((struct socket *)fp->f_data, SCARG(uap, level),
|
|
SCARG(uap, name), &m)) == 0 && SCARG(uap, val) && valsize &&
|
|
m != NULL) {
|
|
op = 0;
|
|
while (m && !error && op < valsize) {
|
|
i = min(m->m_len, (valsize - op));
|
|
error = copyout(mtod(m, caddr_t), SCARG(uap, val), i);
|
|
op += i;
|
|
SCARG(uap, val) = ((u_int8_t *)SCARG(uap, val)) + i;
|
|
m = m_free(m);
|
|
}
|
|
valsize = op;
|
|
if (error == 0)
|
|
error = copyout(&valsize,
|
|
SCARG(uap, avalsize), sizeof(valsize));
|
|
}
|
|
if (m != NULL)
|
|
(void) m_freem(m);
|
|
out:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
#ifdef PIPE_SOCKETPAIR
|
|
/* ARGSUSED */
|
|
int
|
|
sys_pipe(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct proc *p;
|
|
struct filedesc *fdp;
|
|
struct file *rf, *wf;
|
|
struct socket *rso, *wso;
|
|
int fd, error;
|
|
|
|
p = l->l_proc;
|
|
fdp = p->p_fd;
|
|
if ((error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0)) != 0)
|
|
return (error);
|
|
if ((error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0)) != 0)
|
|
goto free1;
|
|
/* remember this socket pair implements a pipe */
|
|
wso->so_state |= SS_ISAPIPE;
|
|
rso->so_state |= SS_ISAPIPE;
|
|
/* falloc() will use the descriptor for us */
|
|
if ((error = falloc(p, &rf, &fd)) != 0)
|
|
goto free2;
|
|
retval[0] = fd;
|
|
rf->f_flag = FREAD;
|
|
rf->f_type = DTYPE_SOCKET;
|
|
rf->f_ops = &socketops;
|
|
rf->f_data = (caddr_t)rso;
|
|
if ((error = falloc(p, &wf, &fd)) != 0)
|
|
goto free3;
|
|
wf->f_flag = FWRITE;
|
|
wf->f_type = DTYPE_SOCKET;
|
|
wf->f_ops = &socketops;
|
|
wf->f_data = (caddr_t)wso;
|
|
retval[1] = fd;
|
|
if ((error = unp_connect2(wso, rso, PRU_CONNECT2)) != 0)
|
|
goto free4;
|
|
FILE_SET_MATURE(rf);
|
|
FILE_SET_MATURE(wf);
|
|
FILE_UNUSE(rf, p);
|
|
FILE_UNUSE(wf, p);
|
|
return (0);
|
|
free4:
|
|
FILE_UNUSE(wf, p);
|
|
ffree(wf);
|
|
fdremove(fdp, retval[1]);
|
|
free3:
|
|
FILE_UNUSE(rf, p);
|
|
ffree(rf);
|
|
fdremove(fdp, retval[0]);
|
|
free2:
|
|
(void)soclose(wso);
|
|
free1:
|
|
(void)soclose(rso);
|
|
return (error);
|
|
}
|
|
#endif /* PIPE_SOCKETPAIR */
|
|
|
|
/*
|
|
* Get socket name.
|
|
*/
|
|
/* ARGSUSED */
|
|
int
|
|
sys_getsockname(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_getsockname_args /* {
|
|
syscallarg(int) fdes;
|
|
syscallarg(struct sockaddr *) asa;
|
|
syscallarg(unsigned int *) alen;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
struct socket *so;
|
|
struct mbuf *m;
|
|
unsigned int len;
|
|
int error;
|
|
|
|
p = l->l_proc;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0)
|
|
return (error);
|
|
error = copyin((caddr_t)SCARG(uap, alen), (caddr_t)&len, sizeof(len));
|
|
if (error)
|
|
goto out;
|
|
so = (struct socket *)fp->f_data;
|
|
m = m_getclr(M_WAIT, MT_SONAME);
|
|
MCLAIM(m, so->so_mowner);
|
|
error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, (struct mbuf *)0,
|
|
m, (struct mbuf *)0, (struct proc *)0);
|
|
if (error)
|
|
goto bad;
|
|
if (len > m->m_len)
|
|
len = m->m_len;
|
|
error = copyout(mtod(m, caddr_t), (caddr_t)SCARG(uap, asa), len);
|
|
if (error == 0)
|
|
error = copyout((caddr_t)&len, (caddr_t)SCARG(uap, alen),
|
|
sizeof(len));
|
|
bad:
|
|
m_freem(m);
|
|
out:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Get name of peer for connected socket.
|
|
*/
|
|
/* ARGSUSED */
|
|
int
|
|
sys_getpeername(struct lwp *l, void *v, register_t *retval)
|
|
{
|
|
struct sys_getpeername_args /* {
|
|
syscallarg(int) fdes;
|
|
syscallarg(struct sockaddr *) asa;
|
|
syscallarg(unsigned int *) alen;
|
|
} */ *uap = v;
|
|
struct proc *p;
|
|
struct file *fp;
|
|
struct socket *so;
|
|
struct mbuf *m;
|
|
unsigned int len;
|
|
int error;
|
|
|
|
p = l->l_proc;
|
|
/* getsock() will use the descriptor for us */
|
|
if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0)
|
|
return (error);
|
|
so = (struct socket *)fp->f_data;
|
|
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) {
|
|
error = ENOTCONN;
|
|
goto out;
|
|
}
|
|
error = copyin((caddr_t)SCARG(uap, alen), (caddr_t)&len, sizeof(len));
|
|
if (error)
|
|
goto out;
|
|
m = m_getclr(M_WAIT, MT_SONAME);
|
|
MCLAIM(m, so->so_mowner);
|
|
error = (*so->so_proto->pr_usrreq)(so, PRU_PEERADDR, (struct mbuf *)0,
|
|
m, (struct mbuf *)0, (struct proc *)0);
|
|
if (error)
|
|
goto bad;
|
|
if (len > m->m_len)
|
|
len = m->m_len;
|
|
error = copyout(mtod(m, caddr_t), (caddr_t)SCARG(uap, asa), len);
|
|
if (error)
|
|
goto bad;
|
|
error = copyout((caddr_t)&len, (caddr_t)SCARG(uap, alen), sizeof(len));
|
|
bad:
|
|
m_freem(m);
|
|
out:
|
|
FILE_UNUSE(fp, p);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* 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 *buf, size_t buflen, int type)
|
|
{
|
|
struct sockaddr *sa;
|
|
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 (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;
|
|
error = copyin(buf, mtod(m, caddr_t), buflen);
|
|
if (error) {
|
|
(void) m_free(m);
|
|
return (error);
|
|
}
|
|
*mp = m;
|
|
if (type == MT_SONAME) {
|
|
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);
|
|
}
|
|
|
|
int
|
|
getsock(struct filedesc *fdp, int fdes, struct file **fpp)
|
|
{
|
|
struct file *fp;
|
|
|
|
if ((fp = fd_getfile(fdp, fdes)) == NULL)
|
|
return (EBADF);
|
|
|
|
FILE_USE(fp);
|
|
|
|
if (fp->f_type != DTYPE_SOCKET) {
|
|
FILE_UNUSE(fp, NULL);
|
|
return (ENOTSOCK);
|
|
}
|
|
*fpp = fp;
|
|
return (0);
|
|
}
|