use rdwri()
[unix-history] / usr / src / sys / kern / uipc_socket.c
index 1428cc1..1193d69 100644 (file)
@@ -1,4 +1,4 @@
-/*     uipc_socket.c   4.2     81/11/08        */
+/*     uipc_socket.c   4.47    82/08/14        */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
 #include "../h/inode.h"
 #include "../h/buf.h"
 #include "../h/mbuf.h"
 #include "../h/inode.h"
 #include "../h/buf.h"
 #include "../h/mbuf.h"
-#include "../h/protocol.h"
 #include "../h/protosw.h"
 #include "../h/socket.h"
 #include "../h/socketvar.h"
 #include "../h/protosw.h"
 #include "../h/socket.h"
 #include "../h/socketvar.h"
-#include "../h/inaddr.h"
-#include "../net/inet.h"
-#include "../net/inet_systm.h"
-
-struct socket zerosocket;
-struct in_addr zeroin_addr;
+#include "../h/stat.h"
+#include "../h/ioctl.h"
+#include "../net/in.h"
+#include "../net/in_systm.h"
+#include "../net/route.h"
+#include "../h/uio.h"
 
 /*
 
 /*
- * Socket system call interface.  Copy in arguments
- * set up file descriptor and call internal socket
- * creation routine.
+ * Socket support routines.
+ *
+ * DEAL WITH INTERRUPT NOTIFICATION.
  */
  */
-ssocket()
-{
-       register struct a {
-               int     type;
-               struct  in_addr *ain;
-               int     options;
-       } *uap = (struct a *)u.u_ap;
-       struct in_addr in;
-       struct socket *so0;
-       register struct socket *so;
-       register struct file *fp;
-
-       if ((fp = falloc()) == NULL)
-               return;
-       fp->f_flag = FSOCKET|FREAD|FWRITE;
-       if (copyin((caddr_t)uap->ain, &in, sizeof (in))) {
-               u.u_error = EFAULT;
-               return;
-       }
-       u.u_error = socket(&so0, uap->type, &in, uap->options);
-       if (u.u_error)
-               goto bad;
-       fp->f_socket = so;
-       return;
-bad:
-       u.u_ofile[u.u_r.r_val1] = 0;
-       fp->f_count = 0;
-}
 
 /*
  * Create a socket.
  */
 
 /*
  * Create a socket.
  */
-socket(aso, type, iap, options)
+socreate(aso, type, asp, asa, options)
        struct socket **aso;
        int type;
        struct socket **aso;
        int type;
