X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/261a854814e68d3a1f171496e311540608b18f15..5b519e941140a2be657d68865fade9f5aa577b79:/usr/src/sys/kern/uipc_socket.c diff --git a/usr/src/sys/kern/uipc_socket.c b/usr/src/sys/kern/uipc_socket.c index 9a8d447b47..33b62748b4 100644 --- a/usr/src/sys/kern/uipc_socket.c +++ b/usr/src/sys/kern/uipc_socket.c @@ -1,25 +1,27 @@ -/* uipc_socket.c 6.6 84/08/21 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/proc.h" -#include "../h/file.h" -#include "../h/inode.h" -#include "../h/buf.h" -#include "../h/mbuf.h" -#include "../h/un.h" -#include "../h/domain.h" -#include "../h/protosw.h" -#include "../h/socket.h" -#include "../h/socketvar.h" -#include "../h/stat.h" -#include "../h/ioctl.h" -#include "../h/uio.h" -#include "../net/route.h" -#include "../netinet/in.h" -#include "../net/if.h" +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of California at 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'' without express or implied warranty. + * + * @(#)uipc_socket.c 7.7 (Berkeley) %G% + */ + +#include "param.h" +#include "dir.h" +#include "user.h" +#include "proc.h" +#include "file.h" +#include "mbuf.h" +#include "domain.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" /* * Socket operation routines. @@ -29,10 +31,8 @@ * switching out to the protocol specific routines. * * TODO: - * sostat * test socketpair - * PR_RIGHTS - * clean up select, async + * clean up async * out-of-band is a kludge */ /*ARGSUSED*/ @@ -47,7 +47,7 @@ socreate(dom, aso, type, proto) register int error; if (proto) - prp = pffindproto(dom, proto); + prp = pffindproto(dom, proto, type); else prp = pffindtype(dom, type); if (prp == 0) @@ -55,8 +55,6 @@ socreate(dom, aso, type, proto) if (prp->pr_type != type) return (EPROTOTYPE); m = m_getclr(M_WAIT, MT_SOCKET); - if (m == 0) - return (ENOBUFS); so = mtod(m, struct socket *); so->so_options = 0; so->so_state = 0; @@ -66,7 +64,7 @@ socreate(dom, aso, type, proto) so->so_proto = prp; error = (*prp->pr_usrreq)(so, PRU_ATTACH, - (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + (struct mbuf *)0, (struct mbuf *)proto, (struct mbuf *)0); if (error) { so->so_state |= SS_NOFDREF; sofree(so); @@ -119,13 +117,13 @@ sofree(so) register struct socket *so; { + if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) + return; if (so->so_head) { if (!soqremque(so, 0) && !soqremque(so, 1)) panic("sofree dq"); so->so_head = 0; } - if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) - return; sbrelease(&so->so_snd); sorflush(so); (void) m_free(dtom(so)); @@ -152,7 +150,7 @@ soclose(so) goto discard; if (so->so_state & SS_ISCONNECTED) { if ((so->so_state & SS_ISDISCONNECTING) == 0) { - error = sodisconnect(so, (struct mbuf *)0); + error = sodisconnect(so); if (error) goto drop; } @@ -213,16 +211,25 @@ soconnect(so, nam) register struct socket *so; struct mbuf *nam; { - int s = splnet(); + int s; int error; - if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) { + if (so->so_options & SO_ACCEPTCONN) + return (EOPNOTSUPP); + s = splnet(); + /* + * If protocol is connection-based, can only connect once. + * Otherwise, if connected, try to disconnect first. + * This allows user to disconnect by connecting to, e.g., + * a null address. + */ + if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && + ((so->so_proto->pr_flags & PR_CONNREQUIRED) || + (error = sodisconnect(so)))) error = EISCONN; - goto bad; - } - error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, - (struct mbuf *)0, nam, (struct mbuf *)0); -bad: + else + error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, + (struct mbuf *)0, nam, (struct mbuf *)0); splx(s); return (error); } @@ -240,9 +247,8 @@ soconnect2(so1, so2) return (error); } -sodisconnect(so, nam) +sodisconnect(so) register struct socket *so; - struct mbuf *nam; { int s = splnet(); int error; @@ -256,7 +262,7 @@ sodisconnect(so, nam) goto bad; } error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, - (struct mbuf *)0, nam, (struct mbuf *)0); + (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); bad: splx(s); return (error); @@ -281,7 +287,7 @@ sosend(so, nam, uio, flags, rights) struct mbuf *top = 0; register struct mbuf *m, **mp; register int space; - int len, error = 0, s, dontroute, first = 1; + int len, rlen = 0, error = 0, s, dontroute, first = 1; if (sosendallatonce(so) && uio->uio_resid > so->so_snd.sb_hiwat) return (EMSGSIZE); @@ -289,16 +295,16 @@ sosend(so, nam, uio, flags, rights) (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && (so->so_proto->pr_flags & PR_ATOMIC); u.u_ru.ru_msgsnd++; + if (rights) + rlen = rights->m_len; #define snderr(errno) { error = errno; splx(s); goto release; } restart: sblock(&so->so_snd); do { s = splnet(); - if (so->so_state & SS_CANTSENDMORE) { - psignal(u.u_procp, SIGPIPE); + if (so->so_state & SS_CANTSENDMORE) snderr(EPIPE); - } if (so->so_error) { error = so->so_error; so->so_error = 0; /* ??? */ @@ -315,8 +321,9 @@ restart: space = 1024; else { space = sbspace(&so->so_snd); - if (space <= 0 || - (sosendallatonce(so) && space < uio->uio_resid) || + if (space <= rlen || + (sosendallatonce(so) && + space < uio->uio_resid + rlen) || (uio->uio_resid >= CLBYTES && space < CLBYTES && so->so_snd.sb_cc >= CLBYTES && (so->so_state & SS_NBIO) == 0)) { @@ -334,31 +341,19 @@ restart: } splx(s); mp = ⊤ - while (uio->uio_resid > 0 && space > 0) { - register struct iovec *iov = uio->uio_iov; - - if (iov->iov_len == 0) { - uio->uio_iov++; - uio->uio_iovcnt--; - if (uio->uio_iovcnt < 0) - panic("sosend"); - continue; - } + space -= rlen; + while (space > 0) { MGET(m, M_WAIT, MT_DATA); - if (m == NULL) { - error = ENOBUFS; /* SIGPIPE? */ - goto release; - } - if (iov->iov_len >= CLBYTES && space >= CLBYTES) { - register struct mbuf *p; - MCLGET(p, 1); - if (p == 0) + if (uio->uio_resid >= CLBYTES / 2 && space >= CLBYTES) { + MCLGET(m); + if (m->m_len != CLBYTES) goto nopages; - m->m_off = (int)p - (int)m; - len = CLBYTES; + len = MIN(CLBYTES, uio->uio_resid); + space -= CLBYTES; } else { nopages: - len = MIN(MLEN, iov->iov_len); + len = MIN(MIN(MLEN, uio->uio_resid), space); + space -= len; } error = uiomove(mtod(m, caddr_t), len, UIO_WRITE, uio); m->m_len = len; @@ -366,19 +361,20 @@ nopages: if (error) goto release; mp = &m->m_next; - space -= len; - } - if (top) { - if (dontroute) - so->so_options |= SO_DONTROUTE; - s = splnet(); - error = (*so->so_proto->pr_usrreq)(so, - (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, - top, (caddr_t)nam, rights); - splx(s); - if (dontroute) - so->so_options &= ~SO_DONTROUTE; + if (uio->uio_resid <= 0) + break; } + if (dontroute) + so->so_options |= SO_DONTROUTE; + s = splnet(); /* XXX */ + error = (*so->so_proto->pr_usrreq)(so, + (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, + top, (caddr_t)nam, rights); + splx(s); + if (dontroute) + so->so_options &= ~SO_DONTROUTE; + rights = 0; + rlen = 0; top = 0; first = 0; if (error) @@ -389,9 +385,23 @@ release: sbunlock(&so->so_snd); if (top) m_freem(top); + if (error == EPIPE) + psignal(u.u_procp, SIGPIPE); return (error); } +/* + * Implement receive operations on a socket. + * We depend on the way that records are added to the sockbuf + * by sbappend*. In particular, each record (mbufs linked through m_next) + * must begin with an address if the protocol so specifies, + * followed by an optional mbuf containing access rights if supported + * by the protocol, and then zero or more mbufs of data. + * In order to avoid blocking network interrupts for the entire time here, + * we splx() while doing the actual copy to user space. + * Although the sockbuf is locked, new data may still be appended, + * and thus we must maintain consistency of the sockbuf during that time. + */ soreceive(so, aname, uio, flags, rightsp) register struct socket *so; struct mbuf **aname; @@ -399,8 +409,8 @@ soreceive(so, aname, uio, flags, rightsp) int flags; struct mbuf **rightsp; { - register struct mbuf *m, *n; - register int len, error = 0, s, tomark; + register struct mbuf *m; + register int len, error = 0, s, offset; struct protosw *pr = so->so_proto; struct mbuf *nextrecord; int moff; @@ -411,10 +421,8 @@ soreceive(so, aname, uio, flags, rightsp) *aname = 0; if (flags & MSG_OOB) { m = m_get(M_WAIT, MT_DATA); - if (m == 0) - return (ENOBUFS); error = (*pr->pr_usrreq)(so, PRU_RCVOOB, - m, (struct mbuf *)0, (struct mbuf *)0); + m, (struct mbuf *)(flags & MSG_PEEK), (struct mbuf *)0); if (error) goto bad; do { @@ -435,23 +443,25 @@ 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_error) { error = so->so_error; so->so_error = 0; - splx(s); goto release; } - if (so->so_state & SS_CANTRCVMORE) { - splx(s); + if (so->so_state & SS_CANTRCVMORE) goto release; - } if ((so->so_state & SS_ISCONNECTED) == 0 && - (so->so_proto->pr_flags & PR_CONNREQUIRED)) - rcverr(ENOTCONN); - if (so->so_state & SS_NBIO) - rcverr(EWOULDBLOCK); + (so->so_proto->pr_flags & PR_CONNREQUIRED)) { + error = ENOTCONN; + goto release; + } + if (uio->uio_resid == 0) + goto release; + if (so->so_state & SS_NBIO) { + error = EWOULDBLOCK; + goto release; + } sbunlock(&so->so_rcv); sbwait(&so->so_rcv); splx(s); @@ -459,68 +469,80 @@ restart: } u.u_ru.ru_msgrcv++; m = so->so_rcv.sb_mb; + if (m == 0) + panic("receive 1"); + nextrecord = m->m_act; if (pr->pr_flags & PR_ADDR) { - if (m == 0 || m->m_type != MT_SONAME) + if (m->m_type != MT_SONAME) panic("receive 1a"); if (flags & MSG_PEEK) { if (aname) *aname = m_copy(m, 0, m->m_len); - else - m = m->m_act; + m = m->m_next; } else { + sbfree(&so->so_rcv, m); if (aname) { *aname = m; - sbfree(&so->so_rcv, m); -if(m->m_next) panic("receive 1b"); - so->so_rcv.sb_mb = m = m->m_act; - } else - m = sbdroprecord(&so->so_rcv); + m = m->m_next; + (*aname)->m_next = 0; + so->so_rcv.sb_mb = m; + } else { + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + if (m) + m->m_act = nextrecord; } } if (m && m->m_type == MT_RIGHTS) { if ((pr->pr_flags & PR_RIGHTS) == 0) - panic("receive 2a"); + panic("receive 2"); if (flags & MSG_PEEK) { if (rightsp) *rightsp = m_copy(m, 0, m->m_len); - else - m = m->m_act; + m = m->m_next; } else { + sbfree(&so->so_rcv, m); if (rightsp) { *rightsp = m; - sbfree(&so->so_rcv, m); -if(m->m_next) panic("receive 2b"); - so->so_rcv.sb_mb = m = m->m_act; - } else - m = sbdroprecord(&so->so_rcv); + so->so_rcv.sb_mb = m->m_next; + m->m_next = 0; + m = so->so_rcv.sb_mb; + } else { + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + if (m) + m->m_act = nextrecord; } } - if (m == 0) - panic("receive 3"); moff = 0; - tomark = so->so_oobmark; + offset = 0; while (m && uio->uio_resid > 0 && error == 0) { + if (m->m_type != MT_DATA && m->m_type != MT_HEADER) + panic("receive 3"); len = uio->uio_resid; so->so_state &= ~SS_RCVATMARK; - if (tomark && len > tomark) - len = tomark; - if (moff+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) len = m->m_len - moff; splx(s); error = uiomove(mtod(m, caddr_t) + moff, (int)len, UIO_READ, uio); s = splnet(); - if (len == m->m_len) { - if ((flags & MSG_PEEK) == 0) { + if (len == m->m_len - moff) { + if (flags & MSG_PEEK) { + m = m->m_next; + moff = 0; + } else { nextrecord = m->m_act; sbfree(&so->so_rcv, m); - MFREE(m, n); - if (m = n) + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + if (m) m->m_act = nextrecord; - so->so_rcv.sb_mb = m; - } else - m = m->m_next; - moff = 0; + } } else { if (flags & MSG_PEEK) moff += len; @@ -530,17 +552,15 @@ if(m->m_next) panic("receive 2b"); so->so_rcv.sb_cc -= len; } } - if ((flags & MSG_PEEK) == 0 && so->so_oobmark) { - so->so_oobmark -= len; - if (so->so_oobmark == 0) { - so->so_state |= SS_RCVATMARK; - break; - } - } - if (tomark) { - tomark -= len; - if (tomark == 0) - break; + if (so->so_oobmark) { + if ((flags & MSG_PEEK) == 0) { + so->so_oobmark -= len; + if (so->so_oobmark == 0) { + so->so_state |= SS_RCVATMARK; + break; + } + } else + offset += len; } } if ((flags & MSG_PEEK) == 0) { @@ -551,11 +571,12 @@ if(m->m_next) panic("receive 2b"); if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); + if (error == 0 && rightsp && *rightsp && + pr->pr_domain->dom_externalize) + error = (*pr->pr_domain->dom_externalize)(*rightsp); } release: sbunlock(&so->so_rcv); - if (error == 0 && rightsp && *rightsp && pr->pr_domain->dom_externalize) - error = (*pr->pr_domain->dom_externalize)(*rightsp); splx(s); return (error); } @@ -595,84 +616,184 @@ sorflush(so) sbrelease(&asb); } -sosetopt(so, level, optname, m) +sosetopt(so, level, optname, m0) register struct socket *so; int level, optname; - register struct mbuf *m; + struct mbuf *m0; { + int error = 0; + register struct mbuf *m = m0; + + if (level != SOL_SOCKET) { + if (so->so_proto && so->so_proto->pr_ctloutput) + return ((*so->so_proto->pr_ctloutput) + (PRCO_SETOPT, so, level, optname, &m0)); + error = ENOPROTOOPT; + } else { + switch (optname) { + + case SO_LINGER: + if (m == NULL || m->m_len != sizeof (struct linger)) { + error = EINVAL; + goto bad; + } + so->so_linger = mtod(m, struct linger *)->l_linger; + /* fall thru... */ + + case SO_DEBUG: + case SO_KEEPALIVE: + case SO_DONTROUTE: + case SO_USELOOPBACK: + case SO_BROADCAST: + case SO_REUSEADDR: + case SO_OOBINLINE: + if (m == NULL || m->m_len < sizeof (int)) { + error = EINVAL; + goto bad; + } + if (*mtod(m, int *)) + so->so_options |= optname; + else + so->so_options &= ~optname; + break; + + case SO_SNDBUF: + case SO_RCVBUF: + case SO_SNDLOWAT: + case SO_RCVLOWAT: + case SO_SNDTIMEO: + case SO_RCVTIMEO: + if (m == NULL || m->m_len < sizeof (int)) { + error = EINVAL; + goto bad; + } + switch (optname) { + + case SO_SNDBUF: + case SO_RCVBUF: + if (sbreserve(optname == SO_SNDBUF ? &so->so_snd : + &so->so_rcv, *mtod(m, int *)) == 0) { + error = ENOBUFS; + goto bad; + } + break; - 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); + case SO_SNDLOWAT: + so->so_snd.sb_lowat = *mtod(m, int *); + break; + case SO_RCVLOWAT: + so->so_rcv.sb_lowat = *mtod(m, int *); + break; + case SO_SNDTIMEO: + so->so_snd.sb_timeo = *mtod(m, int *); + break; + case SO_RCVTIMEO: + so->so_rcv.sb_timeo = *mtod(m, int *); + break; + } + break; + + default: + error = ENOPROTOOPT; + break; + } } - return (0); +bad: + if (m) + (void) m_free(m); + return (error); } -sogetopt(so, level, optname, m) +sogetopt(so, level, optname, mp) register struct socket *so; int level, optname; - register struct mbuf *m; + struct mbuf **mp; { + register 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) + if (level != SOL_SOCKET) { + if (so->so_proto && so->so_proto->pr_ctloutput) { + return ((*so->so_proto->pr_ctloutput) + (PRCO_GETOPT, so, level, optname, mp)); + } else return (ENOPROTOOPT); - if (optname == SO_LINGER && m != NULL) { - *mtod(m, int *) = so->so_linger; - m->m_len = sizeof (so->so_linger); - } - break; + } else { + m = m_get(M_WAIT, MT_SOOPTS); + m->m_len = sizeof (int); + + switch (optname) { + + case SO_LINGER: + m->m_len = sizeof (struct linger); + mtod(m, struct linger *)->l_onoff = + so->so_options & SO_LINGER; + mtod(m, struct linger *)->l_linger = so->so_linger; + break; + + case SO_USELOOPBACK: + case SO_DONTROUTE: + case SO_DEBUG: + case SO_KEEPALIVE: + case SO_REUSEADDR: + case SO_BROADCAST: + case SO_OOBINLINE: + *mtod(m, int *) = so->so_options & optname; + break; + + case SO_TYPE: + *mtod(m, int *) = so->so_type; + break; + + case SO_ERROR: + *mtod(m, int *) = so->so_error; + so->so_error = 0; + break; + + case SO_SNDBUF: + *mtod(m, int *) = so->so_snd.sb_hiwat; + break; + + case SO_RCVBUF: + *mtod(m, int *) = so->so_rcv.sb_hiwat; + break; + + case SO_SNDLOWAT: + *mtod(m, int *) = so->so_snd.sb_lowat; + break; + + case SO_RCVLOWAT: + *mtod(m, int *) = so->so_rcv.sb_lowat; + break; + + case SO_SNDTIMEO: + *mtod(m, int *) = so->so_snd.sb_timeo; + break; - default: - return (EINVAL); + case SO_RCVTIMEO: + *mtod(m, int *) = so->so_rcv.sb_timeo; + break; + + default: + (void)m_free(m); + return (ENOPROTOOPT); + } + *mp = m; + return (0); } - return (0); } sohasoutofband(so) register 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); + struct proc *p; + + if (so->so_pgrp < 0) + gsignal(-so->so_pgrp, SIGURG); + else if (so->so_pgrp > 0 && (p = pfind(so->so_pgrp)) != 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; } }