Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/kern/uipc_socket.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/kern/uipc_socket.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.4 retrieving revision 1.29.4.1 diff -u -p -r1.4 -r1.29.4.1 --- src/sys/kern/uipc_socket.c 1993/08/03 01:36:10 1.4 +++ src/sys/kern/uipc_socket.c 1998/01/30 19:24:12 1.29.4.1 @@ -1,6 +1,8 @@ +/* $NetBSD: uipc_socket.c,v 1.29.4.1 1998/01/30 19:24:12 mellon Exp $ */ + /* - * 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, with or without * modification, are permitted provided that the following conditions @@ -30,23 +32,22 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: @(#)uipc_socket.c 7.28 (Berkeley) 5/4/91 - * $Id: uipc_socket.c,v 1.4 1993/08/03 01:36:10 cgd Exp $ + * @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94 */ -#include "param.h" -#include "systm.h" -#include "proc.h" -#include "file.h" -#include "malloc.h" -#include "mbuf.h" -#include "domain.h" -#include "kernel.h" -#include "select.h" -#include "protosw.h" -#include "socket.h" -#include "socketvar.h" -#include "resourcevar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* * Socket operation routines. @@ -58,6 +59,7 @@ /*ARGSUSED*/ int socreate(dom, aso, type, proto) + int dom; struct socket **aso; register int type; int proto; @@ -71,24 +73,30 @@ socreate(dom, aso, type, proto) prp = pffindproto(dom, proto, type); else prp = pffindtype(dom, type); - if (prp == 0 || || !prp->pr_usrreq) + 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)); + TAILQ_INIT(&so->so_q0); + TAILQ_INIT(&so->so_q); so->so_type = type; - if (p->p_ucred->cr_uid == 0) - 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, p); if (error) { so->so_state |= SS_NOFDREF; sofree(so); return (error); } +#ifdef COMPAT_SUNOS + { + extern struct emul emul_sunos; + if (p->p_emul == &emul_sunos && type == SOCK_DGRAM) + so->so_options |= SO_BROADCAST; + } +#endif *aso = so; return (0); } @@ -98,12 +106,12 @@ sobind(so, nam) struct socket *so; struct mbuf *nam; { - int s = splnet(); + struct proc *p = curproc; /* XXX */ + int s = splsoftnet(); int error; - error = - (*so->so_proto->pr_usrreq)(so, PRU_BIND, - (struct mbuf *)0, nam, (struct mbuf *)0); + error = (*so->so_proto->pr_usrreq)(so, PRU_BIND, (struct mbuf *)0, + nam, (struct mbuf *)0, p); splx(s); return (error); } @@ -113,16 +121,15 @@ solisten(so, backlog) register struct socket *so; int backlog; { - int s = splnet(), error; + int s = splsoftnet(), error; - error = - (*so->so_proto->pr_usrreq)(so, PRU_LISTEN, - (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + error = (*so->so_proto->pr_usrreq)(so, PRU_LISTEN, (struct mbuf *)0, + (struct mbuf *)0, (struct mbuf *)0, (struct proc *)0); if (error) { splx(s); return (error); } - if (so->so_q == 0) + if (so->so_q.tqh_first == NULL) so->so_options |= SO_ACCEPTCONN; if (backlog < 0) backlog = 0; @@ -131,7 +138,7 @@ solisten(so, backlog) return (0); } -int +void sofree(so) register struct socket *so; { @@ -157,14 +164,14 @@ int soclose(so) register struct socket *so; { - int s = splnet(); /* conservative */ + int s = splsoftnet(); /* conservative */ int error = 0; if (so->so_options & SO_ACCEPTCONN) { - while (so->so_q0) - (void) soabort(so->so_q0); - while (so->so_q) - (void) soabort(so->so_q); + while (so->so_q0.tqh_first) + (void) soabort(so->so_q0.tqh_first); + while (so->so_q.tqh_first) + (void) soabort(so->so_q.tqh_first); } if (so->so_pcb == 0) goto discard; @@ -178,17 +185,20 @@ soclose(so) if ((so->so_state & SS_ISDISCONNECTING) && (so->so_state & SS_NBIO)) goto drop; - while (so->so_state & SS_ISCONNECTED) - if (error = tsleep((caddr_t)&so->so_timeo, - PSOCK | PCATCH, netcls, so->so_linger)) + while (so->so_state & SS_ISCONNECTED) { + error = tsleep((caddr_t)&so->so_timeo, + PSOCK | PCATCH, netcls, + so->so_linger * hz); + if (error) break; + } } } drop: if (so->so_pcb) { - int error2 = - (*so->so_proto->pr_usrreq)(so, PRU_DETACH, - (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + int error2 = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0, + (struct proc *)0); if (error == 0) error = error2; } @@ -202,16 +212,15 @@ discard: } /* - * Must be called at splnet... + * Must be called at splsoftnet... */ int soabort(so) struct socket *so; { - return ( - (*so->so_proto->pr_usrreq)(so, PRU_ABORT, - (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); + return (*so->so_proto->pr_usrreq)(so, PRU_ABORT, (struct mbuf *)0, + (struct mbuf *)0, (struct mbuf *)0, (struct proc *)0); } int @@ -219,14 +228,14 @@ soaccept(so, nam) register struct socket *so; struct mbuf *nam; { - int s = splnet(); + int s = splsoftnet(); 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, - (struct mbuf *)0, nam, (struct mbuf *)0); + error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, (struct mbuf *)0, + nam, (struct mbuf *)0, (struct proc *)0); splx(s); return (error); } @@ -236,12 +245,13 @@ soconnect(so, nam) register struct socket *so; struct mbuf *nam; { + struct proc *p = curproc; /* XXX */ int s; int error; if (so->so_options & SO_ACCEPTCONN) return (EOPNOTSUPP); - s = splnet(); + s = splsoftnet(); /* * If protocol is connection-based, can only connect once. * Otherwise, if connected, try to disconnect first. @@ -254,7 +264,7 @@ soconnect(so, nam) error = EISCONN; else error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, - (struct mbuf *)0, nam, (struct mbuf *)0); + (struct mbuf *)0, nam, (struct mbuf *)0, p); splx(s); return (error); } @@ -264,11 +274,12 @@ soconnect2(so1, so2) register struct socket *so1; struct socket *so2; { - int s = splnet(); + int s = splsoftnet(); int error; error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2, - (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0); + (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0, + (struct proc *)0); splx(s); return (error); } @@ -277,7 +288,7 @@ int sodisconnect(so) register struct socket *so; { - int s = splnet(); + int s = splsoftnet(); int error; if ((so->so_state & SS_ISCONNECTED) == 0) { @@ -289,12 +300,14 @@ sodisconnect(so) goto bad; } error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, - (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0, + (struct proc *)0); bad: splx(s); 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 @@ -332,6 +345,17 @@ sosend(so, addr, uio, top, control, flag 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) { + error = EINVAL; + goto out; + } dontroute = (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && (so->so_proto->pr_flags & PR_ATOMIC); @@ -341,10 +365,10 @@ sosend(so, addr, uio, top, control, flag #define snderr(errno) { error = errno; splx(s); goto release; } restart: - if (error = sblock(&so->so_snd)) + if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0) goto out; do { - s = splnet(); + s = splsoftnet(); if (so->so_state & SS_CANTSENDMORE) snderr(EPIPE); if (so->so_error) @@ -360,11 +384,11 @@ restart: 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)) { - 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); @@ -403,13 +427,13 @@ restart: #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; } else len = min(MCLBYTES, resid); #endif - space -= MCLBYTES; + space -= len; } else { nopages: len = min(min(mlen, resid), space); @@ -437,10 +461,10 @@ nopages: } while (space > 0 && atomic); if (dontroute) so->so_options |= SO_DONTROUTE; - s = splnet(); /* XXX */ + s = splsoftnet(); /* XXX */ error = (*so->so_proto->pr_usrreq)(so, (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, - top, addr, control); + top, addr, control, p); splx(s); if (dontroute) so->so_options &= ~SO_DONTROUTE; @@ -488,12 +512,11 @@ soreceive(so, paddr, uio, mp0, controlp, struct mbuf **controlp; int *flagsp; { - struct proc *p = curproc; /* XXX */ register struct mbuf *m, **mp; register int flags, len, error, s, offset; struct protosw *pr = so->so_proto; struct mbuf *nextrecord; - int moff, type; + int moff, type = 0; int orig_resid = uio->uio_resid; mp = mp0; @@ -507,8 +530,9 @@ soreceive(so, paddr, uio, mp0, controlp, 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, + (struct proc *)0); if (error) goto bad; do { @@ -525,35 +549,37 @@ bad: *mp = (struct mbuf *)0; if (so->so_state & SS_ISCONFIRMING && uio->uio_resid) (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, - (struct mbuf *)0, (struct mbuf *)0); + (struct mbuf *)0, (struct mbuf *)0, (struct proc *)0); restart: - if (error = sblock(&so->so_rcv)) + if ((error = sblock(&so->so_rcv, SBLOCKWAIT(flags))) != 0) return (error); - s = splnet(); + s = splsoftnet(); m = so->so_rcv.sb_mb; /* * 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 + * 1. the current count is less than the low water mark, * 2. MSG_WAITALL is set, and it is possible to do the entire - * receive operation at once if we block (resid <= hiwat). + * 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. */ - while (m == 0 || so->so_rcv.sb_cc < uio->uio_resid && + 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) { + 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) { if (m) - break; + goto dontblock; error = so->so_error; if ((flags & MSG_PEEK) == 0) so->so_error = 0; @@ -561,7 +587,7 @@ restart: } if (so->so_state & SS_CANTRCVMORE) { if (m) - break; + goto dontblock; else goto release; } @@ -577,7 +603,7 @@ restart: } if (uio->uio_resid == 0) goto release; - if (so->so_state & SS_NBIO) { + if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) { error = EWOULDBLOCK; goto release; } @@ -589,7 +615,10 @@ restart: goto restart; } dontblock: - p->p_stats->p_ru.ru_msgrcv++; +#ifdef notyet /* XXXX */ + if (uio->uio_procp) + uio->uio_procp->p_stats->p_ru.ru_msgrcv++; +#endif nextrecord = m->m_nextpkt; if (pr->pr_flags & PR_ADDR) { #ifdef DIAGNOSTIC @@ -676,7 +705,7 @@ dontblock: if (mp == 0) { splx(s); error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); - s = splnet(); + s = splsoftnet(); } else uio->uio_resid -= len; if (len == m->m_len - moff) { @@ -718,8 +747,11 @@ dontblock: so->so_state |= SS_RCVATMARK; break; } - } else + } else { offset += len; + if (offset == so->so_oobmark) + break; + } } if (flags & MSG_EOR) break; @@ -740,7 +772,7 @@ dontblock: splx(s); return (0); } - if (m = so->so_rcv.sb_mb) + if ((m = so->so_rcv.sb_mb) != NULL) nextrecord = m->m_nextpkt; } } @@ -755,8 +787,8 @@ dontblock: so->so_rcv.sb_mb = nextrecord; if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, - (struct mbuf *)flags, (struct mbuf *)0, - (struct mbuf *)0); + (struct mbuf *)(long)flags, (struct mbuf *)0, + (struct proc *)0); } if (orig_resid == uio->uio_resid && orig_resid && (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { @@ -773,6 +805,7 @@ release: return (error); } +int soshutdown(so, how) register struct socket *so; register int how; @@ -783,11 +816,12 @@ soshutdown(so, how) if (how & FREAD) sorflush(so); if (how & FWRITE) - return ((*pr->pr_usrreq)(so, PRU_SHUTDOWN, - (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); + return (*pr->pr_usrreq)(so, PRU_SHUTDOWN, (struct mbuf *)0, + (struct mbuf *)0, (struct mbuf *)0, (struct proc *)0); return (0); } +void sorflush(so) register struct socket *so; { @@ -797,7 +831,7 @@ sorflush(so) struct sockbuf asb; sb->sb_flags |= SB_NOINTR; - (void) sblock(sb); + (void) sblock(sb, M_WAITOK); s = splimp(); socantrcvmore(so); sbunlock(sb); @@ -809,6 +843,7 @@ sorflush(so) sbrelease(&asb); } +int sosetopt(so, level, optname, m0) register struct socket *so; int level, optname; @@ -839,7 +874,9 @@ sosetopt(so, level, optname, m0) case SO_USELOOPBACK: case SO_BROADCAST: case SO_REUSEADDR: + case SO_REUSEPORT: case SO_OOBINLINE: + case SO_TIMESTAMP: if (m == NULL || m->m_len < sizeof (int)) { error = EINVAL; goto bad; @@ -854,30 +891,53 @@ sosetopt(so, level, optname, m0) case SO_RCVBUF: case SO_SNDLOWAT: case SO_RCVLOWAT: + { + int optval; + if (m == NULL || m->m_len < sizeof (int)) { error = EINVAL; goto bad; } + + /* + * Values < 1 make no sense for any of these + * options, so disallow them. + */ + optval = *mtod(m, int *); + if (optval < 1) { + error = EINVAL; + goto bad; + } + switch (optname) { case SO_SNDBUF: case SO_RCVBUF: if (sbreserve(optname == SO_SNDBUF ? &so->so_snd : &so->so_rcv, - (u_long) *mtod(m, int *)) == 0) { + (u_long) optval) == 0) { error = ENOBUFS; goto bad; } break; + /* + * Make sure the low-water is never greater than + * the high-water. + */ case SO_SNDLOWAT: - so->so_snd.sb_lowat = *mtod(m, int *); + so->so_snd.sb_lowat = + (optval > so->so_snd.sb_hiwat) ? + so->so_snd.sb_hiwat : optval; break; case SO_RCVLOWAT: - so->so_rcv.sb_lowat = *mtod(m, int *); + so->so_rcv.sb_lowat = + (optval > so->so_rcv.sb_hiwat) ? + so->so_rcv.sb_hiwat : optval; break; } break; + } case SO_SNDTIMEO: case SO_RCVTIMEO: @@ -890,7 +950,7 @@ sosetopt(so, level, optname, m0) goto bad; } tv = mtod(m, struct timeval *); - if (tv->tv_sec > SHRT_MAX / hz - hz) { + if (tv->tv_sec * hz + tv->tv_usec / tick > SHRT_MAX) { error = EDOM; goto bad; } @@ -912,6 +972,11 @@ sosetopt(so, level, optname, m0) 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) @@ -919,6 +984,7 @@ bad: return (error); } +int sogetopt(so, level, optname, mp) register struct socket *so; int level, optname; @@ -950,8 +1016,10 @@ sogetopt(so, level, optname, mp) case SO_DEBUG: case SO_KEEPALIVE: case SO_REUSEADDR: + case SO_REUSEPORT: case SO_BROADCAST: case SO_OOBINLINE: + case SO_TIMESTAMP: *mtod(m, int *) = so->so_options & optname; break; @@ -989,7 +1057,7 @@ sogetopt(so, level, optname, mp) m->m_len = sizeof(struct timeval); mtod(m, struct timeval *)->tv_sec = val / hz; mtod(m, struct timeval *)->tv_usec = - (val % hz) / tick; + (val % hz) * tick; break; } @@ -1002,6 +1070,7 @@ sogetopt(so, level, optname, mp) } } +void sohasoutofband(so) register struct socket *so; {