-       register struct in_addr *iap;
+       struct sockproto *asp;
+       struct sockaddr *asa;
        int options;
 {
        register struct protosw *prp;
        register struct socket *so;
        struct mbuf *m;
        int options;
 {
        register struct protosw *prp;
        register struct socket *so;
        struct mbuf *m;
-       int pf, proto;
+       int pf, proto, error;
 
        /*
 
        /*
-        * Pin down protocol if possible.
-        * If no address specified, use a generic protocol.
+        * Use process standard protocol/protocol family if none
+        * specified by address argument.
         */
         */
-       if (iap == 0) {
-               pf = PF_GENERIC;
+       if (asp == 0) {
+               pf = PF_INET;           /* should be u.u_protof */
                proto = 0;
        } else {
                proto = 0;
        } else {
-               pf = iap->ia_pf;
-               proto = iap->ia_proto;
-       }
-       if (proto) {
-               /*
-                * A specific protocol was requested.  Look
-                * for the protocol.  If not found, then we
-                * don't support it.
-                */
-               prp = pf_findproto(pf, proto);
-               if (prp == 0)
-                       return (EPROTONOSUPPORT);
-       } else {
-               /*
-                * No specific protocol was requested.  Look
-                * in the specified (or generic) protocol set
-                * for a protocol of this type.
-                */
-               prp = pf_findtype(pf, type);
-               if (prp == 0)
-                       return (pf == PF_GENERIC ?
-                           ESOCKTNOSUPPORT : EPROTONOSUPPORT);
+               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);
+       else
+               prp = pffindtype(pf, type);
+       if (prp == 0)
+               return (EPROTONOSUPPORT);
+
        /*
         * Get a socket structure.
         */
        /*
         * Get a socket structure.
         */
-       m = m_get(M_WAIT);
+       m = m_getclr(M_WAIT);
        if (m == 0)
                return (ENOBUFS);
        if (m == 0)
                return (ENOBUFS);
-       m->m_off = MMINOFF;
        so = mtod(m, struct socket *);
        so = mtod(m, struct socket *);
-       *so = zerosocket;
        so->so_options = options;
        so->so_options = options;
+       if (options & SO_ACCEPTCONN) {
+               so->so_q = so;
+               so->so_q0 = so;
+               so->so_qlimit = (so->so_options & SO_NEWFDONCONN) ? 5 : 1;
+       }
+       so->so_state = 0;
+       if (u.u_uid == 0)
+               so->so_state = SS_PRIV;
 
        /*
 
        /*
-        * An early call to protocol initialization.  If protocol
-        * actually hasn't been decided on yet (till we know
-        * peer), then the generic protocol allocated so far can
-        * just make sure a reasonable amount of resources will
-        * be available to it (say by allocating liberally now
-        * and returning some of the resources later).
+        * Attach protocol to socket, initializing
+        * and reserving resources.
         */
        so->so_proto = prp;
         */
        so->so_proto = prp;
-       (*prp->pr_usrreq)(so, PRU_ATTACH, 0, 0);
-       if (so->so_error) {
-               m_free(dtom(so));
-               return (u.u_error);
+       error = (*prp->pr_usrreq)(so, PRU_ATTACH, 0, asa);
+       if (error) {
+               so->so_state |= SS_NOFDREF;
+               sofree(so);
+               return (error);
        }
        *aso = so;
        return (0);
 }
 
        }
        *aso = so;
        return (0);
 }
 
-spipe()
-{
-
-}
-
-skclose(so)
-       register struct socket *so;
-{
-
-       if (so->so_pcb)
-               (*so->so_proto->pr_usrreq)(so, PRU_DETACH, 0, 0);
-}
-
-/*
- * Select a socket.
- */
-soselect(so, flag)
-       register struct socket *so;
-       int flag;
-{
-       register struct proc *p;
-
-       if (so->so_rcv.sb_cc)
-               return (1);
-       if ((p = so->so_rcv.sb_sel) && p->p_wchan == (caddr_t)select)
-               so->so_rcv.sb_flags |= SB_COLL;
-       else
-               so->so_rcv.sb_sel = u.u_procp;
-       return (0);
-}
-
-/*
- * Wakeup read sleep/select'ers.
- */
-sowakeup(so)
+sofree(so)
        struct socket *so;
 {
 
        struct socket *so;
 {
 
-       if (so->so_rcv.sb_cc && 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;
-       }
-       if (so->so_rcv.sb_flags & SB_WAIT) {
-               so->so_rcv.sb_flags &= ~SB_WAIT;
-               wakeup((caddr_t)&so->so_rcv.sb_cc);
+       if (so->so_head) {
+               if (!soqremque(so, 0) && !soqremque(so, 1))
+                       panic("sofree dq");
+               so->so_head = 0;
        }
        }
+       if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0)
+               return;
+       sbrelease(&so->so_snd);
+       sbrelease(&so->so_rcv);
+       (void) m_free(dtom(so));
 }
 
 /*
 }
 
 /*
- * Connect socket to foreign peer; system call
- * interface.  Copy in arguments and call internal routine.
+ * Close a socket on last file table reference removal.
+ * Initiate disconnect if connected.
+ * Free socket when disconnect complete.
  */
  */
-sconnect()
-{
-       register struct a {
-               int fdes;
-               struct in_addr *a;
-       } *uap = (struct a *)u.u_ap;
-       in_addr in;
-       register struct file *fp;
+soclose(so, exiting)
        register struct socket *so;
        register struct socket *so;
-       int s;
-
-       if (copyin((caddr_t)uap->a, &in, sizeof (in))) {
-               u.u_error = EFAULT;
-               return;
+       int exiting;
+{
+       int s = splnet();               /* conservative */
+       register struct socket *so2;
+
+       if (so->so_options & SO_ACCEPTCONN) {
+               while (so->so_q0 != so)
+                       soclose(so->so_q0, 1);
+               while (so->so_q != so)
+                       soclose(so->so_q, 1);
        }
        }
-       fp = getf(uap->fdes);
-       if (fp == 0)
-               return;
-       if ((fp->f_flag & FSOCKET) == 0) {
-               u.u_error = ENOTSOCK;
-               return;
+       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;
+                       }
+               }
+               if ((so->so_options & SO_DONTLINGER) == 0) {
+                       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 */
+                       while (so->so_state & SS_ISCONNECTED)
+                               sleep((caddr_t)&so->so_timeo, PZERO+1);
+               }
        }
        }
