hack to allow FTP to rebind local addresses
[unix-history] / usr / src / sys / kern / uipc_socket.c
index 249dd4f..a0dc797 100644 (file)
@@ -1,4 +1,4 @@
-/*     uipc_socket.c   4.65    82/12/05        */
+/*     uipc_socket.c   4.71    83/01/22        */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
  */
 
 /*ARGSUSED*/
  */
 
 /*ARGSUSED*/
-socreate(dom, aso, type, proto, opt)
+socreate(dom, aso, type, proto)
        struct socket **aso;
        int type, proto;
        struct socket **aso;
        int type, proto;
-       struct socketopt *opt;
 {
        register struct protosw *prp;
        register struct socket *so;
 {
        register struct protosw *prp;
        register struct socket *so;
@@ -44,18 +43,18 @@ socreate(dom, aso, type, proto, opt)
                return (EPROTONOSUPPORT);
        if (prp->pr_type != type)
                return (EPROTOTYPE);
                return (EPROTONOSUPPORT);
        if (prp->pr_type != type)
                return (EPROTOTYPE);
-       m = m_getclr(M_WAIT);
+       m = m_getclr(M_WAIT, MT_SOCKET);
        if (m == 0)
                return (ENOBUFS);
        so = mtod(m, struct socket *);
        if (m == 0)
                return (ENOBUFS);
        so = mtod(m, struct socket *);
-       so->so_options = 0;
+       so->so_options = SO_LINGER;
        so->so_state = 0;
        so->so_type = type;
        if (u.u_uid == 0)
                so->so_state = SS_PRIV;
        so->so_proto = prp;
        error = (*prp->pr_usrreq)(so, PRU_ATTACH,
        so->so_state = 0;
        so->so_type = type;
        if (u.u_uid == 0)
                so->so_state = SS_PRIV;
        so->so_proto = prp;
        error = (*prp->pr_usrreq)(so, PRU_ATTACH,
-           (struct mbuf *)0, (struct mbuf *)0, (struct socketopt *)0);
+           (struct mbuf *)0, (struct mbuf *)0);
        if (error) {
                so->so_state |= SS_NOFDREF;
                sofree(so);
        if (error) {
                so->so_state |= SS_NOFDREF;
                sofree(so);
@@ -65,17 +64,15 @@ socreate(dom, aso, type, proto, opt)
        return (0);
 }
 
        return (0);
 }
 
-sobind(so, nam, opt)
+sobind(so, nam)
        struct socket *so;
        struct mbuf *nam;
        struct socket *so;
        struct mbuf *nam;
-       struct socketopt *opt;
 {
        int s = splnet();
        int error;
 
        error =
 {
        int s = splnet();
        int error;
 
        error =
-           (*so->so_proto->pr_usrreq)(so, PRU_BIND,
-               (struct mbuf *)0, nam, opt);
+           (*so->so_proto->pr_usrreq)(so, PRU_BIND, (struct mbuf *)0, nam);
        splx(s);
        return (error);
 }
        splx(s);
        return (error);
 }
@@ -88,7 +85,7 @@ solisten(so, backlog)
        int error;
 
        error = (*so->so_proto->pr_usrreq)(so, PRU_LISTEN,
        int error;
 
        error = (*so->so_proto->pr_usrreq)(so, PRU_LISTEN,
-           (struct mbuf *)0, (struct mbuf *)0, (struct socketopt *)0);
+           (struct mbuf *)0, (struct mbuf *)0);
        if (error) {
                splx(s);
                return (error);
        if (error) {
                splx(s);
                return (error);
@@ -100,7 +97,8 @@ solisten(so, backlog)
        }
        if (backlog < 0)
                backlog = 0;
        }
        if (backlog < 0)
                backlog = 0;
-       so->so_qlimit = backlog < 5 ? backlog : 5;
+#define        SOMAXCONN       5
+       so->so_qlimit = MIN(backlog, SOMAXCONN);
        so->so_options |= SO_NEWFDONCONN;
        return (0);
 }
        so->so_options |= SO_NEWFDONCONN;
        return (0);
 }
