X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/1ee9e088b257f4e291015a566167f103a472adb2..15d436e0ed8d8c3d1d6dae157d8d010bbc94f48e:/usr/src/sys/kern/uipc_socket.c diff --git a/usr/src/sys/kern/uipc_socket.c b/usr/src/sys/kern/uipc_socket.c index 5a313d4294..645dd3be07 100644 --- a/usr/src/sys/kern/uipc_socket.c +++ b/usr/src/sys/kern/uipc_socket.c @@ -1,244 +1,260 @@ -/* uipc_socket.c 4.35 82/03/19 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/proc.h" -#include "../h/file.h" -#include "../h/inode.h" -#include "../h/buf.h" -#include "../h/mbuf.h" -#include "../h/protosw.h" -#include "../h/socket.h" -#include "../h/socketvar.h" -#include "../h/stat.h" -#include "../h/ioctl.h" -#include "../net/in.h" -#include "../net/in_systm.h" - /* - * Socket support routines. + * Copyright (c) 1982, 1986, 1988 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. * - * DEAL WITH INTERRUPT NOTIFICATION. + * @(#)uipc_socket.c 7.14 (Berkeley) %G% */ +#include "param.h" +#include "user.h" +#include "proc.h" +#include "file.h" +#include "malloc.h" +#include "mbuf.h" +#include "domain.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" + /* - * Create a socket. + * Socket operation routines. + * These routines are called by the routines in + * sys_socket.c or from a system process, and + * implement the semantics of socket operations by + * switching out to the protocol specific routines. + * + * TODO: + * test socketpair + * clean up async + * out-of-band is a kludge */ -socreate(aso, type, asp, asa, options) +/*ARGSUSED*/ +socreate(dom, aso, type, proto) struct socket **aso; - int type; - struct sockproto *asp; - struct sockaddr *asa; - int options; + register int type; + int proto; { register struct protosw *prp; register struct socket *so; - struct mbuf *m; - int pf, proto, error; -COUNT(SOCREATE); + register int error; - /* - * Use process standard protocol/protocol family if none - * specified by address argument. - */ - if (asp == 0) { - pf = PF_INET; /* should be u.u_protof */ - proto = 0; - } else { - pf = asp->sp_family; - proto = asp->sp_protocol; - } - - /* - * If protocol specified, look for it, otherwise - * for a protocol of the correct type in the right family. - */ if (proto) - prp = pffindproto(pf, proto); + prp = pffindproto(dom, proto, type); else - prp = pffindtype(pf, type); + prp = pffindtype(dom, type); if (prp == 0) return (EPROTONOSUPPORT); - - /* - * Get a socket structure. - */ - m = m_getclr(M_WAIT); - if (m == 0) - return (ENOBUFS); - so = mtod(m, struct socket *); - so->so_options = options; - so->so_state = 0; + if (prp->pr_type != type) + return (EPROTOTYPE); + MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_WAIT); + bzero((caddr_t)so, sizeof(*so)); + so->so_type = type; if (u.u_uid == 0) so->so_state = SS_PRIV; - - /* - * Attach protocol to socket, initializing - * and reserving resources. - */ so->so_proto = prp; - error = (*prp->pr_usrreq)(so, PRU_ATTACH, 0, asa); + error = + (*prp->pr_usrreq)(so, PRU_ATTACH, + (struct mbuf *)0, (struct mbuf *)proto, (struct mbuf *)0); if (error) { - (void) m_free(dtom(so)); + so->so_state |= SS_NOFDREF; + sofree(so); return (error); } *aso = so; return (0); } -sofree(so) +sobind(so, nam) struct socket *so; + struct mbuf *nam; +{ + int s = splnet(); + int error; + + error = + (*so->so_proto->pr_usrreq)(so, PRU_BIND, + (struct mbuf *)0, nam, (struct mbuf *)0); + splx(s); + return (error); +} + +solisten(so, backlog) + register struct socket *so; + int backlog; { + int s = splnet(), error; -COUNT(SOFREE); - if (so->so_pcb || (so->so_state & SS_USERGONE) == 0) + error = + (*so->so_proto->pr_usrreq)(so, PRU_LISTEN, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + if (error) { + splx(s); + return (error); + } + if (so->so_q == 0) { + so->so_q = so; + so->so_q0 = so; + so->so_options |= SO_ACCEPTCONN; + } + if (backlog < 0) + backlog = 0; + so->so_qlimit = min(backlog, SOMAXCONN); + splx(s); + return (0); +} + +sofree(so) + register struct socket *so; +{ + + if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) return; + if (so->so_head) { + if (!soqremque(so, 0) && !soqremque(so, 1)) + panic("sofree dq"); + so->so_head = 0; + } sbrelease(&so->so_snd); - sbrelease(&so->so_rcv); - (void) m_free(dtom(so)); + sorflush(so); + FREE(so, M_SOCKET); } /* * Close a socket on last file table reference removal. * Initiate disconnect if connected. * Free socket when disconnect complete. - * - * THIS IS REALLY A UNIX INTERFACE ROUTINE */ -soclose(so, exiting) +soclose(so) register struct socket *so; - int exiting; { int s = splnet(); /* conservative */ + int error = 0; -COUNT(SOCLOSE); + if (so->so_options & SO_ACCEPTCONN) { + while (so->so_q0 != so) + (void) soabort(so->so_q0); + while (so->so_q != so) + (void) soabort(so->so_q); + } if (so->so_pcb == 0) goto discard; - if (exiting) - so->so_options |= SO_KEEPALIVE; if (so->so_state & SS_ISCONNECTED) { if ((so->so_state & SS_ISDISCONNECTING) == 0) { - u.u_error = sodisconnect(so, (struct sockaddr *)0); - if (u.u_error) { - if (exiting) - goto drop; - splx(s); - return; - } + error = sodisconnect(so); + if (error) + goto drop; } - if ((so->so_options & SO_DONTLINGER) == 0) { + if (so->so_options & SO_LINGER) { if ((so->so_state & SS_ISDISCONNECTING) && - (so->so_state & SS_NBIO) && - exiting == 0) { - u.u_error = EINPROGRESS; - splx(s); - return; - } - /* should use tsleep here, for at most linger */ + (so->so_state & SS_NBIO)) + goto drop; while (so->so_state & SS_ISCONNECTED) sleep((caddr_t)&so->so_timeo, PZERO+1); } } drop: - u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, 0, 0); + if (so->so_pcb) { + int error2 = + (*so->so_proto->pr_usrreq)(so, PRU_DETACH, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + if (error == 0) + error = error2; + } discard: - so->so_state |= SS_USERGONE; + if (so->so_state & SS_NOFDREF) + panic("soclose: NOFDREF"); + so->so_state |= SS_NOFDREF; sofree(so); splx(s); + return (error); } -sosplice(pso, so) - struct socket *pso, *so; +/* + * Must be called at splnet... + */ +soabort(so) + struct socket *so; { -COUNT(SOSPLICE); - if (pso->so_proto->pr_family != PF_UNIX) { - struct socket *tso; - tso = pso; pso = so; so = tso; - } - if (pso->so_proto->pr_family != PF_UNIX) - return (EOPNOTSUPP); - /* check types and buffer space */ - /* merge buffers */ - return (0); + return ( + (*so->so_proto->pr_usrreq)(so, PRU_ABORT, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); } -/*ARGSUSED*/ -sostat(so, sb) - struct socket *so; - struct stat *sb; +soaccept(so, nam) + register struct socket *so; + struct mbuf *nam; { + int s = splnet(); + int error; -COUNT(SOSTAT); - bzero((caddr_t)sb, sizeof (*sb)); /* XXX */ - return (0); /* XXX */ + if ((so->so_state & SS_NOFDREF) == 0) + panic("soaccept: !NOFDREF"); + so->so_state &= ~SS_NOFDREF; + error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, + (struct mbuf *)0, nam, (struct mbuf *)0); + splx(s); + return (error); } -/* - * Accept connection on a socket. - */ -soaccept(so, asa) - struct socket *so; - struct sockaddr *asa; +soconnect(so, nam) + register struct socket *so; + struct mbuf *nam; { - int s = splnet(); + int s; int error; -COUNT(SOACCEPT); - if ((so->so_options & SO_ACCEPTCONN) == 0) { - error = EINVAL; /* XXX */ - goto bad; - } - if ((so->so_state & SS_CONNAWAITING) == 0) { - error = ENOTCONN; - goto bad; - } - so->so_state &= ~SS_CONNAWAITING; - error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, 0, (caddr_t)asa); -bad: + if (so->so_options & SO_ACCEPTCONN) + return (EOPNOTSUPP); + s = splnet(); + /* + * If protocol is connection-based, can only connect once. + * Otherwise, if connected, try to disconnect first. + * This allows user to disconnect by connecting to, e.g., + * a null address. + */ + if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && + ((so->so_proto->pr_flags & PR_CONNREQUIRED) || + (error = sodisconnect(so)))) + error = EISCONN; + else + error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, + (struct mbuf *)0, nam, (struct mbuf *)0); splx(s); return (error); } -/* - * Connect socket to a specified address. - * If already connected or connecting, then avoid - * the protocol entry, to keep its job simpler. - */ -soconnect(so, asa) - struct socket *so; - struct sockaddr *asa; +soconnect2(so1, so2) + register struct socket *so1; + struct socket *so2; { int s = splnet(); int error; -COUNT(SOCONNECT); - if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) { - error = EISCONN; - goto bad; - } - error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, 0, (caddr_t)asa); -bad: + error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2, + (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0); splx(s); return (error); } -/* - * Disconnect from a socket. - * Address parameter is from system call for later multicast - * protocols. Check to make sure that connected and no disconnect - * in progress (for protocol's sake), and then invoke protocol. - */ -sodisconnect(so, asa) - struct socket *so; - struct sockaddr *asa; +sodisconnect(so) + register struct socket *so; { int s = splnet(); int error; -COUNT(SODISCONNECT); if ((so->so_state & SS_ISCONNECTED) == 0) { error = ENOTCONN; goto bad; @@ -247,7 +263,8 @@ COUNT(SODISCONNECT); error = EALREADY; goto bad; } - error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, asa); + error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); bad: splx(s); return (error); @@ -260,366 +277,597 @@ bad: * Lock against other senders. * If must go all at once and not enough room now, then * inform user that this would block and do nothing. + * Otherwise, if nonblocking, send as much as possible. */ -sosend(so, asa) +sosend(so, nam, uio, flags, rights, control) register struct socket *so; - struct sockaddr *asa; + struct mbuf *nam; + register struct uio *uio; + int flags; + struct mbuf *rights, *control; { - struct mbuf *top = 0; - register struct mbuf *m, **mp = ⊤ - register u_int len; - int error = 0, space, s; + struct mbuf *top = 0, **mp; + register struct mbuf *m; + register int space, len; + int rlen = 0, error = 0, s, dontroute, first = 1, mlen; + int atomic = sosendallatonce(so); -COUNT(SOSEND); - if (so->so_state & SS_CANTSENDMORE) { - psignal(u.u_procp, SIGPIPE); - return (EPIPE); - } - if (sosendallatonce(so) && u.u_count > so->so_snd.sb_hiwat) + if (atomic && uio->uio_resid > so->so_snd.sb_hiwat) return (EMSGSIZE); - if ((so->so_snd.sb_flags & SB_LOCK) && (so->so_state & SS_NBIO)) - return (EWOULDBLOCK); - sblock(&so->so_snd); + dontroute = + (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && + (so->so_proto->pr_flags & PR_ATOMIC); + u.u_ru.ru_msgsnd++; + if (rights) + rlen = rights->m_len; #define snderr(errno) { error = errno; splx(s); goto release; } - s = splnet(); -again: - if (so->so_error) { - error = so->so_error; - so->so_error = 0; - splx(s); - goto release; - } - if ((so->so_state & SS_ISCONNECTED) == 0) { - if (so->so_proto->pr_flags & PR_CONNREQUIRED) - snderr(ENOTCONN); - if (asa == 0) - snderr(EDESTADDRREQ); - } - if (top) { - error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, top, asa); - if (error) { - splx(s); - goto release; +restart: + sblock(&so->so_snd); + do { + s = splnet(); + if (so->so_state & SS_CANTSENDMORE) + snderr(EPIPE); + if (so->so_error) + snderr(so->so_error); + if ((so->so_state & SS_ISCONNECTED) == 0) { + if (so->so_proto->pr_flags & PR_CONNREQUIRED) { + if (!uio->uio_resid && !rights && control) { + snderr((*so->so_proto->pr_usrreq)(so, + (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, + top, (caddr_t)0, rights, control)); + } else if (so->so_state & SS_ISCONFIRMING) + /* is ok */; + else + snderr(ENOTCONN); + } else if (nam == 0) + snderr(EDESTADDRREQ); } - top = 0; - mp = ⊤ - } - if (u.u_count == 0) { - splx(s); - goto release; - } - space = sbspace(&so->so_snd); - if (space <= 0 || sosendallatonce(so) && space < u.u_count) { - if (so->so_state & SS_NBIO) - snderr(EWOULDBLOCK); - sbunlock(&so->so_snd); - sbwait(&so->so_snd); - splx(s); - goto again; - } - splx(s); - while (u.u_count && space > 0) { - MGET(m, 1); - if (m == NULL) { - error = ENOBUFS; - m_freem(top); - goto release; + if (flags & MSG_OOB) + space = 1024; + else { + space = sbspace(&so->so_snd); + if (space <= rlen || + (atomic && space < uio->uio_resid + rlen) || + (uio->uio_resid >= MCLBYTES && space < MCLBYTES && + so->so_snd.sb_cc >= MCLBYTES && + (so->so_state & SS_NBIO) == 0)) { + if (so->so_state & SS_NBIO) { + if (first) + error = EWOULDBLOCK; + splx(s); + goto release; + } + sbunlock(&so->so_snd); + sbwait(&so->so_snd); + splx(s); + goto restart; + } } - if (u.u_count >= CLBYTES && space >= CLBYTES) { - register struct mbuf *p; - MCLGET(p, 1); - if (p == 0) - goto nopages; - m->m_off = (int)p - (int)m; - len = CLBYTES; - } else { + splx(s); + mp = ⊤ + space -= rlen; + do { + do { + if (top == 0) { + MGETHDR(m, M_WAIT, MT_DATA); + mlen = MHLEN; + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = (struct ifnet *)0; + } else { + MGET(m, M_WAIT, MT_DATA); + mlen = MLEN; + } + if (uio->uio_resid >= MINCLSIZE && space >= MCLBYTES) { + MCLGET(m, M_WAIT); + if ((m->m_flags & M_EXT) == 0) + goto nopages; + mlen = MCLBYTES; +#ifdef MAPPED_MBUFS + len = min(MCLBYTES, uio->uio_resid); + if (len < mlen - max_hdr) + m->m_data += max_hdr; +#else + len = min(MCLBYTES - max_hdr, uio->uio_resid); + m->m_data += max_hdr; +#endif + space -= MCLBYTES; + } else { nopages: - m->m_off = MMINOFF; - len = MIN(MLEN, u.u_count); - } - iomove(mtod(m, caddr_t), len, B_WRITE); - m->m_len = len; - *mp = m; - mp = &m->m_next; - space = sbspace(&so->so_snd); - } - s = splnet(); - goto again; + len = min(min(mlen, uio->uio_resid), space); + space -= len; + /* + * For datagram protocols, leave room + * for protocol headers in first mbuf. + */ + if (atomic && top == 0 && len < mlen) + MH_ALIGN(m, len); + } + error = uiomove(mtod(m, caddr_t), len, uio); + m->m_len = len; + *mp = m; + top->m_pkthdr.len += len; + if (error) + goto release; + mp = &m->m_next; + if (uio->uio_resid <= 0) { + if ((flags & MSG_EOR) && top) + top->m_flags |= M_EOR; + break; + } + } while (space > 0 && atomic); + if (dontroute) + so->so_options |= SO_DONTROUTE; + s = splnet(); /* XXX */ + error = (*so->so_proto->pr_usrreq)(so, + (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, + top, (caddr_t)nam, rights, control); + splx(s); + if (dontroute) + so->so_options &= ~SO_DONTROUTE; + rights = 0; + rlen = 0; + top = 0; + mp = ⊤ + first = 0; + if (error) + goto release; + } while (uio->uio_resid && space > 0); + } while (uio->uio_resid); release: sbunlock(&so->so_snd); + if (top) + m_freem(top); + if (error == EPIPE) + psignal(u.u_procp, SIGPIPE); return (error); } -soreceive(so, asa) +/* + * 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 containing access rights if supported + * by the protocol, 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. + */ +soreceive(so, aname, uio, flagsp, rightsp, controlp) register struct socket *so; - struct sockaddr *asa; + struct mbuf **aname; + register struct uio *uio; + int *flagsp; + struct mbuf **rightsp, **controlp; { - register struct mbuf *m, *n; - u_int len; - int eor, s, error = 0, cnt = u.u_count; - caddr_t base = u.u_base; + register struct mbuf *m; + register int flags, len, error = 0, s, offset; + struct protosw *pr = so->so_proto; + struct mbuf *nextrecord; + int moff; + + if (rightsp) + *rightsp = 0; + if (aname) + *aname = 0; + if (controlp) + *controlp = 0; + if (flagsp) + flags = *flagsp &~ MSG_EOR; + else + flags = 0; + if (flags & MSG_OOB) { + m = m_get(M_WAIT, MT_DATA); + error = (*pr->pr_usrreq)(so, PRU_RCVOOB, + m, (struct mbuf *)(flags & MSG_PEEK), (struct mbuf *)0); + if (error) + goto bad; + do { + len = uio->uio_resid; + if (len > m->m_len) + len = m->m_len; + error = uiomove(mtod(m, caddr_t), (int)len, uio); + m = m_free(m); + } while (uio->uio_resid && error == 0 && m); +bad: + if (m) + m_freem(m); + return (error); + } + if (so->so_state & SS_ISCONFIRMING && uio->uio_resid) + (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, + (struct mbuf *)0, (struct mbuf *)0); -COUNT(SORECEIVE); restart: sblock(&so->so_rcv); s = splnet(); -#define rcverr(errno) { error = errno; splx(s); goto release; } - if (so->so_rcv.sb_cc == 0) { + m = so->so_rcv.sb_mb; + if (m == 0) { + if (so->so_rcv.sb_cc) + panic("receive 1"); if (so->so_error) { error = so->so_error; so->so_error = 0; - splx(s); goto release; } - if (so->so_state & SS_CANTRCVMORE) { - splx(s); + if (so->so_state & SS_CANTRCVMORE) goto release; - } if ((so->so_state & SS_ISCONNECTED) == 0 && - (so->so_proto->pr_flags & PR_CONNREQUIRED)) - rcverr(ENOTCONN); - if (so->so_state & SS_NBIO) - rcverr(EWOULDBLOCK); + (so->so_proto->pr_flags & PR_CONNREQUIRED)) { + error = ENOTCONN; + goto release; + } + if (uio->uio_resid == 0) + goto release; + if (so->so_state & SS_NBIO) { + error = EWOULDBLOCK; + goto release; + } sbunlock(&so->so_rcv); sbwait(&so->so_rcv); splx(s); goto restart; } - m = so->so_rcv.sb_mb; - if (m == 0) - panic("receive"); - if (so->so_proto->pr_flags & PR_ADDR) { - if (m->m_len != sizeof (struct sockaddr)) - panic("soreceive addr"); - if (asa) - bcopy(mtod(m, caddr_t), (caddr_t)asa, sizeof (*asa)); - so->so_rcv.sb_cc -= m->m_len; - so->so_rcv.sb_mbcnt -= MSIZE; - m = m_free(m); - if (m == 0) + u.u_ru.ru_msgrcv++; +if (m->m_type == 0) +panic("receive 3a"); + nextrecord = m->m_nextpkt; + if (pr->pr_flags & PR_ADDR) { + if (m->m_type != MT_SONAME) + panic("receive 1a"); + if (flags & MSG_PEEK) { + if (aname) + *aname = m_copy(m, 0, m->m_len); + m = m->m_next; + } else { + sbfree(&so->so_rcv, m); + if (aname) { + *aname = m; + so->so_rcv.sb_mb = m->m_next; + m->m_next = 0; + m = so->so_rcv.sb_mb; + } else { + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + } + } + if (m && m->m_type == MT_RIGHTS) { + if ((pr->pr_flags & PR_RIGHTS) == 0) panic("receive 2"); - so->so_rcv.sb_mb = m; + if (flags & MSG_PEEK) { + if (rightsp) + *rightsp = m_copy(m, 0, m->m_len); + m = m->m_next; + } else { + sbfree(&so->so_rcv, m); + if (rightsp) { + *rightsp = m; + so->so_rcv.sb_mb = m->m_next; + m->m_next = 0; + m = so->so_rcv.sb_mb; + } else { + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + } } - so->so_state &= ~SS_RCVATMARK; - if (so->so_oobmark && cnt > so->so_oobmark) - cnt = so->so_oobmark; - eor = 0; - do { - len = MIN(m->m_len, cnt); + if (m && m->m_type == MT_CONTROL) { + if (flags & MSG_PEEK) { + if (controlp) + *controlp = m_copy(m, 0, m->m_len); + m = m->m_next; + } else { + sbfree(&so->so_rcv, m); + if (controlp) { + *controlp = m; + so->so_rcv.sb_mb = m->m_next; + m->m_next = 0; + m = so->so_rcv.sb_mb; + } else { + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + } + } + if (m) + m->m_nextpkt = nextrecord; + moff = 0; + offset = 0; + while (m && uio->uio_resid > 0 && error == 0) { + if (m->m_type == MT_OOBDATA) + flags |= MSG_OOB; + else if (m->m_type != MT_DATA && m->m_type != MT_HEADER) + panic("receive 3"); + if (m->m_flags & M_EOR) + flags |= MSG_EOR; + len = uio->uio_resid; + so->so_state &= ~SS_RCVATMARK; + 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; splx(s); - iomove(mtod(m, caddr_t), len, B_READ); - cnt -= len; + error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); s = splnet(); - if (len == m->m_len) { - eor = (int)m->m_act; - sbfree(&so->so_rcv, m); - so->so_rcv.sb_mb = m->m_next; - MFREE(m, n); + if (len == m->m_len - moff) { + if (flags & MSG_PEEK) { + m = m->m_next; + moff = 0; + } else { + nextrecord = m->m_nextpkt; + sbfree(&so->so_rcv, m); + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + if (m) + m->m_nextpkt = nextrecord; + } } else { - m->m_off += len; - m->m_len -= len; - so->so_rcv.sb_cc -= len; + if (flags & MSG_PEEK) + moff += len; + else { + m->m_data += len; + m->m_len -= len; + so->so_rcv.sb_cc -= len; + } } - } while ((m = so->so_rcv.sb_mb) && cnt && !eor); - if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0) - do { - if (m == 0) - panic("receive 3"); - sbfree(&so->so_rcv, m); - eor = (int)m->m_act; - so->so_rcv.sb_mb = m->m_next; - MFREE(m, n); - m = n; - } while (eor == 0); - if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb) - (*so->so_proto->pr_usrreq)(so, PRU_RCVD, 0, 0); - if (so->so_oobmark) { - so->so_oobmark -= u.u_base - base; - if (so->so_oobmark == 0) - so->so_state |= SS_RCVATMARK; + 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 (m && (flags & MSG_EOR)) { + flags &= ~MSG_EOR; + if ((flags & MSG_PEEK) == 0) + m->m_flags |= M_EOR; } + if ((flags & MSG_PEEK) == 0) { + if (m == 0) + so->so_rcv.sb_mb = nextrecord; + else if (pr->pr_flags & PR_ATOMIC) { + flags |= MSG_TRUNC; + (void) sbdroprecord(&so->so_rcv); + } + if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) + (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, + (struct mbuf *)flags, (struct mbuf *)0, + (struct mbuf *)0); + if (error == 0 && rightsp && *rightsp && + pr->pr_domain->dom_externalize) + error = (*pr->pr_domain->dom_externalize)(*rightsp); + } + if (flagsp) + *flagsp |= flags; release: sbunlock(&so->so_rcv); splx(s); return (error); } -sohasoutofband(so) - struct socket *so; +soshutdown(so, how) + register struct socket *so; + register int how; { + register struct protosw *pr = so->so_proto; + + how++; + if (how & FREAD) + sorflush(so); + if (how & FWRITE) + return ((*pr->pr_usrreq)(so, PRU_SHUTDOWN, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); + return (0); +} - if (so->so_pgrp == 0) - return; - if (so->so_pgrp > 0) - gsignal(so->so_pgrp, SIGURG); - else { - struct proc *p = pfind(-so->so_pgrp); - - if (p) - psignal(p, SIGURG); - } +sorflush(so) + register struct socket *so; +{ + register struct sockbuf *sb = &so->so_rcv; + register struct protosw *pr = so->so_proto; + register int s; + struct sockbuf asb; + + sblock(sb); + s = splimp(); + socantrcvmore(so); + sbunlock(sb); + asb = *sb; + bzero((caddr_t)sb, sizeof (*sb)); + splx(s); + if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose) + (*pr->pr_domain->dom_dispose)(asb.sb_mb); + sbrelease(&asb); } -/*ARGSUSED*/ -soioctl(so, cmd, cmdp) +sosetopt(so, level, optname, m0) register struct socket *so; - int cmd; - register caddr_t cmdp; + int level, optname; + struct mbuf *m0; { + int error = 0; + register struct mbuf *m = m0; + + if (level != SOL_SOCKET) { + if (so->so_proto && so->so_proto->pr_ctloutput) + return ((*so->so_proto->pr_ctloutput) + (PRCO_SETOPT, so, level, optname, &m0)); + error = ENOPROTOOPT; + } else { + switch (optname) { -COUNT(SOIOCTL); - switch (cmd) { + case SO_LINGER: + if (m == NULL || m->m_len != sizeof (struct linger)) { + error = EINVAL; + goto bad; + } + so->so_linger = mtod(m, struct linger *)->l_linger; + /* fall thru... */ + + case SO_DEBUG: + case SO_KEEPALIVE: + case SO_DONTROUTE: + case SO_USELOOPBACK: + case SO_BROADCAST: + case SO_REUSEADDR: + case SO_OOBINLINE: + if (m == NULL || m->m_len < sizeof (int)) { + error = EINVAL; + goto bad; + } + if (*mtod(m, int *)) + so->so_options |= optname; + else + so->so_options &= ~optname; + break; + + case SO_SNDBUF: + case SO_RCVBUF: + case SO_SNDLOWAT: + case SO_RCVLOWAT: + case SO_SNDTIMEO: + case SO_RCVTIMEO: + if (m == NULL || m->m_len < sizeof (int)) { + error = EINVAL; + goto bad; + } + switch (optname) { + + case SO_SNDBUF: + case SO_RCVBUF: + if (sbreserve(optname == SO_SNDBUF ? + &so->so_snd : &so->so_rcv, + (u_long) *mtod(m, int *)) == 0) { + error = ENOBUFS; + goto bad; + } + break; + + case SO_SNDLOWAT: + so->so_snd.sb_lowat = *mtod(m, int *); + break; + case SO_RCVLOWAT: + so->so_rcv.sb_lowat = *mtod(m, int *); + break; + case SO_SNDTIMEO: + so->so_snd.sb_timeo = *mtod(m, int *); + break; + case SO_RCVTIMEO: + so->so_rcv.sb_timeo = *mtod(m, int *); + break; + } + break; - case FIONBIO: { - int nbio; - if (copyin(cmdp, (caddr_t)&nbio, sizeof (nbio))) { - u.u_error = EFAULT; - return; + default: + error = ENOPROTOOPT; + break; } - if (nbio) - so->so_state |= SS_NBIO; - else - so->so_state &= ~SS_NBIO; - return; } +bad: + if (m) + (void) m_free(m); + return (error); +} - case FIOASYNC: { - int async; - if (copyin(cmdp, (caddr_t)&async, sizeof (async))) { - u.u_error = EFAULT; - return; - } - if (async) - so->so_state |= SS_ASYNC; - else - so->so_state &= ~SS_ASYNC; - return; - } +sogetopt(so, level, optname, mp) + register struct socket *so; + int level, optname; + struct mbuf **mp; +{ + register struct mbuf *m; + + if (level != SOL_SOCKET) { + if (so->so_proto && so->so_proto->pr_ctloutput) { + return ((*so->so_proto->pr_ctloutput) + (PRCO_GETOPT, so, level, optname, mp)); + } else + return (ENOPROTOOPT); + } else { + m = m_get(M_WAIT, MT_SOOPTS); + m->m_len = sizeof (int); + + switch (optname) { + + case SO_LINGER: + m->m_len = sizeof (struct linger); + mtod(m, struct linger *)->l_onoff = + so->so_options & SO_LINGER; + mtod(m, struct linger *)->l_linger = so->so_linger; + break; + + case SO_USELOOPBACK: + case SO_DONTROUTE: + case SO_DEBUG: + case SO_KEEPALIVE: + case SO_REUSEADDR: + case SO_BROADCAST: + case SO_OOBINLINE: + *mtod(m, int *) = so->so_options & optname; + break; + + case SO_TYPE: + *mtod(m, int *) = so->so_type; + break; + + case SO_ERROR: + *mtod(m, int *) = so->so_error; + so->so_error = 0; + break; - case SIOCSKEEP: { - int keep; - if (copyin(cmdp, (caddr_t)&keep, sizeof (keep))) { - u.u_error = EFAULT; - return; - } - if (keep) - so->so_options &= ~SO_KEEPALIVE; - else - so->so_options |= SO_KEEPALIVE; - return; - } + case SO_SNDBUF: + *mtod(m, int *) = so->so_snd.sb_hiwat; + break; - case SIOCGKEEP: { - int keep = (so->so_options & SO_KEEPALIVE) != 0; - if (copyout((caddr_t)&keep, cmdp, sizeof (keep))) - u.u_error = EFAULT; - return; - } + case SO_RCVBUF: + *mtod(m, int *) = so->so_rcv.sb_hiwat; + break; - case SIOCSLINGER: { - int linger; - if (copyin(cmdp, (caddr_t)&linger, sizeof (linger))) { - u.u_error = EFAULT; - return; - } - so->so_linger = linger; - if (so->so_linger) - so->so_options &= ~SO_DONTLINGER; - else - so->so_options |= SO_DONTLINGER; - return; - } + case SO_SNDLOWAT: + *mtod(m, int *) = so->so_snd.sb_lowat; + break; - case SIOCGLINGER: { - int linger = so->so_linger; - if (copyout((caddr_t)&linger, cmdp, sizeof (linger))) { - u.u_error = EFAULT; - return; - } - } - case SIOCSPGRP: { - int pgrp; - if (copyin(cmdp, (caddr_t)&pgrp, sizeof (pgrp))) { - u.u_error = EFAULT; - return; - } - so->so_pgrp = pgrp; - return; - } + case SO_RCVLOWAT: + *mtod(m, int *) = so->so_rcv.sb_lowat; + break; - case SIOCGPGRP: { - int pgrp = so->so_pgrp; - if (copyout((caddr_t)&pgrp, cmdp, sizeof (pgrp))) { - u.u_error = EFAULT; - return; - } - } + case SO_SNDTIMEO: + *mtod(m, int *) = so->so_snd.sb_timeo; + break; - case SIOCDONE: { - int flags; - if (copyin(cmdp, (caddr_t)&flags, sizeof (flags))) { - u.u_error = EFAULT; - return; - } - flags++; - if (flags & FREAD) { - int s = splimp(); - socantrcvmore(so); - sbflush(&so->so_rcv); - splx(s); - } - if (flags & FWRITE) - u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_SHUTDOWN, (struct mbuf *)0, 0); - return; - } + case SO_RCVTIMEO: + *mtod(m, int *) = so->so_rcv.sb_timeo; + break; - case SIOCSENDOOB: { - char oob; - struct mbuf *m; - if (copyin(cmdp, (caddr_t)&oob, sizeof (oob))) { - u.u_error = EFAULT; - return; + default: + (void)m_free(m); + return (ENOPROTOOPT); } - m = m_get(M_DONTWAIT); - if (m == 0) { - u.u_error = ENOBUFS; - return; - } - m->m_off = MMINOFF; - m->m_len = 1; - *mtod(m, caddr_t) = oob; - (*so->so_proto->pr_usrreq)(so, PRU_SENDOOB, m, 0); - return; + *mp = m; + return (0); } +} - case SIOCRCVOOB: { - struct mbuf *m = m_get(M_DONTWAIT); - if (m == 0) { - u.u_error = ENOBUFS; - return; - } - m->m_off = MMINOFF; *mtod(m, caddr_t) = 0; - (*so->so_proto->pr_usrreq)(so, PRU_RCVOOB, m, 0); - if (copyout(mtod(m, caddr_t), cmdp, sizeof (char))) { - u.u_error = EFAULT; - return; - } - m_free(m); - return; - } +sohasoutofband(so) + register struct socket *so; +{ + struct proc *p; - case SIOCATMARK: { - int atmark = (so->so_state&SS_RCVATMARK) != 0; - if (copyout((caddr_t)&atmark, cmdp, sizeof (atmark))) { - u.u_error = EFAULT; - return; - } - return; - } - /* type/protocol specific ioctls */ + if (so->so_pgid < 0) + gsignal(-so->so_pgid, SIGURG); + else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) + psignal(p, SIGURG); + if (so->so_rcv.sb_sel) { + selwakeup(so->so_rcv.sb_sel, so->so_rcv.sb_flags & SB_COLL); + so->so_rcv.sb_sel = 0; + so->so_rcv.sb_flags &= ~SB_COLL; } - u.u_error = EOPNOTSUPP; }