-       so = fp->f_socket;
-       u.u_error = connect(so, &in);
-       if (u.u_error)
-               return;
-       s = splnet();
-       for (;;) {
-               /* should use tsleep here */
-               if ((*so->so_proto->pr_usrreq)(so, PRU_ISCONN, 0, &in) == 0)
-                       break;
-               sleep((caddr_t)&so->so_timeo, PZERO+1);
+drop:
+       if (so->so_pcb) {
+               u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, 0, 0);
+               if (exiting == 0 && u.u_error) {
+                       splx(s);
+                       return;
+               }
        }
        }
+discard:
+       so->so_state |= SS_NOFDREF;
+       sofree(so);
        splx(s);
 }
 
        splx(s);
 }
 
-connect(so, iap)
+/*ARGSUSED*/
+sostat(so, sb)
        struct socket *so;
        struct socket *so;
-       struct in_addr *iap;
+       struct stat *sb;
 {
 
 {
 
-       return ((*so->so_proto->pr_usrreq)(so, PRU_CONNECT, 0, iap));
+       bzero((caddr_t)sb, sizeof (*sb));               /* XXX */
+       return (0);                                     /* XXX */
 }
 
 /*
 }
 
 /*
- * Disconnect socket from foreign peer; system call
- * interface.  Copy in arguments and call internal routine.
+ * Accept connection on a socket.
  */
  */
-sdisconnect()
+soaccept(so, asa)
+       struct socket *so;
+       struct sockaddr *asa;
 {
 {
-       register struct a {
-               int     fdes;
-               in_addr  *addr;
-       } *uap = (struct a *)u.u_ap;
-       in_addr in;
-       register struct file *fp;
-
-       if (uap->addr &&
-           copyin((caddr_t)uap->addr, (caddr_t)&in, sizeof (in))) {
-               u.u_error = EFAULT;
-               return;
-       }
-       fp = getf(uap->fdes);
-       if (fp == 0)
-               return;
-       if ((fp->f_flag & FSOCKET) == 0) {
-               u.u_error = ENOTSOCK;
-               return;
-       }
-       disconnect(fp->f_socket, uap->addr ? &in : 0);
+       int s = splnet();
+       int error;
+
+       error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, 0, (caddr_t)asa);
+       splx(s);
+       return (error);
 }
 
 }
 
-disconnect(so, iap)
+/*
+ * 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 socket *so;
-       struct in_addr *iap;
+       struct sockaddr *asa;
 {
 {
+       int s = splnet();
+       int error;
 
 
-       u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, iap);
+       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:
+       splx(s);
+       return (error);
 }
 
 /*
 }
 
 /*
- * Send data on socket.
+ * 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.
  */
  */
