BSD 4_4_Lite2 release
[unix-history] / usr / src / sys / kern / uipc_socket.c
index f078cd8..a9c5453 100644 (file)
@@ -1,32 +1,50 @@
 /*
 /*
- * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
- * All rights reserved.
+ * 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 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.
+ * 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.
  *
  *
- *     @(#)uipc_socket.c       7.18 (Berkeley) %G%
+ * 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.6 (Berkeley) 5/2/95
  */
 
  */
 
-#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"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/resourcevar.h>
 
 /*
  * Socket operation routines.
 
 /*
  * Socket operation routines.
  * sys_socket.c or from a system process, and
  * implement the semantics of socket operations by
  * switching out to the protocol specific routines.
  * 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
  */
 /*ARGSUSED*/
  */
 /*ARGSUSED*/
+int
 socreate(dom, aso, type, proto)
 socreate(dom, aso, type, proto)
+       int dom;
        struct socket **aso;
        register int type;
        int proto;
 {
        struct socket **aso;
        register int type;
        int proto;
 {
+       struct proc *p = curproc;               /* XXX */
        register struct protosw *prp;
        register struct socket *so;
        register int error;
        register struct protosw *prp;
        register struct socket *so;
        register int error;
@@ -54,19 +70,18 @@ socreate(dom, aso, type, proto)
                prp = pffindproto(dom, proto, type);
        else
                prp = pffindtype(dom, type);
                prp = pffindproto(dom, proto, type);
        else
                prp = pffindtype(dom, type);
-       if (prp == 0)
+       if (prp == 0 || prp->pr_usrreq == 0)
                return (EPROTONOSUPPORT);
        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;
                return (EPROTONOSUPPORT);
        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)
+       if (p->p_ucred->cr_uid == 0)
                so->so_state = SS_PRIV;
        so->so_proto = prp;
                so->so_state = SS_PRIV;
        so->so_proto = prp;
-       error =
-           (*prp->pr_usrreq)(so, PRU_ATTACH,
-               (struct mbuf *)0, (struct mbuf *)proto, (struct mbuf *)0);
+       error = (*prp->pr_usrreq)(so, PRU_ATTACH, (struct mbuf *)0,
+           (struct mbuf *)(long)proto, (struct mbuf *)0);
        if (error) {
                so->so_state |= SS_NOFDREF;
                sofree(so);
        if (error) {
                so->so_state |= SS_NOFDREF;
                sofree(so);
@@ -76,6 +91,7 @@ socreate(dom, aso, type, proto)
        return (0);
 }
 
        return (0);
 }
 
+int
 sobind(so, nam)
        struct socket *so;
        struct mbuf *nam;
 sobind(so, nam)
        struct socket *so;
        struct mbuf *nam;
@@ -90,6 +106,7 @@ sobind(so, nam)
        return (error);
 }
 
        return (error);
 }
 
+int
 solisten(so, backlog)
        register struct socket *so;
        int backlog;
 solisten(so, backlog)
        register struct socket *so;
        int backlog;
@@ -112,6 +129,7 @@ solisten(so, backlog)
        return (0);
 }
 
        return (0);
 }
 
+int
 sofree(so)
        register struct socket *so;
 {
 sofree(so)
        register struct socket *so;
 {
@@ -133,6 +151,7 @@ sofree(so)
  * Initiate disconnect if connected.
  * Free socket when disconnect complete.
  */
  * Initiate disconnect if connected.
  * Free socket when disconnect complete.
  */
+int
 soclose(so)
        register struct socket *so;
 {
 soclose(so)
        register struct socket *so;
 {
@@ -159,7 +178,7 @@ soclose(so)
                                goto drop;
                        while (so->so_state & SS_ISCONNECTED)
                                if (error = tsleep((caddr_t)&so->so_timeo,
                                goto drop;
                        while (so->so_state & SS_ISCONNECTED)
                                if (error = tsleep((caddr_t)&so->so_timeo,
-                                   PSOCK | PCATCH, netcls, so->so_linger))
+                                   PSOCK | PCATCH, netcls, so->so_linger * hz))
                                        break;
                }
        }
                                        break;
                }
        }
