1
0
Files
irix-657m-src/irix/kern/bsd/socket/uipc_socket1.c
2022-09-29 17:59:04 +03:00

661 lines
15 KiB
C

/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#)uipc_socket.c 7.10 (Berkeley) 6/29/88
*/
#ident "$Revision: 1.49 $"
#include "tcp-param.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/cmn_err.h"
#include "sys/cred.h"
#include "sys/domain.h"
#include "sys/errno.h"
#include "ksys/vfile.h"
#include "sys/kthread.h"
#include "sys/mbuf.h"
#include "sys/proc.h"
#include "sys/protosw.h"
#include "sys/signal.h"
#include "sys/socket.h"
#include "sys/socketvar.h"
#include "sys/strmp.h"
#include "sys/uio.h"
#include "sys/systm.h"
#include "sys/kmem.h"
#include "bstring.h"
#include "sys/sat.h"
#include "sys/capability.h"
#include "sys/atomic_ops.h"
#include <sys/vsocket.h>
#include <sys/fcntl.h>
/* Trusted IRIX */
#include <sys/mac_label.h>
#include <sys/sesmgr.h>
#include <net/soioctl.h> /* XXX fold into ioctl.h */
extern struct pollhead *phalloc(int);
extern void phfree(struct pollhead *);
extern void sbdroprecord(struct sockbuf *sb);
extern int soaccept(struct socket *, struct mbuf *);
extern int sockargs(struct mbuf **, void *, int, int);
extern int rtioctl(int, caddr_t); /* bsd/net/route.c */
int
uipc_accept(bhv_desc_t *bdp,
caddr_t name,
sysarg_t *anamelen,
int namelen,
rval_t *rvp,
struct vsocket **nvso)
{
int error, fd;
struct vfile *fp, *fp2;
struct mbuf nam;
NETSPL_DECL(s)
struct socket *aso;
struct vsocket *vso = 0;
register struct socket *so = bhvtos(bdp);
restart:
if (!(so->so_proto->pr_flags & PR_CONNREQUIRED))
return EOPNOTSUPP;
if ((so->so_options & SO_ACCEPTCONN) == 0)
return EINVAL;
SOCKET_QLOCK(so, s);
if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
SOCKET_QUNLOCK(so, s);
return EWOULDBLOCK;
}
while (so->so_qlen == 0 && so->so_error == 0) {
int rv;
if (so->so_state & SS_CANTRCVMORE) {
so->so_error = ECONNABORTED;
break;
}
rv = SOCKET_WAIT_SIG(&so->so_timeosem, PZERO+1,
&so->so_qlock, s);
SOCKET_QLOCK(so, s);
if (rv) {
SOCKET_QUNLOCK(so, s);
return EINTR;
}
}
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
SOCKET_QUNLOCK(so, s);
return error;
}
aso = so->so_q;
ASSERT(mutex_mine(&aso->so_sem) == 0);
if (!SOCKET_TRYLOCK(aso)) {
SO_HOLD(aso);
SOCKET_QUNLOCK(so, s);
ASSERT(mutex_mine(&aso->so_sem) == 0);
/*
* This lock should be safe due to hold done above; sofree()
* should not free aso out from under us.
*/
SOCKET_LOCK(aso);
if (SO_RELE(aso) == 1) {
/* freed */
goto restart;
}
if (aso->so_head == 0) {
/* someone else has a reference, unlock and continue */
SOCKET_UNLOCK(aso);
goto restart;
}
/*
* This should be OK, because:
* - this is the lock order the lower half uses
* - if we are on a single-CPU system, nobody should
* have the queue lock, because it is a spinlock and
* they couldn't have been preempted.
* - if this is an MP system, any other thread would
* have to do the deadlock avoidance we just did, and
* since we have the lock on aso, they will all be
* blocked, and will soon relinquish the queue lock
* - soclose() on so can't be running because we are
* in uipc_accept() on so.
*/
SOCKET_QLOCK(so, s);
ASSERT(aso->so_head);
}
/*
* Take socket off of accept queue before calling vsowrap(), since
* it could sleep in kmem_zone_alloc().
*/
if (soqremque(aso, 1) == 0)
panic("accept");
SOCKET_QUNLOCK(so, s);
error = vsowrap(aso, &vso, AF_INET, aso->so_type,
aso->so_proto->pr_protocol);
if (error) {
/* XXX assume no behavior chain if error */
goto bail;
}
/*
* prevent protocol from dropping this connection while we get a
* vnode. Notice that the following code is not necessary on MP
* since we have aso locked already.
*/
aso->so_state &= ~SS_NOFDREF;
if (nvso == NULL) {
if (error = makevsock(&fp, &fd)) {
bhv_remove(&(vso->vs_bh), &(aso->so_bhv));
aso->so_state |= SS_NOFDREF;
bail:
if (aso->so_pcb == 0) {
if (sofree(aso) == 0) {
SOCKET_UNLOCK(aso);
}
} else {
if (soabort(aso)) {
SOCKET_UNLOCK(aso);
}
}
if (vso) {
/* might be NULL if vsowrap() failed */
vsocket_release(vso);
}
return error;
}
}
/* XXX might need to initialize more fields here -- SCA */
nam.m_off = MMINOFF;
nam.m_len = 0;
nam.m_next = 0;
nam.m_type = MT_SONAME;
if (aso->so_pcb == 0 || (aso->so_state & SS_CLOSING)) {
/* lost the race: undo allocations and try again */
bhv_remove(&(vso->vs_bh), &(aso->so_bhv));
aso->so_state |= SS_NOFDREF;
if (aso->so_pcb == 0) {
if (sofree(aso) == 0) {
SOCKET_UNLOCK(aso);
}
} else {
if (soabort(aso)) {
SOCKET_UNLOCK(aso);
}
}
if (nvso == NULL) {
vfile_alloc_undo(fd, fp);
}
vsocket_release(vso);
goto restart;
}
if (nvso == NULL) {
vfile_ready(fp, vso);
}
#ifdef DEBUG
error = soaccept(aso, &nam);
ASSERT(!error);
#else
(void)soaccept(aso, &nam);
#endif
SOCKET_UNLOCK(aso);
if (sesmgr_enabled) {
if (error == 0 && (error = sesmgr_samp_accept(aso))) {
SOCKET_LOCK(aso);
if (soabort(aso))
SOCKET_UNLOCK(aso);
}
}
#ifdef sgi /* Audit */
_SAT_BSDIPC_CREATE(fd, aso,
aso->so_proto->pr_domain->dom_family,
aso->so_proto->pr_type, 0);
_SAT_BSDIPC_ADDR(fd, aso, &nam, 0);
#endif
if (name) {
if (namelen > nam.m_len)
/* SHOULD COPY OUT A CHAIN HERE */
error = copyout(mtod(&nam, caddr_t), name, nam.m_len);
else
error = copyout(mtod(&nam, caddr_t), name, namelen);
if (error) {
if (nvso == NULL) {
error = closefd (fd, &fp2);
ASSERT(error == 0);
ASSERT(fp == fp2);
vfile_close(fp);
}
return EFAULT;
}
namelen = nam.m_len;
error = copyout(&namelen, anamelen, sizeof (int));
if (error) {
if (nvso == NULL) {
error = closefd (fd, &fp2);
ASSERT(error == 0);
ASSERT(fp == fp2);
vfile_close(fp);
}
return EFAULT;
}
}
if (nvso == NULL) {
rvp->r_val1 = fd;
} else {
rvp->r_val1 = 0;
*nvso = vso;
}
return error;
}
/* ARGSUSED */
int
uipc_connect(bhv_desc_t *bdp, caddr_t name, int namlen)
{
int error = 0;
struct mbuf *nam;
register struct socket *so = bhvtos(bdp);
SOCKET_LOCK(so);
if ((so->so_state & SS_NBIO) &&
(so->so_state & SS_ISCONNECTING)) {
SOCKET_UNLOCK(so);
return EALREADY;
}
error = sockargs(&nam, name, namlen, MT_SONAME);
if (error) {
SOCKET_UNLOCK(so);
return error;
}
error = _SESMGR_SAMP_INIT_DATA(so, nam);
if (error)
goto bad;
error = soconnect(so, nam);
if (error)
goto bad;
if ((so->so_state & (SS_NBIO|SS_ISCONNECTING)) ==
(SS_NBIO|SS_ISCONNECTING)) {
SOCKET_UNLOCK(so);
m_freem(nam);
return EINPROGRESS;
}
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
int rv;
SOCKET_MPUNLOCK_SOTIMEO(so, PZERO+1, rv);
SOCKET_LOCK(so);
if (rv) {
/*
* If the connection call get's interrupted by
* a signal, then leave this system call, but leave
* the SS_ISCONNECTING flag set and set the
* SS_CONNINTERRUPTED flag so that if we retry this
* connect call, we can resume where we left off.
*/
so->so_state |= SS_CONNINTERRUPTED;
SOCKET_UNLOCK(so);
m_freem(nam);
return EINTR;
}
}
error = so->so_error;
so->so_error = 0;
bad:
so->so_state &= ~SS_ISCONNECTING;
SOCKET_UNLOCK(so);
m_freem(nam);
return error;
}
/* ARGSUSED */
int
uipc_getsockname(bhv_desc_t *bdp, caddr_t asa, sysarg_t *alen, int len, rval_t *rvp)
{
int error;
struct mbuf *m;
register struct socket *so = bhvtos(bdp);
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return ENOBUFS;
SOCKET_LOCK(so);
error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, 0, m, 0);
SOCKET_UNLOCK(so);
if (error)
goto bad;
if (len > m->m_len)
error = copyout(mtod(m, caddr_t), asa, m->m_len);
else
error = copyout(mtod(m, caddr_t), asa, len);
if (error)
goto bad;
len = m->m_len;
error = copyout(&len, alen, sizeof (len));
bad:
m_freem(m);
return error;
}
/* ARGSUSED */
int
uipc_getpeername(bhv_desc_t *bdp, caddr_t asa, sysarg_t *alen, rval_t *rvp)
{
int error;
struct mbuf *m;
int len;
register struct socket *so = bhvtos(bdp);
m = m_getclr(M_WAIT, MT_SONAME);
if (m == NULL)
return ENOBUFS;
error = copyin(alen, &len, sizeof (len));
if (error)
goto bad;
SOCKET_LOCK(so);
/* doing a getpeername() on a shutdown socket returns EINVAL.
* X/OPEN conformance. */
if ((so->so_state & SS_CANTRCVMORE)
&& (so->so_state & SS_CANTSENDMORE)) {
SOCKET_UNLOCK(so);
m_freem(m);
return EINVAL;
}
if ((so->so_state & SS_ISCONNECTED) == 0) {
SOCKET_UNLOCK(so);
m_freem(m);
return ENOTCONN;
}
error = (*so->so_proto->pr_usrreq)(so, PRU_PEERADDR, 0, m, 0);
SOCKET_UNLOCK(so);
if (error)
goto bad;
if (len > m->m_len)
error = copyout(mtod(m, caddr_t), asa, m->m_len);
else
error = copyout(mtod(m, caddr_t), asa, len);
if (error)
goto bad;
len = m->m_len;
error = copyout(&len, alen, sizeof (len));
bad:
m_freem(m);
return error;
}
/*
* do IOCTLs for sockets
*/
/* ARGSUSED */
int
soioctl(bhv_desc_t *bdp,
int cmd,
void *arg,
int flag,
struct cred *cr,
rval_t *rvalp)
{
int error = 0;
register u_int size;
long data[(IOCPARM_MASK+1)/sizeof(long)];
register struct socket *so = bhvtos(bdp);
/*
* Interpret high order word to find
* amount of data to be copied to/from the user address space.
*/
size = (cmd &~ (IOC_INOUT|IOC_VOID)) >> 16;
if (size > sizeof (data))
return EFAULT;
if (cmd & IOC_IN) {
if (size != 0) {
if (error = copyin((caddr_t)arg, data, size))
return error;
} else
*(caddr_t *)data = (caddr_t)arg;
} else if ((cmd & IOC_OUT) && size != 0) {
/*
* Zero the buffer on the stack so the user
* always gets back something deterministic.
*/
bzero(data, size);
} else if (cmd & IOC_VOID) {
*(caddr_t *)data = (caddr_t)arg;
}
SOCKET_LOCK(so);
switch (cmd) {
case FIONBIO:
if (*(int *)data)
so->so_state |= SS_NBIO;
else
so->so_state &= ~SS_NBIO;
break;
case FIOASYNC:
if (*(int *)data) {
so->so_state |= SS_ASYNC;
so->so_rcv.sb_flags |= SB_ASYNC;
so->so_snd.sb_flags |= SB_ASYNC;
} else {
so->so_state &= ~SS_ASYNC;
so->so_rcv.sb_flags &= ~SB_ASYNC;
so->so_snd.sb_flags &= ~SB_ASYNC;
}
break;
case FIONREAD:
*(int *)data = so->so_rcv.sb_cc;
break;
case SIOCNREAD:
if ((*(int *)data = so->so_rcv.sb_cc) == 0) {
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
} else if (so->so_state & SS_CANTRCVMORE) {
/*
* For UDP sockets, this isn't really true...
*/
error = ECONNRESET;
}
}
break;
case FIOSETOWN:
case SIOCSPGRP:
so->so_pgid = *(int *)data;
break;
case FIOGETOWN:
case SIOCGPGRP:
*(int *)data = so->so_pgid;
break;
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
break;
/* Trusted IRIX cases */
case SIOCGETLABEL:
error = _SIOCGETLABEL(so, (caddr_t)arg);
break;
case SIOCSETLABEL:
error = _SIOCSETLABEL(so, (caddr_t)arg);
break;
default:
/*
* Interface/routing/protocol specific ioctls:
* interface and routing ioctls should have a
* different entry since a socket is unnecessary
*/
#define cmdbyte(x) (((x) >> 8) & 0xff)
if (cmdbyte(cmd) == 'i')
error = (ifioctl(so, cmd, (caddr_t)data));
else if (cmdbyte(cmd) == 'r')
error = (rtioctl(cmd, (caddr_t)data));
else
error = (*so->so_proto->pr_usrreq)(so,
PRU_CONTROL, (struct mbuf *)(NULL+cmd),
(struct mbuf *)data, (struct mbuf *)0);
}
SOCKET_UNLOCK(so);
/*
* Copy any data to user, size was
* already set and checked above.
*/
if (!error && (cmd & IOC_OUT) && size)
error = copyout(data, (caddr_t)arg, size);
return error;
}
/* ARGSUSED */
int
uipc_setfl1(bhv_desc_t *bdp,
int oflags,
int nflags,
struct cred *cr)
{
register struct socket *so = bhvtos(bdp);
SOCKET_LOCK(so);
if (nflags & (FNONBLK|FNDELAY))
so->so_state |= SS_NBIO;
else
so->so_state &= ~SS_NBIO;
if (nflags & FASYNC) {
so->so_state |= SS_ASYNC;
so->so_rcv.sb_flags |= SB_ASYNC;
so->so_snd.sb_flags |= SB_ASYNC;
} else {
so->so_state &= ~SS_ASYNC;
so->so_rcv.sb_flags &= ~SB_ASYNC;
so->so_snd.sb_flags &= ~SB_ASYNC;
}
SOCKET_UNLOCK(so);
return 0;
}
/*ARGSUSED*/
int
uipc_getattr1(bhv_desc_t *bdp,
struct vattr *vap,
int flags,
struct cred *cr)
{
register struct socket *so = bhvtos(bdp);
int error;
bzero(vap, sizeof *vap);
vap->va_type = VSOCK;
SOCKET_LOCK(so);
error = (*so->so_proto->pr_usrreq)(so, PRU_SENSE, (struct mbuf *)vap,
(struct mbuf *)0, (struct mbuf *)0);
SOCKET_UNLOCK(so);
return(error);
}
/* ARGSUSED */
int
uipc_fcntl1(
bhv_desc_t *bdp,
int cmd,
void *arg,
rval_t *rvp)
{
register struct socket *so = bhvtos(bdp);
switch (cmd) {
case F_GETOWN:
rvp->r_val1 = so->so_pgid;
break;
case F_SETOWN:
so->so_pgid = (__psint_t)arg;
break;
default:
return EINVAL;
}
return 0;
}
/* ARGSUSED */
int
sopoll(bhv_desc_t *bdp,
short events,
int anyyet,
short *reventsp,
struct pollhead **phpp,
unsigned int *genp)
{
register unsigned revents;
register struct socket *so = bhvtos(bdp);
NETSPL_DECL(s1)
#define sbselqueue(sb) (sb)->sb_flags |= SB_SEL
/* we can set phpp outside lock, saves two arg restores */
if (!anyyet) {
*phpp = &so->so_ph;
/*
* Snapshotting the pollhead's generation number here before
* we grab the socket locks is fine since taking it early
* simply provides a more conservative estimate of what
* ``generation'' of the pollhead our event state report
* indicates.
*/
*genp = POLLGEN(&so->so_ph);
}
revents = events;
SOCKET_LOCK(so);
SOCKET_QLOCK(so, s1);
if ((revents & (POLLIN|POLLRDNORM)) && !soreadable(so) && so->so_qlen == 0) {
revents &= ~(POLLIN|POLLRDNORM);
sbselqueue(&so->so_rcv);
}
if ((revents & (POLLPRI|POLLRDBAND))
&& !(so->so_oobmark | (so->so_state & SS_RCVATMARK))) {
revents &= ~(POLLPRI|POLLRDBAND);
sbselqueue(&so->so_rcv);
}
if ((revents & POLLOUT) && !sowriteable(so)) {
revents &= ~POLLOUT;
sbselqueue(&so->so_snd);
}
SOCKET_QUNLOCK(so, s1);
SOCKET_UNLOCK(so);
*reventsp = revents;
return 0;
}