-ssend()
+sodisconnect(so, asa)
+       struct socket *so;
+       struct sockaddr *asa;
 {
 {
-       register struct a {
-               int     fdes;
-               in_addr *ain;
-               caddr_t cbuf;
-               int     count;
-       } *uap = (struct a *)u.u_ap;
-       register struct file *fp;
-       struct in_addr in;
-
-       fp = getf(uap->fdes);
-       if (fp == 0)
-               return;
-       if ((fp->f_flag & FSOCKET) == 0) {
-               u.u_error = ENOTSOCK;
-               return;
-       }
-       if (uap->count < 0) {
-               u.u_error = EINVAL;
-               return;
+       int s = splnet();
+       int error;
+
+       if ((so->so_state & SS_ISCONNECTED) == 0) {
+               error = ENOTCONN;
+               goto bad;
        }
        }
-       u.u_base = uap->cbuf;
-       u.u_count = uap->count;
-       u.u_segflg = 0;
-       if (useracc(u.u_base, u.u_count, B_READ) == 0 ||
-           uap->ain && copyin((caddr_t)uap->ain, (caddr_t)&in, sizeof (in))) {
-               u.u_error = EFAULT;
-               return;
+       if (so->so_state & SS_ISDISCONNECTING) {
+               error = EALREADY;
+               goto bad;
        }
        }
-       u.u_error = send(fp->f_socket, uap->ain ? &in : 0);
+       error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, asa);
+bad:
+       splx(s);
+       return (error);
 }
 
 }
 
-send(so, iap)
+/*
+ * Send on a socket.
+ * If send must go all at once and message is larger than
+ * send buffering, then hard error.
+ * 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.
+ */
+sosend(so, asa)
        register struct socket *so;
        register struct socket *so;
-       struct in_addr *iap;
+       struct sockaddr *asa;
 {
 {
-       register struct mbuf *m, **mp;
-       struct mbuf *top;
-       int error = 0;
+       struct mbuf *top = 0;
+       register struct mbuf *m, **mp = &top;
+       register u_int len;
+       int error = 0, space, s;
+
+       if (sosendallatonce(so) && u.u_count > so->so_snd.sb_hiwat)
+               return (EMSGSIZE);
+#ifdef notdef
+       /* NEED TO PREVENT BUSY WAITING IN SELECT FOR WRITING */
+       if ((so->so_snd.sb_flags & SB_LOCK) && (so->so_state & SS_NBIO))
+               return (EWOULDBLOCK);
+#endif
+restart:
+       sblock(&so->so_snd);
+#define        snderr(errno)   { error = errno; splx(s); goto release; }
 
 
-       if (so->so_proto->pr_flags & PR_ATOMIC) {
-               if (u.u_count > so->so_snd.sb_hiwat) {
-                       error = EMSGSIZE;
+again:
+       s = splnet();
+       if (so->so_state & SS_CANTSENDMORE) {
+               psignal(u.u_procp, SIGPIPE);
+               snderr(EPIPE);
+       }
+       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);
+               top = 0;
+               if (error) {
+                       splx(s);
                        goto release;
                }
                        goto release;
                }
+               mp = &top;
        }
        }
-again:
-       while (so->so_snd.sb_flags & SB_LOCK) {
-               so->so_snd.sb_flags |= SB_WANT;
-               sleep((caddr_t)&so->so_snd.sb_flags, PZERO+1);
+       if (u.u_count == 0) {
+               splx(s);
+               goto release;
        }
        }
-       if (so->so_snd.sb_hiwat - so->so_snd.sb_cc < u.u_count) {
-               so->so_snd.sb_flags |= SB_WAIT;
-               sleep((caddr_t)&so->so_snd.sb_cc, PZERO+1);
-               goto again;
+       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 restart;
        }
        }