@@ -183,6 +202,7 @@ discard:
 /*
  * Must be called at splnet...
  */
 /*
  * Must be called at splnet...
  */
+int
 soabort(so)
        struct socket *so;
 {
 soabort(so)
        struct socket *so;
 {
@@ -192,6 +212,7 @@ soabort(so)
                (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0));
 }
 
                (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0));
 }
 
+int
 soaccept(so, nam)
        register struct socket *so;
        struct mbuf *nam;
 soaccept(so, nam)
        register struct socket *so;
        struct mbuf *nam;
@@ -208,6 +229,7 @@ soaccept(so, nam)
        return (error);
 }
 
        return (error);
 }
 
+int
 soconnect(so, nam)
        register struct socket *so;
        struct mbuf *nam;
 soconnect(so, nam)
        register struct socket *so;
        struct mbuf *nam;
@@ -235,6 +257,7 @@ soconnect(so, nam)
        return (error);
 }
 
        return (error);
 }
 
+int
 soconnect2(so1, so2)
        register struct socket *so1;
        struct socket *so2;
 soconnect2(so1, so2)
        register struct socket *so1;
        struct socket *so2;
@@ -248,6 +271,7 @@ soconnect2(so1, so2)
        return (error);
 }
 
        return (error);
 }
 
+int
 sodisconnect(so)
        register struct socket *so;
 {
 sodisconnect(so)
        register struct socket *so;
 {
@@ -269,6 +293,7 @@ bad:
        return (error);
 }
 
        return (error);
 }
 
+#define        SBLOCKWAIT(f)   (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
 /*
  * Send on a socket.
  * If send must go all at once and message is larger than
 /*
  * Send on a socket.
  * If send must go all at once and message is larger than
@@ -286,18 +311,16 @@ bad:
  * must check for short counts if EINTR/ERESTART are returned.
  * Data and control buffers are freed on return.
  */
  * must check for short counts if EINTR/ERESTART are returned.
  * Data and control buffers are freed on return.
  */
-sosend(so, addr, uio, top, control, flags/*, sbwait_func, sbwait_arg*/)
+int
+sosend(so, addr, uio, top, control, flags)
        register struct socket *so;
        struct mbuf *addr;
        struct uio *uio;
        struct mbuf *top;
        struct mbuf *control;
        int flags;
        register struct socket *so;
        struct mbuf *addr;
        struct uio *uio;
        struct mbuf *top;
        struct mbuf *control;
        int flags;
