1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-15 10:17:20 +00:00
freebsd/sys/kern/uipc_syscalls.c
Steven Wallace 93c9414e49 Remove compat_43 psuedo-argument hack, and replace with a better hack.
Instead of using a fake "compat" argument, pass a real compat int to function
if COMPAT_43 is defined.  Functions involved: wait4, accept, recvfrom,
getsockname.

With the compat psuedo-argument, this introduces an argument structure
that can have two possible sizes depending on compat options.
This makes life difficult for lkm modules like ibcs2, which would
have to guess what size used in kernel when compiled.  Also,
the prototype generator for these structures cannot generate proper sizes.

Now there is only one fixed structure and makes everybody happy.

I recommend these changes be introduced to 2.1 so that ibcs2, linux
lkm's generated for 2.2 can still run on a 2.1 kernel.
1995-10-07 23:47:26 +00:00

1310 lines
28 KiB
C

/*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. 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.4 (Berkeley) 2/21/94
* $Id: uipc_syscalls.c,v 1.6 1995/05/30 08:06:24 rgrimes Exp $
*/
#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/stat.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/signalvar.h>
#include <sys/un.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
/*
* System call interface to the socket abstraction.
*/
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
#define COMPAT_OLDSOCK
#endif
extern struct fileops socketops;
struct socket_args {
int domain;
int type;
int protocol;
};
int
socket(p, uap, retval)
struct proc *p;
register struct socket_args *uap;
int *retval;
{
struct filedesc *fdp = p->p_fd;
struct socket *so;
struct file *fp;
int fd, error;
error = falloc(p, &fp, &fd);
if (error)
return (error);
fp->f_flag = FREAD|FWRITE;
fp->f_type = DTYPE_SOCKET;
fp->f_ops = &socketops;
error = socreate(uap->domain, &so, uap->type, uap->protocol);
if (error) {
fdp->fd_ofiles[fd] = 0;
ffree(fp);
} else {
fp->f_data = (caddr_t)so;
*retval = fd;
}
return (error);
}
struct bind_args {
int s;
caddr_t name;
int namelen;
};
/* ARGSUSED */
int
bind(p, uap, retval)
struct proc *p;
register struct bind_args *uap;
int *retval;
{
struct file *fp;
struct mbuf *nam;
int error;
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
error = sockargs(&nam, uap->name, uap->namelen, MT_SONAME);
if (error)
return (error);
error = sobind((struct socket *)fp->f_data, nam);
m_freem(nam);
return (error);
}
struct listen_args {
int s;
int backlog;
};
/* ARGSUSED */
int
listen(p, uap, retval)
struct proc *p;
register struct listen_args *uap;
int *retval;
{
struct file *fp;
int error;
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
return (solisten((struct socket *)fp->f_data, uap->backlog));
}
struct accept_args {
int s;
caddr_t name;
int *anamelen;
};
#ifdef COMPAT_OLDSOCK
static int accept1(struct proc *, struct accept_args *, int [], int);
int
oaccept(p, uap, retval)
struct proc *p;
struct accept_args *uap;
int *retval;
{
return (accept1(p, uap, retval, 1));
}
int
accept(p, uap, retval)
struct proc *p;
struct accept_args *uap;
int *retval;
{
return (accept1(p, uap, retval, 0));
}
static int
accept1(p, uap, retval, compat)
struct proc *p;
register struct accept_args *uap;
int *retval;
int compat;
#else /* COMPAT_OLDSOCK */
int
accept(p, uap, retval)
struct proc *p;
register struct accept_args *uap;
int *retval;
#endif /* COMPAT_OLDSOCK*/
{
struct file *fp;
struct mbuf *nam;
int namelen, error, s;
register struct socket *so;
if (uap->name) {
error = copyin((caddr_t)uap->anamelen, (caddr_t)&namelen,
sizeof (namelen));
if(error)
return (error);
}
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
s = splnet();
so = (struct socket *)fp->f_data;
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);
}
error = falloc(p, &fp, retval);
if (error) {
splx(s);
return (error);
}
{ struct socket *aso = so->so_q;
if (soqremque(aso, 1) == 0)
panic("accept");
so = aso;
}
fp->f_type = DTYPE_SOCKET;
fp->f_flag = FREAD|FWRITE;
fp->f_ops = &socketops;
fp->f_data = (caddr_t)so;
nam = m_get(M_WAIT, MT_SONAME);
(void) soaccept(so, nam);
if (uap->name) {
#ifdef COMPAT_OLDSOCK
if (compat)
mtod(nam, struct osockaddr *)->sa_family =
mtod(nam, struct sockaddr *)->sa_family;
#endif
if (namelen > nam->m_len)
namelen = nam->m_len;
/* SHOULD COPY OUT A CHAIN HERE */
error = copyout(mtod(nam, caddr_t), (caddr_t)uap->name,
(u_int)namelen);
if (!error)
error = copyout((caddr_t)&namelen,
(caddr_t)uap->anamelen, sizeof (*uap->anamelen));
}
m_freem(nam);
splx(s);
return (error);
}
struct connect_args {
int s;
caddr_t name;
int namelen;
};
/* ARGSUSED */
int
connect(p, uap, retval)
struct proc *p;
register struct connect_args *uap;
int *retval;
{
struct file *fp;
register struct socket *so;
struct mbuf *nam;
int error, s;
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
so = (struct socket *)fp->f_data;
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING))
return (EALREADY);
error = sockargs(&nam, uap->name, uap->namelen, MT_SONAME);
if (error)
return (error);
error = soconnect(so, nam);
if (error)
goto bad;
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
m_freem(nam);
return (EINPROGRESS);
}
s = splnet();
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;
return (error);
}
struct socketpair_args {
int domain;
int type;
int protocol;
int *rsv;
};
int
socketpair(p, uap, retval)
struct proc *p;
register struct socketpair_args *uap;
int retval[];
{
register struct filedesc *fdp = p->p_fd;
struct file *fp1, *fp2;
struct socket *so1, *so2;
int fd, error, sv[2];
error = socreate(uap->domain, &so1, uap->type, uap->protocol);
if (error)
return (error);
error = socreate(uap->domain, &so2, uap->type, uap->protocol);
if (error)
goto free1;
error = falloc(p, &fp1, &fd);
if (error)
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;
error = falloc(p, &fp2, &fd);
if (error)
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;
error = soconnect2(so1, so2);
if (error)
goto free4;
if (uap->type == SOCK_DGRAM) {
/*
* Datagram socket connection is asymmetric.
*/
error = soconnect2(so2, so1);
if (error)
goto free4;
}
error = copyout((caddr_t)sv, (caddr_t)uap->rsv, 2 * sizeof (int));
retval[0] = sv[0]; /* XXX ??? */
retval[1] = sv[1]; /* XXX ??? */
return (error);
free4:
ffree(fp2);
fdp->fd_ofiles[sv[1]] = 0;
free3:
ffree(fp1);
fdp->fd_ofiles[sv[0]] = 0;
free2:
(void)soclose(so2);
free1:
(void)soclose(so1);
return (error);
}
int
sendit(p, s, mp, flags, retsize)
register struct proc *p;
int s;
register struct msghdr *mp;
int flags, *retsize;
{
struct file *fp;
struct uio auio;
register struct iovec *iov;
register int i;
struct mbuf *to, *control;
int len, error;
#ifdef KTRACE
struct iovec *ktriov = NULL;
#endif
error = getsock(p->p_fd, s, &fp);
if (error)
return (error);
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 ((auio.uio_resid += iov->iov_len) < 0)
return (EINVAL);
}
if (mp->msg_name) {
error = sockargs(&to, mp->msg_name, mp->msg_namelen, MT_SONAME);
if (error)
return (error);
} else
to = 0;
if (mp->msg_control) {
if (mp->msg_controllen < sizeof(struct cmsghdr)
#ifdef COMPAT_OLDSOCK
&& mp->msg_flags != MSG_COMPAT
#endif
) {
error = EINVAL;
goto bad;
}
error = sockargs(&control, mp->msg_control,
mp->msg_controllen, MT_CONTROL);
if (error)
goto bad;
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags == MSG_COMPAT) {
register struct cmsghdr *cm;
M_PREPEND(control, sizeof(*cm), M_WAIT);
if (control == 0) {
error = ENOBUFS;
goto bad;
} else {
cm = mtod(control, struct cmsghdr *);
cm->cmsg_len = control->m_len;
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
}
}
#endif
} else
control = 0;
#ifdef KTRACE
if (KTRPOINT(p, KTR_GENIO)) {
int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
}
#endif
len = auio.uio_resid;
error = sosend((struct socket *)fp->f_data, to, &auio,
(struct mbuf *)0, 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->p_tracep, s, UIO_WRITE,
ktriov, *retsize, error);
FREE(ktriov, M_TEMP);
}
#endif
bad:
if (to)
m_freem(to);
return (error);
}
struct sendto_args {
int s;
caddr_t buf;
size_t len;
int flags;
caddr_t to;
int tolen;
};
int
sendto(p, uap, retval)
struct proc *p;
register struct sendto_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov;
msg.msg_name = uap->to;
msg.msg_namelen = uap->tolen;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
msg.msg_control = 0;
#ifdef COMPAT_OLDSOCK
msg.msg_flags = 0;
#endif
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
return (sendit(p, uap->s, &msg, uap->flags, retval));
}
#ifdef COMPAT_OLDSOCK
struct osend_args {
int s;
caddr_t buf;
int len;
int flags;
};
int
osend(p, uap, retval)
struct proc *p;
register struct osend_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_control = 0;
msg.msg_flags = 0;
return (sendit(p, uap->s, &msg, uap->flags, retval));
}
struct osendmsg_args {
int s;
caddr_t msg;
int flags;
};
int
osendmsg(p, uap, retval)
struct proc *p;
register struct osendmsg_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *iov;
int error;
error = copyin(uap->msg, (caddr_t)&msg, sizeof (struct omsghdr));
if (error)
return (error);
if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
return (EMSGSIZE);
MALLOC(iov, struct iovec *,
sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
M_WAITOK);
} else
iov = aiov;
error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
(unsigned)(msg.msg_iovlen * sizeof (struct iovec)));
if (error)
goto done;
msg.msg_flags = MSG_COMPAT;
msg.msg_iov = iov;
error = sendit(p, uap->s, &msg, uap->flags, retval);
done:
if (iov != aiov)
FREE(iov, M_IOV);
return (error);
}
#endif
struct sendmsg_args {
int s;
caddr_t msg;
int flags;
};
int
sendmsg(p, uap, retval)
struct proc *p;
register struct sendmsg_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *iov;
int error;
error = copyin(uap->msg, (caddr_t)&msg, sizeof (msg));
if (error)
return (error);
if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
return (EMSGSIZE);
MALLOC(iov, struct iovec *,
sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
M_WAITOK);
} else
iov = aiov;
if (msg.msg_iovlen &&
(error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
(unsigned)(msg.msg_iovlen * sizeof (struct iovec)))))
goto done;
msg.msg_iov = iov;
#ifdef COMPAT_OLDSOCK
msg.msg_flags = 0;
#endif
error = sendit(p, uap->s, &msg, uap->flags, retval);
done:
if (iov != aiov)
FREE(iov, M_IOV);
return (error);
}
int
recvit(p, s, mp, namelenp, retsize)
register struct proc *p;
int s;
register struct msghdr *mp;
caddr_t namelenp;
int *retsize;
{
struct file *fp;
struct uio auio;
register struct iovec *iov;
register int i;
int len, error;
struct mbuf *from = 0, *control = 0;
#ifdef KTRACE
struct iovec *ktriov = NULL;
#endif
error = getsock(p->p_fd, s, &fp);
if (error)
return (error);
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 ((auio.uio_resid += iov->iov_len) < 0)
return (EINVAL);
}
#ifdef KTRACE
if (KTRPOINT(p, KTR_GENIO)) {
int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
}
#endif
len = auio.uio_resid;
error = soreceive((struct socket *)fp->f_data, &from, &auio,
(struct mbuf **)0, mp->msg_control ? &control : (struct mbuf **)0,
&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->p_tracep, 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 {
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
mtod(from, struct osockaddr *)->sa_family =
mtod(from, struct sockaddr *)->sa_family;
#endif
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)))) {
#ifdef COMPAT_OLDSOCK
if (mp->msg_flags & MSG_COMPAT)
error = 0; /* old recvfrom didn't check */
else
#endif
goto out;
}
}
if (mp->msg_control) {
#ifdef COMPAT_OLDSOCK
/*
* We assume that old recvmsg calls won't receive access
* rights and other control info, esp. as control info
* is always optional and those options didn't exist in 4.3.
* If we receive rights, trim the cmsghdr; anything else
* is tossed.
*/
if (control && mp->msg_flags & MSG_COMPAT) {
if (mtod(control, struct cmsghdr *)->cmsg_level !=
SOL_SOCKET ||
mtod(control, struct cmsghdr *)->cmsg_type !=
SCM_RIGHTS) {
mp->msg_controllen = 0;
goto out;
}
control->m_len -= sizeof (struct cmsghdr);
control->m_data += sizeof (struct cmsghdr);
}
#endif
len = mp->msg_controllen;
if (len <= 0 || control == 0)
len = 0;
else {
if (len >= control->m_len)
len = control->m_len;
else
mp->msg_flags |= MSG_CTRUNC;
error = copyout((caddr_t)mtod(control, caddr_t),
(caddr_t)mp->msg_control, (unsigned)len);
}
mp->msg_controllen = len;
}
out:
if (from)
m_freem(from);
if (control)
m_freem(control);
return (error);
}
struct recvfrom_args {
int s;
caddr_t buf;
size_t len;
int flags;
caddr_t from;
int *fromlenaddr;
};
int
recvfrom(p, uap, retval)
struct proc *p;
register struct recvfrom_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov;
int error;
if (uap->fromlenaddr) {
error = copyin((caddr_t)uap->fromlenaddr,
(caddr_t)&msg.msg_namelen, sizeof (msg.msg_namelen));
if (error)
return (error);
} else
msg.msg_namelen = 0;
msg.msg_name = uap->from;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_control = 0;
msg.msg_flags = uap->flags;
return (recvit(p, uap->s, &msg, (caddr_t)uap->fromlenaddr, retval));
}
#ifdef COMPAT_OLDSOCK
int
orecvfrom(p, uap, retval)
struct proc *p;
struct recvfrom_args *uap;
int *retval;
{
uap->flags |= MSG_COMPAT;
return (recvfrom(p, uap, retval));
}
#endif
#ifdef COMPAT_OLDSOCK
struct orecv_args {
int s;
caddr_t buf;
int len;
int flags;
};
int
orecv(p, uap, retval)
struct proc *p;
register struct orecv_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_control = 0;
msg.msg_flags = uap->flags;
return (recvit(p, uap->s, &msg, (caddr_t)0, retval));
}
/*
* Old recvmsg. This code takes advantage of the fact that the old msghdr
* overlays the new one, missing only the flags, and with the (old) access
* rights where the control fields are now.
*/
struct orecvmsg_args {
int s;
struct omsghdr *msg;
int flags;
};
int
orecvmsg(p, uap, retval)
struct proc *p;
register struct orecvmsg_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *iov;
int error;
error = copyin((caddr_t)uap->msg, (caddr_t)&msg,
sizeof (struct omsghdr));
if (error)
return (error);
if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
return (EMSGSIZE);
MALLOC(iov, struct iovec *,
sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
M_WAITOK);
} else
iov = aiov;
msg.msg_flags = uap->flags | MSG_COMPAT;
error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
(unsigned)(msg.msg_iovlen * sizeof (struct iovec)));
if (error)
goto done;
msg.msg_iov = iov;
error = recvit(p, uap->s, &msg, (caddr_t)&uap->msg->msg_namelen, retval);
if (msg.msg_controllen && error == 0)
error = copyout((caddr_t)&msg.msg_controllen,
(caddr_t)&uap->msg->msg_accrightslen, sizeof (int));
done:
if (iov != aiov)
FREE(iov, M_IOV);
return (error);
}
#endif
struct recvmsg_args {
int s;
struct msghdr *msg;
int flags;
};
int
recvmsg(p, uap, retval)
struct proc *p;
register struct recvmsg_args *uap;
int *retval;
{
struct msghdr msg;
struct iovec aiov[UIO_SMALLIOV], *uiov, *iov;
register int error;
error = copyin((caddr_t)uap->msg, (caddr_t)&msg, sizeof (msg));
if (error)
return (error);
if ((u_int)msg.msg_iovlen >= UIO_SMALLIOV) {
if ((u_int)msg.msg_iovlen >= UIO_MAXIOV)
return (EMSGSIZE);
MALLOC(iov, struct iovec *,
sizeof(struct iovec) * (u_int)msg.msg_iovlen, M_IOV,
M_WAITOK);
} else
iov = aiov;
#ifdef COMPAT_OLDSOCK
msg.msg_flags = uap->flags &~ MSG_COMPAT;
#else
msg.msg_flags = uap->flags;
#endif
uiov = msg.msg_iov;
msg.msg_iov = iov;
error = copyin((caddr_t)uiov, (caddr_t)iov,
(unsigned)(msg.msg_iovlen * sizeof (struct iovec)));
if (error)
goto done;
error = recvit(p, uap->s, &msg, (caddr_t)0, retval);
if (!error) {
msg.msg_iov = uiov;
error = copyout((caddr_t)&msg, (caddr_t)uap->msg, sizeof(msg));
}
done:
if (iov != aiov)
FREE(iov, M_IOV);
return (error);
}
struct shutdown_args {
int s;
int how;
};
/* ARGSUSED */
int
shutdown(p, uap, retval)
struct proc *p;
register struct shutdown_args *uap;
int *retval;
{
struct file *fp;
int error;
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
return (soshutdown((struct socket *)fp->f_data, uap->how));
}
struct setsockopt_args {
int s;
int level;
int name;
caddr_t val;
int valsize;
};
/* ARGSUSED */
int
setsockopt(p, uap, retval)
struct proc *p;
register struct setsockopt_args *uap;
int *retval;
{
struct file *fp;
struct mbuf *m = NULL;
int error;
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
if (uap->valsize > MLEN)
return (EINVAL);
if (uap->val) {
m = m_get(M_WAIT, MT_SOOPTS);
if (m == NULL)
return (ENOBUFS);
error = copyin(uap->val, mtod(m, caddr_t), (u_int)uap->valsize);
if (error) {
(void) m_free(m);
return (error);
}
m->m_len = uap->valsize;
}
return (sosetopt((struct socket *)fp->f_data, uap->level,
uap->name, m));
}
struct getsockopt_args {
int s;
int level;
int name;
caddr_t val;
int *avalsize;
};
/* ARGSUSED */
int
getsockopt(p, uap, retval)
struct proc *p;
register struct getsockopt_args *uap;
int *retval;
{
struct file *fp;
struct mbuf *m = NULL;
int valsize, error;
error = getsock(p->p_fd, uap->s, &fp);
if (error)
return (error);
if (uap->val) {
error = copyin((caddr_t)uap->avalsize, (caddr_t)&valsize,
sizeof (valsize));
if (error)
return (error);
} else
valsize = 0;
if ((error = sogetopt((struct socket *)fp->f_data, uap->level,
uap->name, &m)) == 0 && uap->val && valsize && m != NULL) {
if (valsize > m->m_len)
valsize = m->m_len;
error = copyout(mtod(m, caddr_t), uap->val, (u_int)valsize);
if (error == 0)
error = copyout((caddr_t)&valsize,
(caddr_t)uap->avalsize, sizeof (valsize));
}
if (m != NULL)
(void) m_free(m);
return (error);
}
struct pipe_args {
int dummy;
};
/* ARGSUSED */
int
pipe(p, uap, retval)
struct proc *p;
struct pipe_args *uap;
int retval[];
{
register struct filedesc *fdp = p->p_fd;
struct file *rf, *wf;
struct socket *rso, *wso;
int fd, error;
error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0);
if (error)
return (error);
error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0);
if (error)
goto free1;
error = falloc(p, &rf, &fd);
if (error)
goto free2;
retval[0] = fd;
rf->f_flag = FREAD;
rf->f_type = DTYPE_SOCKET;
rf->f_ops = &socketops;
rf->f_data = (caddr_t)rso;
error = falloc(p, &wf, &fd);
if (error)
goto free3;
wf->f_flag = FWRITE;
wf->f_type = DTYPE_SOCKET;
wf->f_ops = &socketops;
wf->f_data = (caddr_t)wso;
retval[1] = fd;
error = unp_connect2(wso, rso);
if (error)
goto free4;
return (0);
free4:
ffree(wf);
fdp->fd_ofiles[retval[1]] = 0;
free3:
ffree(rf);
fdp->fd_ofiles[retval[0]] = 0;
free2:
(void)soclose(wso);
free1:
(void)soclose(rso);
return (error);
}
/*
* Get socket name.
*/
struct getsockname_args {
int fdes;
caddr_t asa;
int *alen;
};
#ifdef COMPAT_OLDSOCK
static int getsockname1(struct proc *, struct getsockname_args *, int [], int);
int
ogetsockname(p, uap, retval)
struct proc *p;
struct getsockname_args *uap;
int *retval;
{
return (getsockname1(p, uap, retval, 1));
}
int
getsockname(p, uap, retval)
struct proc *p;
struct getsockname_args *uap;
int *retval;
{
return (getsockname1(p, uap, retval, 0));
}
static int
getsockname1(p, uap, retval, compat)
struct proc *p;
register struct getsockname_args *uap;
int *retval;
int compat;
#else /* COMPAT_OLDSOCK */
/* ARGSUSED */
int
getsockname1(p, uap, retval)
struct proc *p;
register struct getsockname_args *uap;
int *retval;
#endif /* COMPAT_OLDSOCK */
{
struct file *fp;
register struct socket *so;
struct mbuf *m;
int len, error;
error = getsock(p->p_fd, uap->fdes, &fp);
if (error)
return (error);
error = copyin((caddr_t)uap->alen, (caddr_t)&len, sizeof (len));
if (error)
return (error);
so = (struct socket *)fp->f_data;
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return (ENOBUFS);
error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, 0, m, 0);
if (error)
goto bad;
if (len > m->m_len)
len = m->m_len;
#ifdef COMPAT_OLDSOCK
if (compat)
mtod(m, struct osockaddr *)->sa_family =
mtod(m, struct sockaddr *)->sa_family;
#endif
error = copyout(mtod(m, caddr_t), (caddr_t)uap->asa, (u_int)len);
if (error == 0)
error = copyout((caddr_t)&len, (caddr_t)uap->alen,
sizeof (len));
bad:
m_freem(m);
return (error);
}
/*
* Get name of peer for connected socket.
*/
struct getpeername_args {
int fdes;
caddr_t asa;
int *alen;
};
#ifdef COMPAT_OLDSOCK
static int getpeername1(struct proc *, struct getpeername_args *, int [], int);
int
ogetpeername(p, uap, retval)
struct proc *p;
struct getpeername_args *uap;
int *retval;
{
return (getpeername1(p, uap, retval, 1));
}
int
getpeername(p, uap, retval)
struct proc *p;
struct getpeername_args *uap;
int *retval;
{
return (getpeername1(p, uap, retval, 0));
}
static int
getpeername1(p, uap, retval, compat)
struct proc *p;
register struct getpeername_args *uap;
int *retval;
int compat;
#else /* COMPAT_OLDSOCK */
/* ARGSUSED */
int
getpeername1(p, uap, retval)
struct proc *p;
register struct getpeername_args *uap;
int *retval;
#endif /* COMPAT_OLDSOCK */
{
struct file *fp;
register struct socket *so;
struct mbuf *m;
int len, error;
error = getsock(p->p_fd, uap->fdes, &fp);
if (error)
return (error);
so = (struct socket *)fp->f_data;
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0)
return (ENOTCONN);
error = copyin((caddr_t)uap->alen, (caddr_t)&len, sizeof (len));
if (error)
return (error);
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return (ENOBUFS);
error = (*so->so_proto->pr_usrreq)(so, PRU_PEERADDR, 0, m, 0);
if (error)
goto bad;
if (len > m->m_len)
len = m->m_len;
#ifdef COMPAT_OLDSOCK
if (compat)
mtod(m, struct osockaddr *)->sa_family =
mtod(m, struct sockaddr *)->sa_family;
#endif
error = copyout(mtod(m, caddr_t), (caddr_t)uap->asa, (u_int)len);
if (error)
goto bad;
error = copyout((caddr_t)&len, (caddr_t)uap->alen, sizeof (len));
bad:
m_freem(m);
return (error);
}
int
sockargs(mp, buf, buflen, type)
struct mbuf **mp;
caddr_t buf;
int buflen, type;
{
register struct sockaddr *sa;
register struct mbuf *m;
int error;
if ((u_int)buflen > MLEN) {
#ifdef COMPAT_OLDSOCK
if (type == MT_SONAME && (u_int)buflen <= 112)
buflen = MLEN; /* unix domain compat. hack */
else
#endif
return (EINVAL);
}
m = m_get(M_WAIT, type);
if (m == NULL)
return (ENOBUFS);
m->m_len = buflen;
error = copyin(buf, mtod(m, caddr_t), (u_int)buflen);
if (error)
(void) m_free(m);
else {
*mp = m;
if (type == MT_SONAME) {
sa = mtod(m, struct sockaddr *);
#if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN
if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
sa->sa_family = sa->sa_len;
#endif
sa->sa_len = buflen;
}
}
return (error);
}
int
getsock(fdp, fdes, fpp)
struct filedesc *fdp;
int fdes;
struct file **fpp;
{
register struct file *fp;
if ((unsigned)fdes >= fdp->fd_nfiles ||
(fp = fdp->fd_ofiles[fdes]) == NULL)
return (EBADF);
if (fp->f_type != DTYPE_SOCKET)
return (ENOTSOCK);
*fpp = fp;
return (0);
}