-       so->so_snd.sb_flags |= SB_LOCK;
-       while (u.u_count > 0) {
-               register int bufs = so->so_snd.sb_mbmax - so->so_snd.sb_mbcnt;
-               while (bufs == 0) {
-                       so->so_snd.sb_flags |= SB_WAIT;
-                       sleep((caddr_t)&so->so_snd.sb_cc, PZERO+1);
+       splx(s);
+       while (u.u_count && space > 0) {
+               MGET(m, 1);
+               if (m == NULL) {
+                       error = ENOBUFS;                        /* SIGPIPE? */
+                       goto release;
                }
                }
-               mp = &top;
-               top = 0;
-               while (--bufs >= 0 && u.u_count > 0) {
-                       register int len;
-                       MGET(m, 1);
-                       if (m == NULL) {
-                               error = ENOBUFS;
-                               m_freem(top);
-                               goto release;
-                       }
-                       if (u.u_count >= PGSIZE && bufs >= NMBPG) {
-                               register struct mbuf *p;
-                               MPGET(p, 1);
-                               if (p == 0)
-                                       goto nopages;
-                               m->m_off = (int)p - (int)m;
-                               len = PGSIZE;
-                       } else {
+               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 {
 nopages:
 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;
+                       m->m_off = MMINOFF;
+                       len = MIN(MLEN, u.u_count);
                }
                }
-               { register int s = splnet();
-                 error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, top, iap);
-                 splx(s); }
-               if (error)
-                       break;
+               iomove(mtod(m, caddr_t), len, B_WRITE);
+               m->m_len = len;
+               *mp = m;
+               mp = &m->m_next;
+               space = sbspace(&so->so_snd);
        }
        }
+       goto again;
+
 release:
 release:
-       so->so_snd.sb_flags &= ~SB_LOCK;
-       if (so->so_snd.sb_flags & SB_WANT)
-               wakeup((caddr_t)&so->so_snd.sb_flags);
+       sbunlock(&so->so_snd);
+       if (top)
+               m_freem(top);
        return (error);
 }
 
        return (error);
 }
 
-/*
- * Receive data on socket.
- */
-sreceive()
-{
-       register struct a {
-               int     fdes;
-               in_addr *ain;
-               caddr_t cbuf;
-               int     count;
-       } *uap = (struct a *)u.u_ap;
-       register struct file *fp;
-       struct in_addr *in;
-
-       fp = getf(uap->fdes);
-       if (fp == 0)
-               return;
-       if ((fp->f_flag & FSOCKET) == 0) {
-               u.u_error = ENOTSOCK;
-               return;
-       }
-       if (uap->count < 0) {
-               u.u_error = EINVAL;
-               return;
-       }
-       u.u_base = uap->cbuf;
-       u.u_count = uap->count;
-       u.u_segflg = 0;
-       if (useracc(u.u_base, u.u_count, B_WRITE) == 0 ||
-           uap->ain && copyin((caddr_t)uap->ain, (caddr_t)&in, sizeof (in))) {
-               u.u_error = EFAULT;
-               return;
-       }
-       receive(fp->f_socket, uap->ain ? &in : 0);
-}
-
-receive(so, iap)
+soreceive(so, asa, uio)
        register struct socket *so;
        register struct socket *so;