-/*
-       int (*sbwait_func)();
-       caddr_t sbwait_arg;
-*/
 {
 {
+       struct proc *p = curproc;               /* XXX */
        struct mbuf **mp;
        register struct mbuf *m;
        register long space, len, resid;
        struct mbuf **mp;
        register struct mbuf *m;
        register long space, len, resid;
@@ -308,16 +331,25 @@ sosend(so, addr, uio, top, control, flags/*, sbwait_func, sbwait_arg*/)
                resid = uio->uio_resid;
        else
                resid = top->m_pkthdr.len;
                resid = uio->uio_resid;
        else
                resid = top->m_pkthdr.len;
+       /*
+        * In theory resid should be unsigned.
+        * However, space must be signed, as it might be less than 0
+        * if we over-committed, and we must use a signed comparison
+        * of space and resid.  On the other hand, a negative resid
+        * causes us to loop sending 0-length segments to the protocol.
+        */
+       if (resid < 0)
+               return (EINVAL);
        dontroute =
            (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
            (so->so_proto->pr_flags & PR_ATOMIC);
        dontroute =
            (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
            (so->so_proto->pr_flags & PR_ATOMIC);
-       u.u_ru.ru_msgsnd++;
+       p->p_stats->p_ru.ru_msgsnd++;
        if (control)
                clen = control->m_len;
 #define        snderr(errno)   { error = errno; splx(s); goto release; }
 
 restart:
        if (control)
                clen = control->m_len;
 #define        snderr(errno)   { error = errno; splx(s); goto release; }
 
 restart:
-       if (error = sblock(&so->so_snd))
+       if (error = sblock(&so->so_snd, SBLOCKWAIT(flags)))
                goto out;
        do {
                s = splnet();
                goto out;
        do {
                s = splnet();
@@ -327,7 +359,8 @@ restart:
                        snderr(so->so_error);
                if ((so->so_state & SS_ISCONNECTED) == 0) {
                        if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
                        snderr(so->so_error);
                if ((so->so_state & SS_ISCONNECTED) == 0) {
                        if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
-                               if ((so->so_state & SS_ISCONFIRMING) == 0)
+                               if ((so->so_state & SS_ISCONFIRMING) == 0 &&
+                                   !(resid == 0 && clen != 0))
                                        snderr(ENOTCONN);
                        } else if (addr == 0)
                                snderr(EDESTADDRREQ);
                                        snderr(ENOTCONN);
                        } else if (addr == 0)
                                snderr(EDESTADDRREQ);
@@ -335,20 +368,15 @@ restart:
                space = sbspace(&so->so_snd);
                if (flags & MSG_OOB)
                        space += 1024;
                space = sbspace(&so->so_snd);
                if (flags & MSG_OOB)
                        space += 1024;
-               if (space < resid + clen &&
+               if (atomic && resid > so->so_snd.sb_hiwat ||
+                   clen > so->so_snd.sb_hiwat)
+                       snderr(EMSGSIZE);
+               if (space < resid + clen && uio &&
                    (atomic || space < so->so_snd.sb_lowat || space < clen)) {
                    (atomic || space < so->so_snd.sb_lowat || space < clen)) {
-                       if (atomic && resid > so->so_snd.sb_hiwat ||
-                           clen > so->so_snd.sb_hiwat)
-                               snderr(EMSGSIZE);
                        if (so->so_state & SS_NBIO)
                                snderr(EWOULDBLOCK);
                        sbunlock(&so->so_snd);
                        if (so->so_state & SS_NBIO)
                                snderr(EWOULDBLOCK);
                        sbunlock(&so->so_snd);
-/*
-                       if (sbwait_func)
-                               error = (*sbwait_func)(&so->so_snd, sbwait_arg);
-                       else
-*/
-                               error = sbwait(&so->so_snd);
+                       error = sbwait(&so->so_snd);
                        splx(s);
                        if (error)
                                goto out;
                        splx(s);
                        if (error)
                                goto out;
@@ -357,15 +385,15 @@ restart:
                splx(s);
                mp = &top;
                space -= clen;
                splx(s);
                mp = &top;
                space -= clen;
-               if (uio == NULL) {
+               do {
+                   if (uio == NULL) {
                        /*
                         * Data is prepackaged in "top".
                         */
                        resid = 0;
                        if (flags & MSG_EOR)
                                top->m_flags |= M_EOR;
                        /*
                         * Data is prepackaged in "top".
                         */
                        resid = 0;
                        if (flags & MSG_EOR)
                                top->m_flags |= M_EOR;
-               } else do {
-                  do {
+                   } else do {
                        if (top == 0) {
                                MGETHDR(m, M_WAIT, MT_DATA);
                                mlen = MHLEN;
                        if (top == 0) {
                                MGETHDR(m, M_WAIT, MT_DATA);
                                mlen = MHLEN;
@@ -383,10 +411,11 @@ restart:
 #ifdef MAPPED_MBUFS
                                len = min(MCLBYTES, resid);
 #else
 #ifdef MAPPED_MBUFS
                                len = min(MCLBYTES, resid);
 #else
-                               if (top == 0) {
+                               if (atomic && top == 0) {
                                        len = min(MCLBYTES - max_hdr, resid);
                                        m->m_data += max_hdr;
                                        len = min(MCLBYTES - max_hdr, resid);
                                        m->m_data += max_hdr;
-                               }
+                               } else
+                                       len = min(MCLBYTES, resid);
 #endif
                                space -= MCLBYTES;
                        } else {
 #endif
                                space -= MCLBYTES;
                        } else {
@@ -400,7 +429,7 @@ nopages:
                                if (atomic && top == 0 && len < mlen)
                                        MH_ALIGN(m, len);
                        }
                                if (atomic && top == 0 && len < mlen)
                                        MH_ALIGN(m, len);
                        }
-                       error = uiomove(mtod(m, caddr_t), len, uio);
+                       error = uiomove(mtod(m, caddr_t), (int)len, uio);
                        resid = uio->uio_resid;
                        m->m_len = len;
                        *mp = m;
                        resid = uio->uio_resid;
                        m->m_len = len;
                        *mp = m;
@@ -453,41 +482,40 @@ out:
  * 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.
  * 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
  * The caller may receive the data as a single mbuf chain by supplying
- * an mbuf **mp for use in returning the chain.  The uio is then used
+ * an mbuf **mp0 for use in returning the chain.  The uio is then used
  * only for the count in uio_resid.
  */
  * only for the count in uio_resid.
  */
-soreceive(so, paddr, uio, mp, controlp, flagsp/*, sbwait_func, sbwait_arg*/)
+int
+soreceive(so, paddr, uio, mp0, controlp, flagsp)
        register struct socket *so;
        struct mbuf **paddr;
        struct uio *uio;
        register struct socket *so;
        struct mbuf **paddr;
        struct uio *uio;
-       struct mbuf **mp;
+       struct mbuf **mp0;
        struct mbuf **controlp;
        int *flagsp;
        struct mbuf **controlp;
        int *flagsp;
-/*
-       int (*sbwait_func)();
-       caddr_t sbwait_arg;
-*/
 {
 {
-       register struct mbuf *m;
-       register int resid, flags, len, error, s, offset;
+       register struct mbuf *m, **mp;
+       register int flags, len, error, s, offset;
        struct protosw *pr = so->so_proto;
        struct mbuf *nextrecord;
        int moff, type;
        struct protosw *pr = so->so_proto;
        struct mbuf *nextrecord;
        int moff, type;
+       int orig_resid = uio->uio_resid;
 
 
+       mp = mp0;
        if (paddr)
                *paddr = 0;
        if (controlp)
                *controlp = 0;
        if (flagsp)
                flags = *flagsp &~ MSG_EOR;
        if (paddr)
                *paddr = 0;
        if (controlp)
                *controlp = 0;
        if (flagsp)
                flags = *flagsp &~ MSG_EOR;
-       else 
+       else
                flags = 0;
        if (flags & MSG_OOB) {
                m = m_get(M_WAIT, MT_DATA);
                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);
+               error = (*pr->pr_usrreq)(so, PRU_RCVOOB, m,
+                   (struct mbuf *)(long)(flags & MSG_PEEK), (struct mbuf *)0);
                if (error)
                        goto bad;
                do {
                if (error)
                        goto bad;
                do {
@@ -502,64 +530,83 @@ bad:
        }
        if (mp)
                *mp = (struct mbuf *)0;
        }
        if (mp)
                *mp = (struct mbuf *)0;
-       resid = uio->uio_resid;
-       if (so->so_state & SS_ISCONFIRMING && resid)
+       if (so->so_state & SS_ISCONFIRMING && uio->uio_resid)
                (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0,
                    (struct mbuf *)0, (struct mbuf *)0);
 
 restart:
                (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0,
                    (struct mbuf *)0, (struct mbuf *)0);
 
 restart:
-       if (error = sblock(&so->so_rcv))
+       if (error = sblock(&so->so_rcv, SBLOCKWAIT(flags)))
                return (error);
        s = splnet();
 
        m = so->so_rcv.sb_mb;
                return (error);
        s = splnet();
 
        m = so->so_rcv.sb_mb;
-       if (m == 0 || (so->so_rcv.sb_cc < resid && 
-           so->so_rcv.sb_cc < so->so_rcv.sb_lowat)) {
+       /*
+        * 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), or
+        *   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) {
 #ifdef DIAGNOSTIC
                if (m == 0 && so->so_rcv.sb_cc)
                        panic("receive 1");
 #endif
                if (so->so_error) {
 #ifdef DIAGNOSTIC
                if (m == 0 && so->so_rcv.sb_cc)
                        panic("receive 1");
 #endif
                if (so->so_error) {
+                       if (m)
+                               goto dontblock;
                        error = so->so_error;
                        error = so->so_error;
-                       so->so_error = 0;
+                       if ((flags & MSG_PEEK) == 0)
+                               so->so_error = 0;
                        goto release;
                }
                        goto release;
                }
-               if (so->so_state & SS_CANTRCVMORE)
-                       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 ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
                    (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
                        error = ENOTCONN;
                        goto release;
                }
-               if (resid == 0)
+               if (uio->uio_resid == 0)
                        goto release;
                        goto release;
-               if (so->so_state & SS_NBIO) {
+               if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) {
                        error = EWOULDBLOCK;
                        goto release;
                }
                sbunlock(&so->so_rcv);
                        error = EWOULDBLOCK;
                        goto release;
                }
                sbunlock(&so->so_rcv);
-/*
-               if (sbwait_func)
-                       error = (*sbwait_func)(&so->so_rcv, sbwait_arg);
-               else
-*/
-                       error = sbwait(&so->so_rcv);
+               error = sbwait(&so->so_rcv);
                splx(s);
                if (error)
                        return (error);
                goto restart;
        }
                splx(s);
                if (error)
                        return (error);
                goto restart;
        }
-       u.u_ru.ru_msgrcv++;
-#ifdef DIAGNOSTIC
-if (m->m_type == 0)
-panic("receive 3a");
-#endif
+dontblock:
+       if (uio->uio_procp)
+               uio->uio_procp->p_stats->p_ru.ru_msgrcv++;
        nextrecord = m->m_nextpkt;
        if (pr->pr_flags & PR_ADDR) {
 #ifdef DIAGNOSTIC
                if (m->m_type != MT_SONAME)
                        panic("receive 1a");
 #endif
        nextrecord = m->m_nextpkt;
        if (pr->pr_flags & PR_ADDR) {
 #ifdef DIAGNOSTIC
                if (m->m_type != MT_SONAME)
                        panic("receive 1a");
 #endif
+               orig_resid = 0;
                if (flags & MSG_PEEK) {
                        if (paddr)
                                *paddr = m_copy(m, 0, m->m_len);
                if (flags & MSG_PEEK) {
                        if (paddr)
                                *paddr = m_copy(m, 0, m->m_len);
@@ -585,9 +632,9 @@ panic("receive 3a");
                } else {
                        sbfree(&so->so_rcv, m);
                        if (controlp) {
                } else {
                        sbfree(&so->so_rcv, m);
                        if (controlp) {
-                               if (mtod(m, struct cmsghdr *)->cmsg_type ==
-                                   SCM_RIGHTS &&
-                                   pr->pr_domain->dom_externalize)
+                               if (pr->pr_domain->dom_externalize &&
+                                   mtod(m, struct cmsghdr *)->cmsg_type ==
+                                   SCM_RIGHTS)
                                   error = (*pr->pr_domain->dom_externalize)(m);
                                *controlp = m;
                                so->so_rcv.sb_mb = m->m_next;
                                   error = (*pr->pr_domain->dom_externalize)(m);
                                *controlp = m;
                                so->so_rcv.sb_mb = m->m_next;
@@ -598,25 +645,32 @@ panic("receive 3a");
                                m = so->so_rcv.sb_mb;
                        }
                }
                                m = so->so_rcv.sb_mb;
                        }
                }
-               if (controlp)
+               if (controlp) {
+                       orig_resid = 0;
                        controlp = &(*controlp)->m_next;
                        controlp = &(*controlp)->m_next;
+               }
        }
        if (m) {
        }
        if (m) {
-               m->m_nextpkt = nextrecord;
+               if ((flags & MSG_PEEK) == 0)
+                       m->m_nextpkt = nextrecord;
                type = m->m_type;
                type = m->m_type;
+               if (type == MT_OOBDATA)
+                       flags |= MSG_OOB;
        }
        moff = 0;
        offset = 0;
        }
        moff = 0;
        offset = 0;
-       while (m && m->m_type == type && resid > 0 && error == 0) {
-               if (m->m_type == MT_OOBDATA)
-                       flags |= MSG_OOB;
+       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;
 #ifdef DIAGNOSTIC
                else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
                        panic("receive 3");
 #endif
 #ifdef DIAGNOSTIC
                else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
                        panic("receive 3");
 #endif
-               type = m->m_type;
                so->so_state &= ~SS_RCVATMARK;
                so->so_state &= ~SS_RCVATMARK;
-               len = resid;
+               len = uio->uio_resid;
                if (so->so_oobmark && len > so->so_oobmark - offset)
                        len = so->so_oobmark - offset;
                if (len > m->m_len - moff)
                if (so->so_oobmark && len > so->so_oobmark - offset)
                        len = so->so_oobmark - offset;
                if (len > m->m_len - moff)
@@ -632,9 +686,9 @@ panic("receive 3a");
                if (mp == 0) {
                        splx(s);
                        error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio);
                if (mp == 0) {
                        splx(s);
                        error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio);
-                       resid = uio->uio_resid;
                        s = splnet();
                        s = splnet();
-               }
+               } else
+                       uio->uio_resid -= len;
                if (len == m->m_len - moff) {
                        if (m->m_flags & M_EOR)
                                flags |= MSG_EOR;
                if (len == m->m_len - moff) {
                        if (m->m_flags & M_EOR)
                                flags |= MSG_EOR;
@@ -647,7 +701,8 @@ panic("receive 3a");
                                if (mp) {
                                        *mp = m;
                                        mp = &m->m_next;
                                if (mp) {
                                        *mp = m;
                                        mp = &m->m_next;
-                                       m = 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;
                                } else {
                                        MFREE(m, so->so_rcv.sb_mb);
                                        m = so->so_rcv.sb_mb;
@@ -659,6 +714,8 @@ panic("receive 3a");
                        if (flags & MSG_PEEK)
                                moff += len;
                        else {
                        if (flags & MSG_PEEK)
                                moff += len;
                        else {
+                               if (mp)
+                                       *mp = m_copym(m, 0, len, M_WAIT);
                                m->m_data += len;
                                m->m_len -= len;
                                so->so_rcv.sb_cc -= len;
                                m->m_data += len;
                                m->m_len -= len;
                                so->so_rcv.sb_cc -= len;
@@ -671,49 +728,56 @@ panic("receive 3a");
                                        so->so_state |= SS_RCVATMARK;
                                        break;
                                }
                                        so->so_state |= SS_RCVATMARK;
                                        break;
                                }
-                       } else
+                       } else {
                                offset += len;
                                offset += len;
+                               if (offset == so->so_oobmark)
+                                       break;
+                       }
                }
                if (flags & MSG_EOR)
                        break;
                /*
                 * If the MSG_WAITALL flag is set (for non-atomic socket),
                }
                if (flags & MSG_EOR)
                        break;
                /*
                 * If the MSG_WAITALL flag is set (for non-atomic socket),
-                * we must not quit until "resid == 0" or an error
+                * we must not quit until "uio->uio_resid == 0" or an error
                 * termination.  If a signal/timeout occurs, return
                 * termination.  If a signal/timeout occurs, return
-                * prematurely but without error.
+                * with a short count but without error.
                 * Keep sockbuf locked against other readers.
                 */
                 * Keep sockbuf locked against other readers.
                 */
-               while (flags & MSG_WAITALL && m == 0 && resid > 0 &&
-                   !sosendallatonce(so)) {
+               while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 &&
+                   !sosendallatonce(so) && !nextrecord) {
+                       if (so->so_error || so->so_state & SS_CANTRCVMORE)
+                               break;
                        error = sbwait(&so->so_rcv);
                        if (error) {
                                sbunlock(&so->so_rcv);
                                splx(s);
                        error = sbwait(&so->so_rcv);
                        if (error) {
                                sbunlock(&so->so_rcv);
                                splx(s);
-                               if (mp)
-                                       *mp = (struct mbuf *)0;
                                return (0);
                        }
                        if (m = so->so_rcv.sb_mb)
                                nextrecord = m->m_nextpkt;
                                return (0);
                        }
                        if (m = so->so_rcv.sb_mb)
                                nextrecord = m->m_nextpkt;
-                       if (so->so_error || so->so_state & SS_CANTRCVMORE)
-                               break;
-                       continue;
                }
        }
                }
        }
-       if (mp)
-               *mp = (struct mbuf *)0;
+
+       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 ((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,
                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 *)(long)flags, (struct mbuf *)0,
                            (struct mbuf *)0);
        }
                            (struct mbuf *)0);
        }
+       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:
        if (flagsp)
                *flagsp |= flags;
 release:
@@ -722,6 +786,7 @@ release:
        return (error);
 }
 
        return (error);
 }
 
+int
 soshutdown(so, how)
        register struct socket *so;
        register int how;
 soshutdown(so, how)
        register struct socket *so;
        register int how;
@@ -737,6 +802,7 @@ soshutdown(so, how)
        return (0);
 }
 
        return (0);
 }
 
+void
 sorflush(so)
        register struct socket *so;
 {
 sorflush(so)
        register struct socket *so;
 {
@@ -746,7 +812,7 @@ sorflush(so)
        struct sockbuf asb;
 
        sb->sb_flags |= SB_NOINTR;
        struct sockbuf asb;
 
        sb->sb_flags |= SB_NOINTR;
-       (void) sblock(sb);
+       (void) sblock(sb, M_WAITOK);
        s = splimp();
        socantrcvmore(so);
        sbunlock(sb);
        s = splimp();
        socantrcvmore(so);
        sbunlock(sb);
@@ -758,6 +824,7 @@ sorflush(so)
        sbrelease(&asb);
 }
 
        sbrelease(&asb);
 }
 
+int
 sosetopt(so, level, optname, m0)
        register struct socket *so;
        int level, optname;
 sosetopt(so, level, optname, m0)
        register struct socket *so;
        int level, optname;
@@ -788,6 +855,7 @@ sosetopt(so, level, optname, m0)
                case SO_USELOOPBACK:
                case SO_BROADCAST:
                case SO_REUSEADDR:
                case SO_USELOOPBACK:
                case SO_BROADCAST:
                case SO_REUSEADDR:
+               case SO_REUSEPORT:
                case SO_OOBINLINE:
                        if (m == NULL || m->m_len < sizeof (int)) {
                                error = EINVAL;
                case SO_OOBINLINE:
                        if (m == NULL || m->m_len < sizeof (int)) {
                                error = EINVAL;
@@ -803,8 +871,6 @@ sosetopt(so, level, optname, m0)
                case SO_RCVBUF:
                case SO_SNDLOWAT:
                case SO_RCVLOWAT:
                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;
                        if (m == NULL || m->m_len < sizeof (int)) {
                                error = EINVAL;
                                goto bad;
@@ -827,19 +893,47 @@ sosetopt(so, level, optname, m0)
                        case SO_RCVLOWAT:
                                so->so_rcv.sb_lowat = *mtod(m, int *);
                                break;
                        case SO_RCVLOWAT:
                                so->so_rcv.sb_lowat = *mtod(m, int *);
                                break;
+                       }
+                       break;
+
+               case SO_SNDTIMEO:
+               case SO_RCVTIMEO:
+                   {
+                       struct timeval *tv;
+                       short val;
+
+                       if (m == NULL || m->m_len < sizeof (*tv)) {
+                               error = EINVAL;
+                               goto bad;
+                       }
+                       tv = mtod(m, struct timeval *);
+                       if (tv->tv_sec * hz + tv->tv_usec / tick > SHRT_MAX) {
+                               error = EDOM;
+                               goto bad;
+                       }
+                       val = tv->tv_sec * hz + tv->tv_usec / tick;
+
+                       switch (optname) {
+
                        case SO_SNDTIMEO:
                        case SO_SNDTIMEO:
-                               so->so_snd.sb_timeo = *mtod(m, int *);
+                               so->so_snd.sb_timeo = val;
                                break;
                        case SO_RCVTIMEO:
                                break;
                        case SO_RCVTIMEO:
-                               so->so_rcv.sb_timeo = *mtod(m, int *);
+                               so->so_rcv.sb_timeo = val;
                                break;
                        }
                        break;
                                break;
                        }
                        break;
+                   }
 
                default:
                        error = ENOPROTOOPT;
                        break;
                }
 
                default:
                        error = ENOPROTOOPT;
                        break;
                }
+               if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) {
+                       (void) ((*so->so_proto->pr_ctloutput)
+                                 (PRCO_SETOPT, so, level, optname, &m0));
+                       m = NULL;       /* freed by protocol */
+               }
        }
 bad:
        if (m)
        }
 bad:
        if (m)