@@ -135,9 +133,9 @@ soclose(so, exiting)
 
        if (so->so_options & SO_ACCEPTCONN) {
                while (so->so_q0 != so)
 
        if (so->so_options & SO_ACCEPTCONN) {
                while (so->so_q0 != so)
-                       (void) soclose(so->so_q0, 1);
+                       (void) soabort(so->so_q0);
                while (so->so_q != so)
                while (so->so_q != so)
-                       (void) soclose(so->so_q, 1);
+                       (void) soabort(so->so_q);
        }
        if (so->so_pcb == 0)
                goto discard;
        }
        if (so->so_pcb == 0)
                goto discard;
@@ -153,7 +151,7 @@ soclose(so, exiting)
                                return (error);
                        }
                }
                                return (error);
                        }
                }
-               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)
                        if ((so->so_state & SS_ISDISCONNECTING) &&
                            (so->so_state & SS_NBIO) &&
                            exiting == 0)
@@ -166,19 +164,34 @@ soclose(so, exiting)
 drop:
        if (so->so_pcb) {
                error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH,
 drop:
        if (so->so_pcb) {
                error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH,
-                   (struct mbuf *)0, (struct mbuf *)0, (struct socketopt *)0);
+                   (struct mbuf *)0, (struct mbuf *)0);
                if (exiting == 0 && error) {
                        splx(s);
                        return (error);
                }
        }
 discard:
                if (exiting == 0 && error) {
                        splx(s);
                        return (error);
                }
        }
 discard:
+       if (so->so_state & SS_NOFDREF)
+               panic("soclose: NOFDREF");
        so->so_state |= SS_NOFDREF;
        sofree(so);
        splx(s);
        return (0);
 }
 
        so->so_state |= SS_NOFDREF;
        sofree(so);
        splx(s);
        return (0);
 }
 
+/*
+ * Must be called at splnet...
+ */
+soabort(so)
+       struct socket *so;
+{
+       int error;
+
+       error = (*so->so_proto->pr_usrreq)(so, PRU_ABORT,
+          (struct mbuf *)0, (struct mbuf *)0);
+       return (error);
+}
+
 /*ARGSUSED*/
 sostat(so, ub)
        struct socket *so;
 /*ARGSUSED*/
 sostat(so, ub)
        struct socket *so;
@@ -191,24 +204,25 @@ sostat(so, ub)
        return (0);                                     /* XXX */
 }
 
        return (0);                                     /* XXX */
 }
 
-soaccept(so, nam, opt)
+soaccept(so, nam)
        struct socket *so;
        struct mbuf *nam;
        struct socket *so;
        struct mbuf *nam;
-       struct socketopt *opt;
 {
        int s = splnet();
        int error;
 
 {
        int s = splnet();
        int error;
 
+       if ((so->so_state & SS_NOFDREF) == 0)
+               panic("soaccept: !NOFDREF");
+       so->so_state &= ~SS_NOFDREF;
        error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT,
        error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT,
-           (struct mbuf *)0, nam, opt);
+           (struct mbuf *)0, nam);
        splx(s);
        return (error);
 }
 
        splx(s);
        return (error);
 }
 
-soconnect(so, nam, opt)
+soconnect(so, nam)
        struct socket *so;
        struct mbuf *nam;
        struct socket *so;
        struct mbuf *nam;
-       struct socketopt *opt;
 {
        int s = splnet();
        int error;
 {
        int s = splnet();
        int error;
@@ -218,7 +232,7 @@ soconnect(so, nam, opt)
                goto bad;
        }
        error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT,
                goto bad;
        }
        error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT,
-           (struct mbuf *)0, nam, opt);
+           (struct mbuf *)0, nam);
 bad:
        splx(s);
        return (error);
 bad:
        splx(s);
        return (error);
@@ -240,7 +254,7 @@ sodisconnect(so, nam)
                goto bad;
        }
        error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT,
                goto bad;
        }
        error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT,
-           (struct mbuf *)0, nam, (struct socketopt *)0);
+           (struct mbuf *)0, nam);
 bad:
        splx(s);
        return (error);
 bad:
        splx(s);
        return (error);
@@ -263,10 +277,13 @@ sosend(so, nam, uio, flags)
        struct mbuf *top = 0;
        register struct mbuf *m, **mp = &top;
        register int len;
        struct mbuf *top = 0;
        register struct mbuf *m, **mp = &top;
        register int len;