-       struct in_addr *iap;
+       struct sockaddr *asa;
+       struct uio *uio;
 {
 {
+       register struct iovec *iov;
        register struct mbuf *m, *n;
        register struct mbuf *m, *n;
-       register int eor, len, s;
+       u_int len;
+       int eor, s, error = 0, resid = uio->uio_resid;
+       int cnt;
 
 
-again:
-       while (so->so_rcv.sb_flags & SB_LOCK) {
-               so->so_rcv.sb_flags |= SB_WANT;
-               sleep((caddr_t)&so->so_rcv.sb_flags, PZERO+1);
-       }
+restart:
+       sblock(&so->so_rcv);
+       s = splnet();
+
+#define        rcverr(errno)   { error = errno; splx(s); goto release; }
        if (so->so_rcv.sb_cc == 0) {
        if (so->so_rcv.sb_cc == 0) {
-               if ((so->so_proto->pr_usrreq)(so, PRU_ISDISCONN, 0, 0) == 0)
-                       return;
-               so->so_rcv.sb_flags |= SB_WAIT;
-               sleep((caddr_t)&so->so_rcv.sb_cc, PZERO+1);
-               goto again;
+               if (so->so_error) {
+                       error = so->so_error;
+                       so->so_error = 0;
+                       splx(s);
+                       goto release;
+               }
+               if (so->so_state & SS_CANTRCVMORE) {
+                       splx(s);
+                       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);
+               sbunlock(&so->so_rcv);
+               sbwait(&so->so_rcv);
+               splx(s);
+               goto restart;
        }
        }
-       so->so_rcv.sb_flags |= SB_LOCK;
        m = so->so_rcv.sb_mb;
        if (m == 0)
                panic("receive");
        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)
+                       panic("receive 2");
+               so->so_rcv.sb_mb = m;
+       }
        eor = 0;
        do {
        eor = 0;
        do {
-               len = MIN(m->m_len, u.u_count);
-               if (len == m->m_len) {
-                       eor = (int)m->m_act;
-                       so->so_rcv.sb_mb = m->m_next;
-                       so->so_rcv.sb_cc -= len;
-                       if (so->so_rcv.sb_cc < 0)
-                               panic("receive 2");
-               }
+               if (uio->uio_iovcnt == 0)
+                       break;
+               iov = uio->uio_iov;
+               len = iov->iov_len;
+               so->so_state &= ~SS_RCVATMARK;
+               if (so->so_oobmark && len > so->so_oobmark)
+                       len = so->so_oobmark;
+               if (len > m->m_len)
+                       len = m->m_len;
                splx(s);
                splx(s);
-               iomove(mtod(m, caddr_t), len, B_READ);
+               uiomove(mtod(m, caddr_t), len, UIO_WRITETO, uio);
                s = splnet();
                if (len == m->m_len) {
                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);
                } else {
                        m->m_off += len;
                        m->m_len -= len;
                        so->so_rcv.sb_cc -= len;
                        MFREE(m, n);
                } else {
                        m->m_off += len;
                        m->m_len -= len;
                        so->so_rcv.sb_cc -= len;
-                       if (so->so_rcv.sb_cc < 0)
-                               panic("receive 3");
                }
                }
-       } while ((m = so->so_rcv.sb_mb) && u.u_count && !eor);
+               if (so->so_oobmark) {
+                       so->so_oobmark -= len;
+                       if (so->so_oobmark == 0) {
+                               so->so_state |= SS_RCVATMARK;
+                               break;
+                       }
+               }
+       } while ((m = so->so_rcv.sb_mb) && !eor);
        if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
                do {
        if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
                do {
-                       m = so->so_rcv.sb_mb;
                        if (m == 0)
                        if (m == 0)
-                               panic("receive 4");
-                       so->so_rcv.sb_cc -= m->m_len;
-                       if (so->so_rcv.sb_cc < 0)
-                               panic("receive 5");
+                               panic("receive 3");
+                       sbfree(&so->so_rcv, m);
                        eor = (int)m->m_act;
                        so->so_rcv.sb_mb = m->m_next;
                        MFREE(m, n);
                        eor = (int)m->m_act;
                        so->so_rcv.sb_mb = m->m_next;
                        MFREE(m, n);
+                       m = n;
                } while (eor == 0);
                } while (eor == 0);