@@ -847,6 +941,7 @@ bad:
        return (error);
 }
 
        return (error);
 }
 
+int
 sogetopt(so, level, optname, mp)
        register struct socket *so;
        int level, optname;
 sogetopt(so, level, optname, mp)
        register struct socket *so;
        int level, optname;
@@ -858,7 +953,7 @@ sogetopt(so, level, optname, mp)
                if (so->so_proto && so->so_proto->pr_ctloutput) {
                        return ((*so->so_proto->pr_ctloutput)
                                  (PRCO_GETOPT, so, level, optname, mp));
                if (so->so_proto && so->so_proto->pr_ctloutput) {
                        return ((*so->so_proto->pr_ctloutput)
                                  (PRCO_GETOPT, so, level, optname, mp));
-               } else 
+               } else
                        return (ENOPROTOOPT);
        } else {
                m = m_get(M_WAIT, MT_SOOPTS);
                        return (ENOPROTOOPT);
        } else {
                m = m_get(M_WAIT, MT_SOOPTS);
@@ -878,6 +973,7 @@ sogetopt(so, level, optname, mp)
                case SO_DEBUG:
                case SO_KEEPALIVE:
                case SO_REUSEADDR:
                case SO_DEBUG:
                case SO_KEEPALIVE:
                case SO_REUSEADDR:
+               case SO_REUSEPORT:
                case SO_BROADCAST:
                case SO_OOBINLINE:
                        *mtod(m, int *) = so->so_options & optname;
                case SO_BROADCAST:
                case SO_OOBINLINE:
                        *mtod(m, int *) = so->so_options & optname;
@@ -909,12 +1005,17 @@ sogetopt(so, level, optname, mp)
                        break;
 
                case SO_SNDTIMEO:
                        break;
 
                case SO_SNDTIMEO:
-                       *mtod(m, int *) = so->so_snd.sb_timeo;
-                       break;
-
                case SO_RCVTIMEO:
                case SO_RCVTIMEO:
-                       *mtod(m, int *) = so->so_rcv.sb_timeo;
+                   {
+                       int val = (optname == SO_SNDTIMEO ?
+                            so->so_snd.sb_timeo : so->so_rcv.sb_timeo);
+
+                       m->m_len = sizeof(struct timeval);
+                       mtod(m, struct timeval *)->tv_sec = val / hz;
+                       mtod(m, struct timeval *)->tv_usec =
+                           (val % hz) * tick;
                        break;
                        break;
+                   }
 
                default:
                        (void)m_free(m);
 
                default:
                        (void)m_free(m);
@@ -925,6 +1026,7 @@ sogetopt(so, level, optname, mp)
        }
 }
 
        }
 }
 
+void
 sohasoutofband(so)
        register struct socket *so;
 {
 sohasoutofband(so)
        register struct socket *so;
 {
@@ -934,9 +1036,5 @@ sohasoutofband(so)
                gsignal(-so->so_pgid, SIGURG);
        else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0)
                psignal(p, SIGURG);
                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;
-       }
+       selwakeup(&so->so_rcv.sb_sel);
 }
 }