-       int error = 0, space, s;
+       int error = 0, space, s, dontroute;
 
        if (sosendallatonce(so) && uio->uio_resid > so->so_snd.sb_hiwat)
                return (EMSGSIZE);
 
        if (sosendallatonce(so) && uio->uio_resid > so->so_snd.sb_hiwat)
                return (EMSGSIZE);
+       dontroute = (flags & SOF_DONTROUTE) &&
+               (so->so_options & SO_DONTROUTE) == 0 &&
+               (so->so_proto->pr_flags & PR_ATOMIC);
 restart:
        sblock(&so->so_snd);
 #define        snderr(errno)   { error = errno; splx(s); goto release; }
 restart:
        sblock(&so->so_snd);
 #define        snderr(errno)   { error = errno; splx(s); goto release; }
@@ -291,9 +308,13 @@ again:
                        snderr(EDESTADDRREQ);
        }
        if (top) {
                        snderr(EDESTADDRREQ);
        }
        if (top) {
+               if (dontroute)
+                       so->so_options |= SO_DONTROUTE;
                error = (*so->so_proto->pr_usrreq)(so,
                    (flags & SOF_OOB) ? PRU_SENDOOB : PRU_SEND,
                error = (*so->so_proto->pr_usrreq)(so,
                    (flags & SOF_OOB) ? PRU_SENDOOB : PRU_SEND,
-                   top, (caddr_t)nam, (struct socketopt *)0);
+                   top, (caddr_t)nam);
+               if (dontroute)
+                       so->so_options &= ~SO_DONTROUTE;
                top = 0;
                if (error) {
                        splx(s);
                top = 0;
                if (error) {
                        splx(s);
@@ -330,7 +351,7 @@ again:
                                panic("sosend");
                        continue;
                }
                                panic("sosend");
                        continue;
                }
-               MGET(m, 1);
+               MGET(m, M_WAIT, MT_DATA);
                if (m == NULL) {
                        error = ENOBUFS;                        /* SIGPIPE? */
                        goto release;
                if (m == NULL) {
                        error = ENOBUFS;                        /* SIGPIPE? */
                        goto release;
@@ -375,19 +396,22 @@ soreceive(so, aname, uio, flags)
        int eor, s, error = 0, moff, tomark;
 
        if (flags & SOF_OOB) {
        int eor, s, error = 0, moff, tomark;
 
        if (flags & SOF_OOB) {
-               m = m_get(M_WAIT);
+               m = m_get(M_WAIT, MT_DATA);
+               if (m == NULL)
+                       return (ENOBUFS);
                error = (*so->so_proto->pr_usrreq)(so, PRU_RCVOOB,
                error = (*so->so_proto->pr_usrreq)(so, PRU_RCVOOB,
-                   m, (struct mbuf *)0, (struct socketopt *)0);
+                   m, (struct mbuf *)0);
                if (error)
                if (error)
-                       return (error);
-               len = uio->uio_resid;
+                       goto bad;
                do {
                do {
+                       len = uio->uio_resid;
                        if (len > m->m_len)
                                len = m->m_len;
                        error =
                            uiomove(mtod(m, caddr_t), (int)len, UIO_READ, uio);
                        m = m_free(m);
                } while (uio->uio_resid && error == 0 && m);
                        if (len > m->m_len)
                                len = m->m_len;
                        error =
                            uiomove(mtod(m, caddr_t), (int)len, UIO_READ, uio);
                        m = m_free(m);
                } while (uio->uio_resid && error == 0 && m);
+bad:
                if (m)
                        m_freem(m);
                return (error);
                if (m)
                        m_freem(m);
                return (error);
@@ -396,7 +420,6 @@ soreceive(so, aname, uio, flags)
 restart:
        sblock(&so->so_rcv);
        s = splnet();
 restart:
        sblock(&so->so_rcv);
        s = splnet();
-SBCHECK(&so->so_rcv, "soreceive restart");
 
 #define        rcverr(errno)   { error = errno; splx(s); goto release; }
        if (so->so_rcv.sb_cc == 0) {
 
 #define        rcverr(errno)   { error = errno; splx(s); goto release; }
        if (so->so_rcv.sb_cc == 0) {
@@ -424,16 +447,17 @@ SBCHECK(&so->so_rcv, "soreceive restart");
        m = so->so_rcv.sb_mb;
        if (m == 0)
                panic("receive");
        m = so->so_rcv.sb_mb;
        if (m == 0)
                panic("receive");
-SBCHECK(&so->so_snd, "soreceive havecc");
        if (so->so_proto->pr_flags & PR_ADDR) {
                if ((flags & SOF_PREVIEW) == 0) {
                        so->so_rcv.sb_cc -= m->m_len;
                        so->so_rcv.sb_mbcnt -= MSIZE;
                }
                if (aname) {
        if (so->so_proto->pr_flags & PR_ADDR) {
                if ((flags & SOF_PREVIEW) == 0) {
                        so->so_rcv.sb_cc -= m->m_len;
                        so->so_rcv.sb_mbcnt -= MSIZE;
                }
                if (aname) {
-                       if (flags & SOF_PREVIEW)
+                       if (flags & SOF_PREVIEW) {
                                *aname = m_copy(m, 0, m->m_len);
                                *aname = m_copy(m, 0, m->m_len);
-                       else
+                               if (*aname == NULL)
+                                       panic("receive 2");
+                       } else
                                *aname = m;
                        m = m->m_next;
                        (*aname)->m_next = 0;
                                *aname = m;
                        m = m->m_next;
                        (*aname)->m_next = 0;
@@ -443,10 +467,9 @@ SBCHECK(&so->so_snd, "soreceive havecc");
                        else
                                m = m_free(m);
                if (m == 0)
                        else
                                m = m_free(m);
                if (m == 0)
-                       panic("receive 2");
+                       panic("receive 3");
                if ((flags & SOF_PREVIEW) == 0)
                        so->so_rcv.sb_mb = m;
                if ((flags & SOF_PREVIEW) == 0)
                        so->so_rcv.sb_mb = m;
-SBCHECK(&so->so_snd, "soreceive afteraddr");
        }
        eor = 0;
        moff = 0;
        }
        eor = 0;
        moff = 0;
@@ -496,30 +519,112 @@ SBCHECK(&so->so_snd, "soreceive afteraddr");
                        if (tomark == 0)
                                break;
                }
                        if (tomark == 0)
                                break;
                }
-SBCHECK(&so->so_snd, "soreceive rcvloop");
        } while (m && error == 0 && !eor);
        if (flags & SOF_PREVIEW)
                goto release;
        if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
                do {
                        if (m == 0)
        } while (m && error == 0 && !eor);
        if (flags & SOF_PREVIEW)
                goto release;
        if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
                do {
                        if (m == 0)
-                               panic("receive 3");
+                               panic("receive 4");
                        sbfree(&so->so_rcv, m);
                        eor = (int)m->m_act;
                        so->so_rcv.sb_mb = m->m_next;
                        MFREE(m, n);
                        m = n;
                        sbfree(&so->so_rcv, m);
                        eor = (int)m->m_act;
                        so->so_rcv.sb_mb = m->m_next;
                        MFREE(m, n);
                        m = n;
-SBCHECK(&so->so_snd, "soreceive atomicloop");
                } while (eor == 0);
        if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb)
                (*so->so_proto->pr_usrreq)(so, PRU_RCVD,
                } while (eor == 0);
        if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb)
                (*so->so_proto->pr_usrreq)(so, PRU_RCVD,
-                   (struct mbuf *)0, (struct mbuf *)0, (struct socketopt *)0);
+                   (struct mbuf *)0, (struct mbuf *)0);
 release:
        sbunlock(&so->so_rcv);
        splx(s);
        return (error);
 }
 
 release:
        sbunlock(&so->so_rcv);
        splx(s);
        return (error);
 }
 
+soshutdown(so, how)
+       struct socket *so;
+       int how;
+{
+
+       how++;
+       if (how & FREAD) {
+               int s = splimp();
+               socantrcvmore(so);
+               sbflush(&so->so_rcv);
+               splx(s);
+       }
+       if (how & FWRITE)
+               return ((*so->so_proto->pr_usrreq)(so, PRU_SHUTDOWN,
+                   (struct mbuf *)0, (struct mbuf *)0));
+       return (0);
+}
+
+sosetopt(so, level, optname, m)
+       struct socket *so;
+       int level, optname;
+       struct mbuf *m;
+{
+
+       if (level != SOL_SOCKET)
+               return (EINVAL);        /* XXX */
+       switch (optname) {
+
+       case SO_DEBUG:
+       case SO_KEEPALIVE:
+       case SO_DONTROUTE:
+       case SO_USELOOPBACK:
+       case SO_REUSEADDR:
+               so->so_options |= optname;
+               break;
+
+       case SO_LINGER:
+               if (m == NULL || m->m_len != sizeof (int))
+                       return (EINVAL);
+               so->so_options |= SO_LINGER;
+               so->so_linger = *mtod(m, int *);
+               break;
+
+       case SO_DONTLINGER:
+               so->so_options &= ~SO_LINGER;
+               so->so_linger = 0;
+               break;
+
+       default:
+               return (EINVAL);
+       }
+       return (0);
+}
+
+sogetopt(so, level, optname, m)
+       struct socket *so;
+       int level, optname;
+       struct mbuf *m;
+{
+
+       if (level != SOL_SOCKET)
+               return (EINVAL);        /* XXX */
+       switch (optname) {
+
+       case SO_USELOOPBACK:
+       case SO_DONTROUTE:
+       case SO_DEBUG:
+       case SO_KEEPALIVE:
+       case SO_LINGER:
+       case SO_REUSEADDR:
+               if ((so->so_options & optname) == 0)
+                       return (ENOPROTOOPT);
+               if (optname == SO_LINGER && m != NULL) {
+                       *mtod(m, int *) = so->so_linger;
+                       m->m_len = sizeof (so->so_linger);
+               }
+               break;
+
+       default:
+               return (EINVAL);
+       }
+       return (0);
+}
+
 sohasoutofband(so)
        struct socket *so;
 {
 sohasoutofband(so)
        struct socket *so;
 {
@@ -559,29 +664,6 @@ soioctl(so, cmd, data)
                        so->so_state &= ~SS_ASYNC;
                break;
 
                        so->so_state &= ~SS_ASYNC;
                break;
 
-       case SIOCSKEEP:
-               if (*(int *)data)
-                       so->so_options &= ~SO_KEEPALIVE;
-               else
-                       so->so_options |= SO_KEEPALIVE;
-               break;
-
-       case SIOCGKEEP:
-               *(int *)data = (so->so_options & SO_KEEPALIVE) != 0;
-               break;
-
-       case SIOCSLINGER:
-               so->so_linger = *(int *)data;
-               if (so->so_linger)
-                       so->so_options &= ~SO_DONTLINGER;
-               else
-                       so->so_options |= SO_DONTLINGER;
-               break;
-
-       case SIOCGLINGER:
-               *(int *)data = so->so_linger;
-               break;
-
        case SIOCSPGRP:
                so->so_pgrp = *(int *)data;
                break;
        case SIOCSPGRP:
                so->so_pgrp = *(int *)data;
                break;
@@ -590,23 +672,6 @@ soioctl(so, cmd, data)
                *(int *)data = so->so_pgrp;
                break;
 
                *(int *)data = so->so_pgrp;
                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)
-                       return ((*so->so_proto->pr_usrreq)(so, PRU_SHUTDOWN,
-                           (struct mbuf *)0, (struct mbuf *)0,
-                           (struct socketopt *)0));
-               break;
-       }
-
        case SIOCATMARK:
                *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
                break;
        case SIOCATMARK:
                *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
                break;
@@ -615,12 +680,12 @@ soioctl(so, cmd, data)
        case SIOCADDRT:
        case SIOCDELRT:
                if (!suser())
        case SIOCADDRT:
        case SIOCDELRT:
                if (!suser())
-                       return (u.u_error);             /* XXX */
+                       return (u.u_error);
                return (rtrequest(cmd, (struct rtentry *)data));
 
        /* type/protocol specific ioctls */
        default:
                return (rtrequest(cmd, (struct rtentry *)data));
 
        /* type/protocol specific ioctls */
        default:
-               return (ENOTTY);
+               return (ENOTTY);                /* XXX */
        }
        return (0);
 }
        }
        return (0);
 }