-       if (iap)
-               if ((so->so_proto->pr_flags & PR_ADDR)) {
-                       m = so->so_rcv.sb_mb;
-                       if (m == 0)
-                               panic("receive 6");
-                       so->so_rcv.sb_mb = m->m_next;
-                       so->so_rcv.sb_cc -= m->m_len;
-                       len = MIN(m->m_len, sizeof (struct in_addr));
-                       bcopy(mtod(m, caddr_t), (caddr_t)iap, len);
-               } else
-                       *iap = zeroin_addr;
-       (*so->so_proto->pr_usrreq)(so, PRU_RCVD, m, 0);
+       if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb)
+               (*so->so_proto->pr_usrreq)(so, PRU_RCVD, 0, 0);
+release:
+       sbunlock(&so->so_rcv);
+       splx(s);
+       return (error);
+}
+
+sohasoutofband(so)
+       struct socket *so;
+{
+
+       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);
+       }
 }
 
 }
 
-skioctl(so, cmd, cmdp)
+/*ARGSUSED*/
+soioctl(so, cmd, data)
        register struct socket *so;
        int cmd;
        register struct socket *so;
        int cmd;
-       register caddr_t cmdp;
+       register char *data;
 {
 
 {
 
-       switch (cmdp) {
+       switch (cmd) {
 
 
-       }
-       switch (so->so_type) {
+       case FIONBIO:
+               if (*(int *)data)
+                       so->so_state |= SS_NBIO;
+               else
+                       so->so_state &= ~SS_NBIO;
+               return;
 
 
-       case SOCK_STREAM:
-               break;
+       case FIOASYNC:
+               if (*(int *)data)
+                       so->so_state |= SS_ASYNC;
+               else
+                       so->so_state &= ~SS_ASYNC;
+               return;
+
+       case SIOCSKEEP:
+               if (*(int *)data)
+                       so->so_options &= ~SO_KEEPALIVE;
+               else
+                       so->so_options |= SO_KEEPALIVE;
+               return;
+
+       case SIOCGKEEP:
+               *(int *)data = (so->so_options & SO_KEEPALIVE) != 0;
+               return;
+
+       case SIOCSLINGER:
+               so->so_linger = *(int *)data;
+               if (so->so_linger)
+                       so->so_options &= ~SO_DONTLINGER;
+               else
+                       so->so_options |= SO_DONTLINGER;
+               return;
+
+       case SIOCGLINGER:
+               *(int *)data = so->so_linger;
+               return;
 
 
-       case SOCK_DGRAM:
-               break;
+       case SIOCSPGRP:
+               so->so_pgrp = *(int *)data;
+               return;
 
 
-       case SOCK_RDM:
-               break;
+       case SIOCGPGRP:
+               *(int *)data = so->so_pgrp;
+               return;
 
 
-       case SOCK_RAW:
-               break;
+       case SIOCDONE: {
+               int flags = *(int *)data;
 
 
+               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;
        }
        }
-}
 
 
-sostat(so)
-       struct socket *so;
-{
+       case SIOCSENDOOB: {
+               char oob = *(char *)data;
+               struct mbuf *m;
 
 
-}
+               m = m_get(M_DONTWAIT);
+               if (m == 0) {
+                       u.u_error = ENOBUFS;
+                       return;
+               }
+               m->m_off = MMINOFF;
+               m->m_len = sizeof (char);
+               *mtod(m, char *) = oob;
+               (*so->so_proto->pr_usrreq)(so, PRU_SENDOOB, m, 0);
+               return;
+       }
 
 
-/*
- * Generic protocol handler.
- */
-gen_usrreq()
-{
+       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);
+               *(char *)data = *mtod(m, char *);
+               (void) m_free(m);
+               return;
+       }
+
+       case SIOCATMARK:
+               *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
+               return;
+
+       /* routing table update calls */
+       case SIOCADDRT:
+       case SIOCDELRT:
+               if (!suser())
+                       return;
+               u.u_error = rtrequest(cmd, (struct rtentry *)data);
+               return;
+
+       /* type/protocol specific ioctls */
+       }
+       u.u_error = EOPNOTSUPP;
 }
 }