mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-23 11:18:54 +00:00
830681f547
violation of either.
818 lines
23 KiB
C
818 lines
23 KiB
C
/*-
|
|
* Copyright (c) 2001 Networks Associates Technologies, Inc.
|
|
*
|
|
* This software was developed for the FreeBSD Project by NAI Labs, the
|
|
* Security Research Division of Network Associates, Inc. under
|
|
* DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
|
|
* CHATS research program.
|
|
*
|
|
* 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 the above entities nor the names of any
|
|
* contributors of those entities may be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
*
|
|
* $Id$
|
|
*/
|
|
/*
|
|
* Copyright (c) 1982, 1986, 1988, 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_socket.c 8.3 (Berkeley) 4/15/94
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* This file implements LOMAC controls over socket operations. LOMAC
|
|
* gains control of socket operations by interposing on the `struct
|
|
* pr_usrreqs' operations vectors of each `struct protosw'. This code
|
|
* replaces each `struct pr_usrreqs' with an instance of `struct
|
|
* lomac_pr_usrreqs' containing LOMAC socket control functions. These
|
|
* socket control functions implement LOMAC's socket controls, and then
|
|
* call the corresponding socket operations from the original `struct
|
|
* pr_usrreqs'. Each instance of `struct lomac_pr_usrreqs' ends with
|
|
* a pointer to the `struct pr_usrreqs' it replaces. These pointers
|
|
* allow the LOMAC socket control functions to find their corresponding
|
|
* original `struct pr_usrreqs' functions.
|
|
*
|
|
* This file provides the function lomac_initialize_sockets() to turn
|
|
* socket interposition on. Once socket iterposition is turned on,
|
|
* the kernel will begin to call LOMAC's socket control functions.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/resourcevar.h>
|
|
|
|
#include <sys/domain.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/unpcb.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/vnode.h>
|
|
|
|
#include "kernel_interface.h"
|
|
#include "kernel_socket.h"
|
|
#include "kernel_mediate.h"
|
|
#include "kernel_monitor.h"
|
|
#include "lomacfs.h"
|
|
|
|
MALLOC_DEFINE(M_LOMAC_USRREQS, "LOMAC-UR", "LOMAC usrreqs");
|
|
|
|
struct lomac_pr_usrreqs {
|
|
struct pr_usrreqs lomac_pr_usrreqs; /* LOMAC socket control fxns */
|
|
struct pr_usrreqs *orig_pr_usrreqs; /* original socket op vector */
|
|
};
|
|
|
|
int lomac_local_accept(struct socket *, struct sockaddr **);
|
|
int lomac_local_connect(struct socket *, struct sockaddr *, struct thread *);
|
|
int lomac_local_connect2(struct socket *, struct socket *);
|
|
int lomac_local_detach(struct socket *);
|
|
int lomac_local_send( struct socket *, int, struct mbuf *, struct sockaddr *,
|
|
struct mbuf *, struct thread * );
|
|
int lomac_soreceive( struct socket *, struct sockaddr **, struct uio *,
|
|
struct mbuf **, struct mbuf **, int * );
|
|
int lomac_local_soreceive( struct socket *, struct sockaddr **, struct uio *,
|
|
struct mbuf **, struct mbuf **, int * );
|
|
static int monitored_soreceive( struct socket *, struct sockaddr **,
|
|
struct uio *, struct mbuf **, struct mbuf **, int * );
|
|
|
|
/* This usrreqs structure implements LOMAC's controls on local sockets */
|
|
struct pr_usrreqs lomac_local_usrreqs = {
|
|
NULL,
|
|
lomac_local_accept,
|
|
NULL,
|
|
NULL,
|
|
lomac_local_connect,
|
|
lomac_local_connect2,
|
|
NULL,
|
|
lomac_local_detach,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
lomac_local_send,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
lomac_local_soreceive,
|
|
NULL
|
|
};
|
|
|
|
/* This usrreqs structure implements LOMAC's controls on network sockets */
|
|
struct pr_usrreqs lomac_net_usrreqs = {
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
lomac_soreceive,
|
|
NULL
|
|
};
|
|
|
|
static __inline struct pr_usrreqs *
|
|
orig_pr_usrreqs( struct socket *so ) {
|
|
return (((struct lomac_pr_usrreqs *)(so->so_proto->pr_usrreqs))->
|
|
orig_pr_usrreqs);
|
|
}
|
|
|
|
int
|
|
lomac_local_accept( struct socket *so, struct sockaddr **nam ) {
|
|
struct vnode *vp;
|
|
struct unpcb *unp;
|
|
int ret_val; /* value to return to caller */
|
|
|
|
unp = sotounpcb(so);
|
|
if (unp == NULL)
|
|
return (EINVAL);
|
|
if (unp->unp_conn != NULL) {
|
|
vp = unp->unp_vnode = unp->unp_conn->unp_vnode;
|
|
if (vp != NULL)
|
|
vref(vp);
|
|
}
|
|
ret_val = (*orig_pr_usrreqs(so)->pru_accept)(so, nam);
|
|
return (ret_val);
|
|
}
|
|
|
|
int
|
|
lomac_local_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
|
{
|
|
register struct sockaddr_un *soun = (struct sockaddr_un *)nam;
|
|
register struct vnode *vp;
|
|
register struct socket *so2, *so3;
|
|
struct unpcb *unp, *unp2, *unp3;
|
|
int error, len;
|
|
struct nameidata nd;
|
|
char buf[SOCK_MAXADDRLEN];
|
|
|
|
len = nam->sa_len - offsetof(struct sockaddr_un, sun_path);
|
|
if (len <= 0)
|
|
return EINVAL;
|
|
strncpy(buf, soun->sun_path, len);
|
|
buf[len] = 0;
|
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, buf, td);
|
|
error = namei(&nd);
|
|
if (error)
|
|
goto bad2;
|
|
vp = nd.ni_vp;
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
|
if (vp->v_type != VSOCK) {
|
|
error = ENOTSOCK;
|
|
goto bad;
|
|
}
|
|
error = VOP_ACCESS(vp, VWRITE, td->td_proc->p_ucred, td);
|
|
if (error)
|
|
goto bad;
|
|
so2 = vp->v_socket;
|
|
if (so2 == 0) {
|
|
error = ECONNREFUSED;
|
|
goto bad;
|
|
}
|
|
if (so->so_type != so2->so_type) {
|
|
error = EPROTOTYPE;
|
|
goto bad;
|
|
}
|
|
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
|
|
if ((so2->so_options & SO_ACCEPTCONN) == 0 ||
|
|
(so3 = sonewconn3(so2, 0, td)) == 0) {
|
|
error = ECONNREFUSED;
|
|
goto bad;
|
|
}
|
|
unp = sotounpcb(so);
|
|
unp2 = sotounpcb(so2);
|
|
unp3 = sotounpcb(so3);
|
|
if (unp2->unp_addr)
|
|
unp3->unp_addr = (struct sockaddr_un *)
|
|
dup_sockaddr((struct sockaddr *)
|
|
unp2->unp_addr, 1);
|
|
|
|
/*
|
|
* unp_peercred management:
|
|
*
|
|
* The connecter's (client's) credentials are copied
|
|
* from its process structure at the time of connect()
|
|
* (which is now).
|
|
*/
|
|
memset(&unp3->unp_peercred, '\0', sizeof(unp3->unp_peercred));
|
|
unp3->unp_peercred.cr_uid = td->td_proc->p_ucred->cr_uid;
|
|
unp3->unp_peercred.cr_ngroups = td->td_proc->p_ucred->cr_ngroups;
|
|
memcpy(unp3->unp_peercred.cr_groups, td->td_proc->p_ucred->cr_groups,
|
|
sizeof(unp3->unp_peercred.cr_groups));
|
|
unp3->unp_flags |= UNP_HAVEPC;
|
|
/*
|
|
* The receiver's (server's) credentials are copied
|
|
* from the unp_peercred member of socket on which the
|
|
* former called listen(); unp_listen() cached that
|
|
* process's credentials at that time so we can use
|
|
* them now.
|
|
*/
|
|
KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED,
|
|
("unp_connect: listener without cached peercred"));
|
|
memcpy(&unp->unp_peercred, &unp2->unp_peercred,
|
|
sizeof(unp->unp_peercred));
|
|
unp->unp_flags |= UNP_HAVEPC;
|
|
|
|
so2 = so3;
|
|
}
|
|
error = lomac_local_connect2(so, so2);
|
|
bad:
|
|
vput(vp);
|
|
bad2:
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
lomac_local_connect2( struct socket *so1, struct socket *so2 ) {
|
|
struct vnode *vp;
|
|
int ret_val; /* value to return to caller */
|
|
|
|
if (so2->so_head != NULL) {
|
|
vp = sotounpcb(so2->so_head)->unp_vnode;
|
|
if (vp != NULL) {
|
|
sotounpcb(so1)->unp_vnode = vp;
|
|
vref(vp);
|
|
}
|
|
}
|
|
ret_val = (*orig_pr_usrreqs(so1)->pru_connect2)(so1, so2);
|
|
return (ret_val);
|
|
}
|
|
|
|
int
|
|
lomac_local_detach( struct socket *so ) {
|
|
int ret_val; /* value to return to caller */
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
if (unp == NULL)
|
|
return (EINVAL);
|
|
if (unp->unp_vnode != NULL && unp->unp_vnode->v_socket != so) {
|
|
vrele(unp->unp_vnode);
|
|
unp->unp_vnode = NULL;
|
|
}
|
|
ret_val = (*orig_pr_usrreqs(so)->pru_detach)(so);
|
|
return (ret_val);
|
|
}
|
|
|
|
int
|
|
lomac_local_send( struct socket *so, int flags, struct mbuf *m,
|
|
struct sockaddr *addr, struct mbuf *control, struct thread *td ) {
|
|
struct vnode *vp;
|
|
struct unpcb *unp = sotounpcb(so);
|
|
int error;
|
|
|
|
/* printf( "pid %d local send\n", p->p_pid ); */
|
|
if (unp == NULL) {
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
if (so->so_type == SOCK_DGRAM) {
|
|
if (addr != NULL) {
|
|
if (unp->unp_conn != NULL) {
|
|
error = EISCONN;
|
|
goto out;
|
|
}
|
|
error = lomac_local_connect(so, addr, td);
|
|
if (error)
|
|
goto out;
|
|
} else if (unp->unp_conn == NULL) {
|
|
error = ENOTCONN;
|
|
goto out;
|
|
}
|
|
} else if ((so->so_state & SS_ISCONNECTED) == 0) {
|
|
if (addr != NULL) {
|
|
error = lomac_local_connect(so, addr, td);
|
|
if (error)
|
|
goto out; /* XXX */
|
|
} else {
|
|
error = ENOTCONN;
|
|
goto out;
|
|
}
|
|
}
|
|
vp = unp->unp_vnode;
|
|
if (vp != NULL) {
|
|
lomac_object_t lobj;
|
|
|
|
lobj.lo_type = VISLOMAC(vp) ? LO_TYPE_LVNODE : LO_TYPE_UVNODE;
|
|
lobj.lo_object.vnode = vp;
|
|
if (!mediate_subject_object("send", td->td_proc, &lobj)) {
|
|
error = EPERM;
|
|
goto out;
|
|
}
|
|
} else {
|
|
/*
|
|
* This is a send to a socket in a socketpair() pair.
|
|
* Mark both sockets in pair with the appropriate level.
|
|
*/
|
|
lomac_object_t lobj1, lobj2;
|
|
lattr_t lattr;
|
|
|
|
lobj1.lo_type = LO_TYPE_SOCKETPAIR;
|
|
lobj1.lo_object.socket = so;
|
|
if ((error = monitor_pipe_write(td->td_proc, &lobj1)) != 0)
|
|
goto out;
|
|
lobj2.lo_type = LO_TYPE_SOCKETPAIR;
|
|
lobj2.lo_object.socket = unp->unp_conn->unp_socket;
|
|
get_object_lattr(&lobj1, &lattr);
|
|
set_object_lattr(&lobj2, lattr);
|
|
}
|
|
error = (*orig_pr_usrreqs(so)->pru_send)( so, flags, m, NULL,
|
|
control, td );
|
|
if (addr != NULL && so->so_type == SOCK_DGRAM)
|
|
(*orig_pr_usrreqs(so)->pru_disconnect)(so);
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
lomac_local_soreceive(struct socket *so, struct sockaddr **paddr,
|
|
struct uio *uio, struct mbuf **mp0, struct mbuf **controlp, int *flagsp) {
|
|
lomac_object_t lobj;
|
|
struct vnode *vp;
|
|
struct unpcb *unp = sotounpcb(so);
|
|
int ret_val; /* value to return to caller */
|
|
|
|
if (unp == NULL)
|
|
return (EINVAL);
|
|
vp = unp->unp_vnode;
|
|
if (vp != NULL) {
|
|
lobj.lo_type = VISLOMAC(vp) ? LO_TYPE_LVNODE : LO_TYPE_UVNODE;
|
|
lobj.lo_object.vnode = vp;
|
|
ret_val = monitor_read_object(uio->uio_td->td_proc, &lobj);
|
|
if (ret_val == 0)
|
|
ret_val = (*orig_pr_usrreqs(so)->pru_soreceive)(so,
|
|
paddr, uio, mp0, controlp, flagsp);
|
|
} else {
|
|
/*
|
|
* This is a receive from a socket in a pair created by
|
|
* socketpair(). Monitor it as we would a pipe read,
|
|
* except for allowing for arbitrary numbers of sleeps.
|
|
*/
|
|
ret_val = monitored_soreceive(so, paddr, uio, mp0, controlp,
|
|
flagsp);
|
|
}
|
|
return (ret_val);
|
|
}
|
|
|
|
int
|
|
lomac_soreceive(struct socket *so, struct sockaddr **paddr, struct uio *uio,
|
|
struct mbuf **mp0, struct mbuf **controlp, int *flagsp) {
|
|
int ret_val; /* value to return to caller */
|
|
|
|
(void)monitor_read_net_socket(uio->uio_td->td_proc);
|
|
ret_val = (*orig_pr_usrreqs(so)->pru_soreceive)(so, paddr, uio, mp0,
|
|
controlp, flagsp);
|
|
return (ret_val);
|
|
}
|
|
|
|
int
|
|
lomac_initialize_sockets(void) {
|
|
struct domain *dp; /* used to traverse global `domains' list */
|
|
struct protosw *pr; /* used to traverse each domain's protosw list */
|
|
struct lomac_pr_usrreqs *lomac_pr_usrreqs; /* lomac usrreqs vectors */
|
|
void (**lfuncp)(void), (**funcp)(void);
|
|
int n, nreq;
|
|
|
|
nreq = sizeof(struct pr_usrreqs) / sizeof(void (*)(void));
|
|
for (dp = domains; dp; dp = dp->dom_next) {
|
|
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
|
|
|
|
lomac_pr_usrreqs = (struct lomac_pr_usrreqs *)malloc(
|
|
sizeof(struct lomac_pr_usrreqs), M_LOMAC_USRREQS,
|
|
M_WAITOK);
|
|
|
|
if (dp->dom_family == AF_LOCAL)
|
|
memcpy(lomac_pr_usrreqs, &lomac_local_usrreqs,
|
|
sizeof(struct pr_usrreqs));
|
|
else
|
|
memcpy(lomac_pr_usrreqs, &lomac_net_usrreqs,
|
|
sizeof(struct pr_usrreqs));
|
|
/*
|
|
* Do sparse allocation of user requests and only
|
|
* override the ones we need to (to reduce overhead).
|
|
*/
|
|
lfuncp = (void (**)(void))lomac_pr_usrreqs;
|
|
funcp = (void (**)(void))pr->pr_usrreqs;
|
|
for (n = 0; n < nreq; n++) {
|
|
if (lfuncp[n] == NULL)
|
|
lfuncp[n] = funcp[n];
|
|
}
|
|
lomac_pr_usrreqs->orig_pr_usrreqs = pr->pr_usrreqs;
|
|
pr->pr_usrreqs = (struct pr_usrreqs *)lomac_pr_usrreqs;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
int
|
|
lomac_uninitialize_sockets(void) {
|
|
struct domain *dp; /* used to traverse global `domains' list */
|
|
struct protosw *pr; /* used to traverse each domain's protosw list */
|
|
struct lomac_pr_usrreqs *lomac_pr_usrreqs; /* lomac usrreqs vectors */
|
|
|
|
for (dp = domains; dp; dp = dp->dom_next) {
|
|
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
|
|
lomac_pr_usrreqs = (struct lomac_pr_usrreqs *)
|
|
pr->pr_usrreqs;
|
|
pr->pr_usrreqs = lomac_pr_usrreqs->orig_pr_usrreqs;
|
|
free(lomac_pr_usrreqs, M_LOMAC_USRREQS);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
|
|
/*
|
|
* Implement receive operations on a socket.
|
|
* We depend on the way that records are added to the sockbuf
|
|
* by sbappend*. In particular, each record (mbufs linked through m_next)
|
|
* must begin with an address if the protocol so specifies,
|
|
* followed by an optional mbuf or mbufs containing ancillary data,
|
|
* and then zero or more mbufs of data.
|
|
* In order to avoid blocking network interrupts for the entire time here,
|
|
* we splx() while doing the actual copy to user space.
|
|
* Although the sockbuf is locked, new data may still be appended,
|
|
* and thus we must maintain consistency of the sockbuf during that time.
|
|
*
|
|
* The caller may receive the data as a single mbuf chain by supplying
|
|
* an mbuf **mp0 for use in returning the chain. The uio is then used
|
|
* only for the count in uio_resid.
|
|
*/
|
|
static int
|
|
monitored_soreceive(so, psa, uio, mp0, controlp, flagsp)
|
|
register struct socket *so;
|
|
struct sockaddr **psa;
|
|
struct uio *uio;
|
|
struct mbuf **mp0;
|
|
struct mbuf **controlp;
|
|
int *flagsp;
|
|
{
|
|
lomac_object_t lobj;
|
|
register struct mbuf *m, **mp;
|
|
register int flags, len, error, s, offset;
|
|
struct protosw *pr = so->so_proto;
|
|
struct mbuf *nextrecord;
|
|
struct proc *p;
|
|
int moff, type = 0;
|
|
int orig_resid = uio->uio_resid;
|
|
|
|
mp = mp0;
|
|
if (psa)
|
|
*psa = 0;
|
|
if (controlp)
|
|
*controlp = 0;
|
|
if (flagsp)
|
|
flags = *flagsp &~ MSG_EOR;
|
|
else
|
|
flags = 0;
|
|
lobj.lo_type = LO_TYPE_SOCKETPAIR;
|
|
lobj.lo_object.socket = so;
|
|
if (uio->uio_td != NULL) /* XXX */
|
|
p = uio->uio_td->td_proc;
|
|
else
|
|
p = curthread->td_proc;
|
|
if (flags & MSG_OOB) {
|
|
m = m_get(M_TRYWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return (ENOBUFS);
|
|
error = (*pr->pr_usrreqs->pru_rcvoob)(so, m, flags & MSG_PEEK);
|
|
if (error)
|
|
goto bad;
|
|
do {
|
|
monitor_read_object(p, &lobj);
|
|
error = uiomove(mtod(m, caddr_t),
|
|
(int) min(uio->uio_resid, m->m_len), uio);
|
|
m = m_free(m);
|
|
} while (uio->uio_resid && error == 0 && m);
|
|
bad:
|
|
if (m)
|
|
m_freem(m);
|
|
return (error);
|
|
}
|
|
if (mp)
|
|
*mp = (struct mbuf *)0;
|
|
if (so->so_state & SS_ISCONFIRMING && uio->uio_resid)
|
|
(*pr->pr_usrreqs->pru_rcvd)(so, 0);
|
|
|
|
restart:
|
|
error = sblock(&so->so_rcv, SBLOCKWAIT(flags));
|
|
if (error)
|
|
return (error);
|
|
s = splnet();
|
|
|
|
m = so->so_rcv.sb_mb;
|
|
/*
|
|
* If we have less data than requested, block awaiting more
|
|
* (subject to any timeout) if:
|
|
* 1. the current count is less than the low water mark, or
|
|
* 2. MSG_WAITALL is set, and it is possible to do the entire
|
|
* receive operation at once if we block (resid <= hiwat).
|
|
* 3. MSG_DONTWAIT is not set
|
|
* If MSG_WAITALL is set but resid is larger than the receive buffer,
|
|
* we have to do the receive in sections, and thus risk returning
|
|
* a short count if a timeout or signal occurs after we start.
|
|
*/
|
|
if (m == 0 || (((flags & MSG_DONTWAIT) == 0 &&
|
|
so->so_rcv.sb_cc < uio->uio_resid) &&
|
|
(so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
|
|
((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) &&
|
|
m->m_nextpkt == 0 && (pr->pr_flags & PR_ATOMIC) == 0)) {
|
|
KASSERT(m != 0 || !so->so_rcv.sb_cc,
|
|
("receive: m == %p so->so_rcv.sb_cc == %lu",
|
|
m, so->so_rcv.sb_cc));
|
|
if (so->so_error) {
|
|
if (m)
|
|
goto dontblock;
|
|
error = so->so_error;
|
|
if ((flags & MSG_PEEK) == 0)
|
|
so->so_error = 0;
|
|
goto release;
|
|
}
|
|
if (so->so_state & SS_CANTRCVMORE) {
|
|
if (m)
|
|
goto dontblock;
|
|
else
|
|
goto release;
|
|
}
|
|
for (; m; m = m->m_next)
|
|
if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
|
|
m = so->so_rcv.sb_mb;
|
|
goto dontblock;
|
|
}
|
|
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
|
|
(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
|
|
error = ENOTCONN;
|
|
goto release;
|
|
}
|
|
if (uio->uio_resid == 0)
|
|
goto release;
|
|
if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) {
|
|
error = EWOULDBLOCK;
|
|
goto release;
|
|
}
|
|
sbunlock(&so->so_rcv);
|
|
error = sbwait(&so->so_rcv);
|
|
splx(s);
|
|
if (error)
|
|
return (error);
|
|
goto restart;
|
|
}
|
|
dontblock:
|
|
if (uio->uio_td)
|
|
p->p_stats->p_ru.ru_msgrcv++;
|
|
nextrecord = m->m_nextpkt;
|
|
if (pr->pr_flags & PR_ADDR) {
|
|
KASSERT(m->m_type == MT_SONAME, ("receive 1a"));
|
|
orig_resid = 0;
|
|
if (psa)
|
|
*psa = dup_sockaddr(mtod(m, struct sockaddr *),
|
|
mp0 == 0);
|
|
if (flags & MSG_PEEK) {
|
|
m = m->m_next;
|
|
} else {
|
|
sbfree(&so->so_rcv, m);
|
|
MFREE(m, so->so_rcv.sb_mb);
|
|
m = so->so_rcv.sb_mb;
|
|
}
|
|
}
|
|
while (m && m->m_type == MT_CONTROL && error == 0) {
|
|
if (flags & MSG_PEEK) {
|
|
if (controlp)
|
|
*controlp = m_copy(m, 0, m->m_len);
|
|
m = m->m_next;
|
|
} else {
|
|
sbfree(&so->so_rcv, m);
|
|
so->so_rcv.sb_mb = m->m_next;
|
|
m->m_next = NULL;
|
|
if (pr->pr_domain->dom_externalize)
|
|
error =
|
|
(*pr->pr_domain->dom_externalize)(m, controlp);
|
|
else if (controlp)
|
|
*controlp = m;
|
|
else
|
|
m_freem(m);
|
|
m = so->so_rcv.sb_mb;
|
|
}
|
|
if (controlp) {
|
|
orig_resid = 0;
|
|
do
|
|
controlp = &(*controlp)->m_next;
|
|
while (*controlp != NULL);
|
|
}
|
|
}
|
|
if (m) {
|
|
if ((flags & MSG_PEEK) == 0)
|
|
m->m_nextpkt = nextrecord;
|
|
type = m->m_type;
|
|
if (type == MT_OOBDATA)
|
|
flags |= MSG_OOB;
|
|
}
|
|
moff = 0;
|
|
offset = 0;
|
|
while (m && uio->uio_resid > 0 && error == 0) {
|
|
if (m->m_type == MT_OOBDATA) {
|
|
if (type != MT_OOBDATA)
|
|
break;
|
|
} else if (type == MT_OOBDATA)
|
|
break;
|
|
else
|
|
KASSERT(m->m_type == MT_DATA || m->m_type == MT_HEADER,
|
|
("receive 3"));
|
|
so->so_state &= ~SS_RCVATMARK;
|
|
len = uio->uio_resid;
|
|
if (so->so_oobmark && len > so->so_oobmark - offset)
|
|
len = so->so_oobmark - offset;
|
|
if (len > m->m_len - moff)
|
|
len = m->m_len - moff;
|
|
/*
|
|
* If mp is set, just pass back the mbufs.
|
|
* Otherwise copy them out via the uio, then free.
|
|
* Sockbuf must be consistent here (points to current mbuf,
|
|
* it points to next record) when we drop priority;
|
|
* we must note any additions to the sockbuf when we
|
|
* block interrupts again.
|
|
*/
|
|
if (mp == 0) {
|
|
splx(s);
|
|
monitor_read_object(p, &lobj);
|
|
error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio);
|
|
s = splnet();
|
|
if (error)
|
|
goto release;
|
|
} else
|
|
uio->uio_resid -= len;
|
|
if (len == m->m_len - moff) {
|
|
if (m->m_flags & M_EOR)
|
|
flags |= MSG_EOR;
|
|
if (flags & MSG_PEEK) {
|
|
m = m->m_next;
|
|
moff = 0;
|
|
} else {
|
|
nextrecord = m->m_nextpkt;
|
|
sbfree(&so->so_rcv, m);
|
|
if (mp) {
|
|
*mp = m;
|
|
mp = &m->m_next;
|
|
so->so_rcv.sb_mb = m = m->m_next;
|
|
*mp = (struct mbuf *)0;
|
|
} else {
|
|
MFREE(m, so->so_rcv.sb_mb);
|
|
m = so->so_rcv.sb_mb;
|
|
}
|
|
if (m)
|
|
m->m_nextpkt = nextrecord;
|
|
}
|
|
} else {
|
|
if (flags & MSG_PEEK)
|
|
moff += len;
|
|
else {
|
|
if (mp)
|
|
*mp = m_copym(m, 0, len, M_TRYWAIT);
|
|
m->m_data += len;
|
|
m->m_len -= len;
|
|
so->so_rcv.sb_cc -= len;
|
|
}
|
|
}
|
|
if (so->so_oobmark) {
|
|
if ((flags & MSG_PEEK) == 0) {
|
|
so->so_oobmark -= len;
|
|
if (so->so_oobmark == 0) {
|
|
so->so_state |= SS_RCVATMARK;
|
|
break;
|
|
}
|
|
} else {
|
|
offset += len;
|
|
if (offset == so->so_oobmark)
|
|
break;
|
|
}
|
|
}
|
|
if (flags & MSG_EOR)
|
|
break;
|
|
/*
|
|
* If the MSG_WAITALL flag is set (for non-atomic socket),
|
|
* we must not quit until "uio->uio_resid == 0" or an error
|
|
* termination. If a signal/timeout occurs, return
|
|
* with a short count but without error.
|
|
* Keep sockbuf locked against other readers.
|
|
*/
|
|
while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 &&
|
|
!sosendallatonce(so) && !nextrecord) {
|
|
if (so->so_error || so->so_state & SS_CANTRCVMORE)
|
|
break;
|
|
/*
|
|
* Notify the protocol that some data has been
|
|
* drained before blocking.
|
|
*/
|
|
if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
|
|
(*pr->pr_usrreqs->pru_rcvd)(so, flags);
|
|
error = sbwait(&so->so_rcv);
|
|
if (error) {
|
|
sbunlock(&so->so_rcv);
|
|
splx(s);
|
|
return (0);
|
|
}
|
|
m = so->so_rcv.sb_mb;
|
|
if (m)
|
|
nextrecord = m->m_nextpkt;
|
|
}
|
|
}
|
|
|
|
if (m && pr->pr_flags & PR_ATOMIC) {
|
|
flags |= MSG_TRUNC;
|
|
if ((flags & MSG_PEEK) == 0)
|
|
(void) sbdroprecord(&so->so_rcv);
|
|
}
|
|
if ((flags & MSG_PEEK) == 0) {
|
|
if (m == 0)
|
|
so->so_rcv.sb_mb = nextrecord;
|
|
if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
|
|
(*pr->pr_usrreqs->pru_rcvd)(so, flags);
|
|
}
|
|
if (orig_resid == uio->uio_resid && orig_resid &&
|
|
(flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
|
|
sbunlock(&so->so_rcv);
|
|
splx(s);
|
|
goto restart;
|
|
}
|
|
|
|
if (flagsp)
|
|
*flagsp |= flags;
|
|
release:
|
|
sbunlock(&so->so_rcv);
|
|
splx(s);
|
|
return (error);
|
|
}
|