-/* uipc_socket.c 4.46 82/08/01 */
+/* uipc_socket.c 4.71 83/01/22 */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/socketvar.h"
#include "../h/stat.h"
#include "../h/ioctl.h"
-#include "../net/in.h"
-#include "../net/in_systm.h"
+#include "../h/uio.h"
#include "../net/route.h"
/*
- * Socket support routines.
- *
- * DEAL WITH INTERRUPT NOTIFICATION.
+ * Socket operation routines.
+ * These routines are called by the routines in
+ * sys_socket.c or from a system process, and
+ * implement the semantics of socket operations by
+ * switching out to the protocol specific routines.
*/
-/*
- * Create a socket.
- */
-socreate(aso, type, asp, asa, options)
+/*ARGSUSED*/
+socreate(dom, aso, type, proto)
struct socket **aso;
- int type;
- struct sockproto *asp;
- struct sockaddr *asa;
- int options;
+ int type, proto;
{
register struct protosw *prp;
register struct socket *so;
struct mbuf *m;
- int pf, proto, error;
-
- /*
- * Use process standard protocol/protocol family if none
- * specified by address argument.
- */
- if (asp == 0) {
- pf = PF_INET; /* should be u.u_protof */
- proto = 0;
- } else {
- pf = asp->sp_family;
- proto = asp->sp_protocol;
- }
+ int error;
- /*
- * If protocol specified, look for it, otherwise
- * for a protocol of the correct type in the right family.
- */
if (proto)
- prp = pffindproto(pf, proto);
+ prp = pffindproto(dom, proto);
else
- prp = pffindtype(pf, type);
+ prp = pffindtype(dom, type);
if (prp == 0)
return (EPROTONOSUPPORT);
-
- /*
- * Get a socket structure.
- */
- m = m_getclr(M_WAIT);
+ 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 = options;
- if (options & SO_ACCEPTCONN) {
- so->so_q = so;
- so->so_q0 = so;
- so->so_qlimit = (so->so_options & SO_NEWFDONCONN) ? 5 : 1;
- }
+ so->so_options = SO_LINGER;
so->so_state = 0;
+ so->so_type = type;
if (u.u_uid == 0)
so->so_state = SS_PRIV;
-
- /*
- * Attach protocol to socket, initializing
- * and reserving resources.
- */
so->so_proto = prp;
- error = (*prp->pr_usrreq)(so, PRU_ATTACH, 0, asa);
+ error = (*prp->pr_usrreq)(so, PRU_ATTACH,
+ (struct mbuf *)0, (struct mbuf *)0);
if (error) {
so->so_state |= SS_NOFDREF;
sofree(so);
return (0);
}
+sobind(so, nam)
+ struct socket *so;
+ struct mbuf *nam;
+{
+ int s = splnet();
+ int error;
+
+ error =
+ (*so->so_proto->pr_usrreq)(so, PRU_BIND, (struct mbuf *)0, nam);
+ splx(s);
+ return (error);
+}
+
+solisten(so, backlog)
+ struct socket *so;
+ int backlog;
+{
+ int s = splnet();
+ int error;
+
+ error = (*so->so_proto->pr_usrreq)(so, PRU_LISTEN,
+ (struct mbuf *)0, (struct mbuf *)0);
+ if (error) {
+ splx(s);
+ return (error);
+ }
+ if (so->so_q == 0) {
+ so->so_q = so;
+ so->so_q0 = so;
+ so->so_options |= SO_ACCEPTCONN;
+ }
+ if (backlog < 0)
+ backlog = 0;
+#define SOMAXCONN 5
+ so->so_qlimit = MIN(backlog, SOMAXCONN);
+ so->so_options |= SO_NEWFDONCONN;
+ return (0);
+}
+
sofree(so)
struct socket *so;
{
int exiting;
{
int s = splnet(); /* conservative */
- register struct socket *so2;
+ int error;
if (so->so_options & SO_ACCEPTCONN) {
while (so->so_q0 != so)
- soclose(so->so_q0, 1);
+ (void) soabort(so->so_q0);
while (so->so_q != so)
- soclose(so->so_q, 1);
+ (void) soabort(so->so_q);
}
if (so->so_pcb == 0)
goto discard;
so->so_options |= SO_KEEPALIVE;
if (so->so_state & SS_ISCONNECTED) {
if ((so->so_state & SS_ISDISCONNECTING) == 0) {
- u.u_error = sodisconnect(so, (struct sockaddr *)0);
- if (u.u_error) {
+ error = sodisconnect(so, (struct mbuf *)0);
+ if (error) {
if (exiting)
goto drop;
splx(s);
- return;
+ 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) {
- u.u_error = EINPROGRESS;
- splx(s);
- return;
- }
+ exiting == 0)
+ return (EINPROGRESS);
/* should use tsleep here, for at most linger */
while (so->so_state & SS_ISCONNECTED)
sleep((caddr_t)&so->so_timeo, PZERO+1);
}
drop:
if (so->so_pcb) {
- u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, 0, 0);
- if (exiting == 0 && u.u_error) {
+ error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH,
+ (struct mbuf *)0, (struct mbuf *)0);
+ if (exiting == 0 && error) {
splx(s);
- return;
+ return (error);
}
}
discard:
+ if (so->so_state & SS_NOFDREF)
+ panic("soclose: NOFDREF");
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, sb)
+sostat(so, ub)
struct socket *so;
- struct stat *sb;
+ struct stat *ub;
{
+ struct stat sb;
- bzero((caddr_t)sb, sizeof (*sb)); /* XXX */
+ bzero((caddr_t)&sb, sizeof (sb)); /* XXX */
+ (void) copyout((caddr_t)&sb, (caddr_t)ub, sizeof (sb));/* XXX */
return (0); /* XXX */
}
-/*
- * Accept connection on a socket.
- */
-soaccept(so, asa)
+soaccept(so, nam)
struct socket *so;
- struct sockaddr *asa;
+ struct mbuf *nam;
{
int s = splnet();
int error;
- error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, 0, (caddr_t)asa);
+ 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);
splx(s);
return (error);
}
-/*
- * Connect socket to a specified address.
- * If already connected or connecting, then avoid
- * the protocol entry, to keep its job simpler.
- */
-soconnect(so, asa)
+soconnect(so, nam)
struct socket *so;
- struct sockaddr *asa;
+ struct mbuf *nam;
{
int s = splnet();
int error;
error = EISCONN;
goto bad;
}
- error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, 0, (caddr_t)asa);
+ error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT,
+ (struct mbuf *)0, nam);
bad:
splx(s);
return (error);
}
-/*
- * Disconnect from a socket.
- * Address parameter is from system call for later multicast
- * protocols. Check to make sure that connected and no disconnect
- * in progress (for protocol's sake), and then invoke protocol.
- */
-sodisconnect(so, asa)
+sodisconnect(so, nam)
struct socket *so;
- struct sockaddr *asa;
+ struct mbuf *nam;
{
int s = splnet();
int error;
error = EALREADY;
goto bad;
}
- error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, asa);
+ error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT,
+ (struct mbuf *)0, nam);
bad:
splx(s);
return (error);
* If must go all at once and not enough room now, then
* inform user that this would block and do nothing.
*/
-sosend(so, asa)
+sosend(so, nam, uio, flags)
register struct socket *so;
- struct sockaddr *asa;
+ struct mbuf *nam;
+ struct uio *uio;
+ int flags;
{
struct mbuf *top = 0;
register struct mbuf *m, **mp = ⊤
- register u_int len;
- int error = 0, space, s;
+ register int len;
+ int error = 0, space, s, dontroute;
- if (sosendallatonce(so) && u.u_count > so->so_snd.sb_hiwat)
+ if (sosendallatonce(so) && uio->uio_resid > so->so_snd.sb_hiwat)
return (EMSGSIZE);
-#ifdef notdef
- /* NEED TO PREVENT BUSY WAITING IN SELECT FOR WRITING */
- if ((so->so_snd.sb_flags & SB_LOCK) && (so->so_state & SS_NBIO))
- return (EWOULDBLOCK);
-#endif
+ 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; }
+ u.u_ru.ru_msgsnd++;
again:
s = splnet();
if (so->so_state & SS_CANTSENDMORE) {
if ((so->so_state & SS_ISCONNECTED) == 0) {
if (so->so_proto->pr_flags & PR_CONNREQUIRED)
snderr(ENOTCONN);
- if (asa == 0)
+ if (nam == 0)
snderr(EDESTADDRREQ);
}
if (top) {
- error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, top, asa);
+ if (dontroute)
+ so->so_options |= SO_DONTROUTE;
+ error = (*so->so_proto->pr_usrreq)(so,
+ (flags & SOF_OOB) ? PRU_SENDOOB : PRU_SEND,
+ top, (caddr_t)nam);
+ if (dontroute)
+ so->so_options &= ~SO_DONTROUTE;
top = 0;
if (error) {
splx(s);
}
mp = ⊤
}
- if (u.u_count == 0) {
+ if (uio->uio_resid == 0) {
splx(s);
goto release;
}
- space = sbspace(&so->so_snd);
- if (space <= 0 || sosendallatonce(so) && space < u.u_count) {
- if (so->so_state & SS_NBIO)
- snderr(EWOULDBLOCK);
- sbunlock(&so->so_snd);
- sbwait(&so->so_snd);
- splx(s);
- goto restart;
+ if (flags & SOF_OOB)
+ space = 1024;
+ else {
+ space = sbspace(&so->so_snd);
+ if (space <= 0 ||
+ sosendallatonce(so) && space < uio->uio_resid) {
+ if (so->so_state & SS_NBIO)
+ snderr(EWOULDBLOCK);
+ sbunlock(&so->so_snd);
+ sbwait(&so->so_snd);
+ splx(s);
+ goto restart;
+ }
}
splx(s);
- while (u.u_count && space > 0) {
- MGET(m, 1);
+ 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;
+ }
+ MGET(m, M_WAIT, MT_DATA);
if (m == NULL) {
error = ENOBUFS; /* SIGPIPE? */
goto release;
}
- if (u.u_count >= CLBYTES && space >= CLBYTES) {
+ if (iov->iov_len >= CLBYTES && space >= CLBYTES) {
register struct mbuf *p;
MCLGET(p, 1);
if (p == 0)
len = CLBYTES;
} else {
nopages:
- m->m_off = MMINOFF;
- len = MIN(MLEN, u.u_count);
+ len = MIN(MLEN, iov->iov_len);
}
- iomove(mtod(m, caddr_t), len, B_WRITE);
+ (void) uiomove(mtod(m, caddr_t), len, UIO_WRITE, uio);
m->m_len = len;
*mp = m;
mp = &m->m_next;
- space = sbspace(&so->so_snd);
+ if (flags & SOF_OOB)
+ space -= len;
+ else
+ space = sbspace(&so->so_snd);
}
goto again;
return (error);
}
-soreceive(so, asa)
+soreceive(so, aname, uio, flags)
register struct socket *so;
- struct sockaddr *asa;
+ struct mbuf **aname;
+ struct uio *uio;
+ int flags;
{
register struct mbuf *m, *n;
- u_int len;
- int eor, s, error = 0, cnt = u.u_count;
- caddr_t base = u.u_base;
+ int len;
+ int eor, s, error = 0, moff, tomark;
+
+ if (flags & SOF_OOB) {
+ m = m_get(M_WAIT, MT_DATA);
+ if (m == NULL)
+ return (ENOBUFS);
+ error = (*so->so_proto->pr_usrreq)(so, PRU_RCVOOB,
+ m, (struct mbuf *)0);
+ if (error)
+ goto bad;
+ 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);
+bad:
+ if (m)
+ m_freem(m);
+ return (error);
+ }
restart:
sblock(&so->so_rcv);
splx(s);
goto restart;
}
+ u.u_ru.ru_msgrcv++;
m = so->so_rcv.sb_mb;
if (m == 0)
panic("receive");
if (so->so_proto->pr_flags & PR_ADDR) {
- if (m->m_len != sizeof (struct sockaddr))
- panic("soreceive addr");
- if (asa)
- bcopy(mtod(m, caddr_t), (caddr_t)asa, sizeof (*asa));
- so->so_rcv.sb_cc -= m->m_len;
- so->so_rcv.sb_mbcnt -= MSIZE;
- m = m_free(m);
+ if ((flags & SOF_PREVIEW) == 0) {
+ so->so_rcv.sb_cc -= m->m_len;
+ so->so_rcv.sb_mbcnt -= MSIZE;
+ }
+ if (aname) {
+ if (flags & SOF_PREVIEW) {
+ *aname = m_copy(m, 0, m->m_len);
+ if (*aname == NULL)
+ panic("receive 2");
+ } else
+ *aname = m;
+ m = m->m_next;
+ (*aname)->m_next = 0;
+ } else
+ if (flags & SOF_PREVIEW)
+ m = m->m_next;
+ else
+ m = m_free(m);
if (m == 0)
- panic("receive 2");
- so->so_rcv.sb_mb = m;
+ panic("receive 3");
+ if ((flags & SOF_PREVIEW) == 0)
+ so->so_rcv.sb_mb = m;
}
- so->so_state &= ~SS_RCVATMARK;
- if (so->so_oobmark && cnt > so->so_oobmark)
- cnt = so->so_oobmark;
eor = 0;
+ moff = 0;
+ tomark = so->so_oobmark;
do {
- len = MIN(m->m_len, cnt);
+ if (uio->uio_resid <= 0)
+ break;
+ len = uio->uio_resid;
+ so->so_state &= ~SS_RCVATMARK;
+ if (tomark && len > tomark)
+ len = tomark;
+ if (moff+len > m->m_len - moff)
+ len = m->m_len - moff;
splx(s);
- iomove(mtod(m, caddr_t), len, B_READ);
- cnt -= len;
+ error =
+ uiomove(mtod(m, caddr_t) + moff, (int)len, UIO_READ, uio);
s = splnet();
if (len == m->m_len) {
eor = (int)m->m_act;
- sbfree(&so->so_rcv, m);
- so->so_rcv.sb_mb = m->m_next;
- MFREE(m, n);
+ if (flags & SOF_PREVIEW)
+ m = m->m_next;
+ else {
+ sbfree(&so->so_rcv, m);
+ MFREE(m, n);
+ m = n;
+ so->so_rcv.sb_mb = m;
+ }
+ moff = 0;
} else {
- m->m_off += len;
- m->m_len -= len;
- so->so_rcv.sb_cc -= len;
+ if (flags & SOF_PREVIEW)
+ moff += len;
+ else {
+ m->m_off += len;
+ m->m_len -= len;
+ so->so_rcv.sb_cc -= len;
+ }
}
- } while ((m = so->so_rcv.sb_mb) && cnt && !eor);
+ if ((flags & SOF_PREVIEW) == 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;
+ }
+ } 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;
m = n;
} while (eor == 0);
if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb)
- (*so->so_proto->pr_usrreq)(so, PRU_RCVD, 0, 0);
- if (so->so_oobmark) {
- so->so_oobmark -= u.u_base - base;
- if (so->so_oobmark == 0)
- so->so_state |= SS_RCVATMARK;
- }
+ (*so->so_proto->pr_usrreq)(so, PRU_RCVD,
+ (struct mbuf *)0, (struct mbuf *)0);
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;
{
so->so_state |= SS_NBIO;
else
so->so_state &= ~SS_NBIO;
- return;
+ break;
case FIOASYNC:
if (*(int *)data)
so->so_state |= SS_ASYNC;
else
so->so_state &= ~SS_ASYNC;
- return;
-
- case SIOCSKEEP:
- if (*(int *)data)
- so->so_options &= ~SO_KEEPALIVE;
- else
- so->so_options |= SO_KEEPALIVE;
- return;
-
- case SIOCGKEEP:
- *(int *)data = (so->so_options & SO_KEEPALIVE) != 0;
- return;
-
- case SIOCSLINGER:
- so->so_linger = *(int *)data;
- if (so->so_linger)
- so->so_options &= ~SO_DONTLINGER;
- else
- so->so_options |= SO_DONTLINGER;
- return;
-
- case SIOCGLINGER:
- *(int *)data = so->so_linger;
- return;
+ break;
case SIOCSPGRP:
so->so_pgrp = *(int *)data;
- return;
+ break;
case SIOCGPGRP:
*(int *)data = so->so_pgrp;
- return;
-
- case SIOCDONE: {
- int flags = *(int *)data;
-
- flags++;
- if (flags & FREAD) {
- int s = splimp();
- socantrcvmore(so);
- sbflush(&so->so_rcv);
- splx(s);
- }
- if (flags & FWRITE)
- u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_SHUTDOWN, (struct mbuf *)0, 0);
- return;
- }
-
- case SIOCSENDOOB: {
- char oob = *(char *)data;
- struct mbuf *m;
-
- m = m_get(M_DONTWAIT);
- if (m == 0) {
- u.u_error = ENOBUFS;
- return;
- }
- m->m_off = MMINOFF;
- m->m_len = sizeof (char);
- *mtod(m, char *) = oob;
- (*so->so_proto->pr_usrreq)(so, PRU_SENDOOB, m, 0);
- return;
- }
-
- case SIOCRCVOOB: {
- struct mbuf *m = m_get(M_DONTWAIT);
-
- if (m == 0) {
- u.u_error = ENOBUFS;
- return;
- }
- m->m_off = MMINOFF; *mtod(m, caddr_t) = 0;
- (*so->so_proto->pr_usrreq)(so, PRU_RCVOOB, m, 0);
- *(char *)data = *mtod(m, char *);
- (void) m_free(m);
- return;
- }
+ break;
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
- return;
+ break;
/* routing table update calls */
case SIOCADDRT:
case SIOCDELRT:
if (!suser())
- return;
- u.u_error = rtrequest(cmd, (struct rtentry *)data);
- return;
+ return (u.u_error);
+ return (rtrequest(cmd, (struct rtentry *)data));
/* type/protocol specific ioctls */
+ default:
+ return (ENOTTY); /* XXX */
}
- u.u_error = EOPNOTSUPP;
+ return (0);
}