/* socket.c 1.3 84/11/02 */ #include "sys/param.h" #include "sys/config.h" #include "sys/errno.h" #include "sys/types.h" #include "sys/sysmacros.h" #include "sys/systm.h" #include "sys/dir.h" #include "sys/termio.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/proc.h" #include "sys/file.h" #include "sys/inode.h" #include "sys/buf.h" #include "net/misc.h" #include "net/mbuf.h" #include "net/protosw.h" #include "net/socket.h" #include "net/socketvar.h" #include "net/ubavar.h" #include "sys/stat.h" #include "sys/ioctl.h" #include "net/in.h" #include "net/in_systm.h" #include "net/route.h" /* * Socket support routines. * * DEAL WITH INTERRUPT NOTIFICATION. */ /* * Create a socket. */ socreate(aso, type, asp, asa, options) struct socket **aso; int type; struct sockproto *asp; struct sockaddr *asa; int options; { register struct protosw *prp; register struct socket *so; 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; } /* * If protocol specified, look for it, otherwise * for a protocol of the correct type in the right family. */ if (proto) prp = pffindproto(pf, proto); else prp = pffindtype(pf, type); if (prp == 0) return (EPROTONOSUPPORT); /* * Get a socket structure. */ MSGET(so, struct socket, 1); if (so == 0) return (ENOBUFS); so->so_options = options; so->so_state = 0; 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); if (error) { #ifdef NEVER /* * This commenting-out came from jsq@utexas-11; see the * iptcp11 messages. Simply means temp lack of mbufs... */ if (so->so_snd.sb_mbmax || so->so_rcv.sb_mbmax) panic("socreate"); #endif so->so_state |= SS_USERGONE; sofree(so); return (error); } *aso = so; return (0); } sofree(so) struct socket *so; { if (so->so_pcb || (so->so_state & SS_USERGONE) == 0) return; sbrelease(&so->so_snd); sbrelease(&so->so_rcv); MSFREE(so); } /* * Close a socket on last file table reference removal. * Initiate disconnect if connected. * Free socket when disconnect complete. * * THIS IS REALLY A UNIX INTERFACE ROUTINE */ soclose(so, exiting) register struct socket *so; int exiting; { int s = splnet(); /* conservative */ if (so->so_pcb == 0) goto discard; if (exiting) 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) { if (exiting) goto drop; splx(s); return; } } if ((so->so_options & SO_DONTLINGER) == 0) { if ((so->so_state & SS_ISDISCONNECTING) && (so->so_state & SS_NBIO) && exiting == 0) { u.u_error = EINPROGRESS; /* * billn. This is a kludge rendered unnecessary * by a bug fix 12/27/83. so->so_state |= SS_USERGONE; */ splx(s); return; } /* should use tsleep here, for at most linger */ while (so->so_state & SS_ISCONNECTED) (void) 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) { splx(s); return; } } discard: so->so_state |= SS_USERGONE; sofree(so); splx(s); } /*ARGSUSED*/ sostat(so, sb) struct socket *so; struct stat *sb; { /* bug fix by JC Stewart of sri; this zeros the kernel, should zero user data (!)... bzero((caddr_t)sb, sizeof (*sb)); /* XXX */ return (0); /* XXX */ } /* * Accept connection on a socket. */ soaccept(so, asa) struct socket *so; struct sockaddr *asa; { int s = splnet(); int error; if ((so->so_options & SO_ACCEPTCONN) == 0) { error = EINVAL; /* XXX */ goto bad; } if ((so->so_state & SS_CONNAWAITING) == 0) { error = ENOTCONN; goto bad; } so->so_state &= ~SS_CONNAWAITING; error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, 0, (caddr_t)asa); bad: 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) struct socket *so; struct sockaddr *asa; { int s = splnet(); int error; if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) { error = EISCONN; goto bad; } error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, 0, (caddr_t)asa); 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) struct socket *so; struct sockaddr *asa; { int s = splnet(); int error; if ((so->so_state & SS_ISCONNECTED) == 0) { error = ENOTCONN; goto bad; } if (so->so_state & SS_ISDISCONNECTING) { error = EALREADY; goto bad; } error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, asa); bad: splx(s); return (error); } /* * Send on a socket. * If send must go all at once and message is larger than * send buffering, then hard error. * Lock against other senders. * If must go all at once and not enough room now, then * inform user that this would block and do nothing. */ sosend(so, asa) register struct socket *so; struct sockaddr *asa; { struct mbuf *top = 0; register struct mbuf **mp = ⊤ register struct mbuf *m; register struct user *up = &u; register u_int len; register int space; int error = 0, s; if (sosendallatonce(so) && up->u_count > 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 restart: sblock(&so->so_snd); #define snderr(errno) { error = errno; splx(s); goto release; } again: s = splnet(); if (so->so_state & SS_CANTSENDMORE) { psignal(up->u_procp, SIGPIPE); snderr(EPIPE); } if (so->so_error) { error = so->so_error; so->so_error = 0; /* ??? */ splx(s); goto release; } if ((so->so_state & SS_ISCONNECTED) == 0) { if (so->so_proto->pr_flags & PR_CONNREQUIRED) snderr(ENOTCONN); if (asa == 0) snderr(EDESTADDRREQ); } if (top) { error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, top, asa); top = 0; if (error) { splx(s); goto release; } mp = ⊤ } if (up->u_count == 0) { splx(s); goto release; } space = sbspace(&so->so_snd); if (space <= 0 || sosendallatonce(so) && space < up->u_count) { if (so->so_state & SS_NBIO) snderr(EWOULDBLOCK); sbunlock(&so->so_snd); sbwait(&so->so_snd); splx(s); goto restart; } splx(s); while (up->u_count && space > 0) { MGET(m, 1); if (m == NULL) { error = ENOBUFS; /* SIGPIPE? */ goto release; } m->m_off = MMINOFF; len = (u_int)MIN(((int)MLEN), ((int)up->u_count)); iomove(mtod(m, caddr_t), (int)len, B_WRITE); if (error = up->u_error) goto release; m->m_len = len; *mp = m; mp = &m->m_next; space = sbspace(&so->so_snd); } goto again; release: sbunlock(&so->so_snd); if (top) m_freem(top); return (error); } soreceive(so, asa) register struct socket *so; struct sockaddr *asa; { register struct mbuf *m, *n; u_int len; int eor, s, error = 0, cnt = u.u_count; caddr_t base = u.u_base; 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); 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); sbunlock(&so->so_rcv); sbwait(&so->so_rcv); splx(s); goto restart; } 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 (m == 0) panic("receive 2"); 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; do { len = MIN(m->m_len, cnt); splx(s); iomove(mtod(m, caddr_t), (int)len, B_READ); cnt -= len; 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); } 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 ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0) do { if (m == 0) panic("receive 3"); sbfree(&so->so_rcv, m); eor = (int)m->m_act; so->so_rcv.sb_mb = m->m_next; MFREE(m, n); 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; } release: sbunlock(&so->so_rcv); splx(s); return (error); } sohasoutofband(so) struct socket *so; { struct proc *pfind(); if (so->so_pgrp == 0) return; if (so->so_pgrp > 0) /* gsignal(so->so_pgrp, SIGURG); */ signal(so->so_pgrp, SIGURG); else { struct proc *p = pfind(-so->so_pgrp); if (p) psignal(p, SIGURG); } } /*ARGSUSED*/ soioctl(so, cmd, cmdp) register struct socket *so; int cmd; register caddr_t cmdp; { extern struct uba_device ubdinit[]; long *iaddrp = (long *)((int)ubdinit + 0xA); switch (cmd) { case SIOCCIADDR: /* set internet address ie write into uba flags */ if (u.u_ruid != 0 && u.u_uid != 0) { u.u_error = EPERM; return; } if (copyin(cmdp, (caddr_t)iaddrp, sizeof (long))) { u.u_error = EFAULT; return; } return; case SIOCGIADDR: if (copyout((caddr_t)iaddrp, cmdp, sizeof (long))) u.u_error = EFAULT; return; case FIONBIO: { int nbio; if (copyin(cmdp, (caddr_t)&nbio, sizeof (nbio))) { u.u_error = EFAULT; return; } if (nbio) so->so_state |= SS_NBIO; else so->so_state &= ~SS_NBIO; return; } case FIOASYNC: { int async; if (copyin(cmdp, (caddr_t)&async, sizeof (async))) { u.u_error = EFAULT; return; } if (async) so->so_state |= SS_ASYNC; else so->so_state &= ~SS_ASYNC; return; } case FIONREAD: { long nread = so->so_rcv.sb_cc; if (copyout((caddr_t)&nread, cmdp, sizeof (nread))) u.u_error = EFAULT; return; } case SIOCSKEEP: { int keep; if (copyin(cmdp, (caddr_t)&keep, sizeof (keep))) { u.u_error = EFAULT; return; } if (keep) so->so_options |= SO_KEEPALIVE; else so->so_options &= ~SO_KEEPALIVE; return; } case SIOCGKEEP: { int keep = (so->so_options & SO_KEEPALIVE) != 0; if (copyout((caddr_t)&keep, cmdp, sizeof (keep))) u.u_error = EFAULT; return; } case SIOCSLINGER: { int linger; if (copyin(cmdp, (caddr_t)&linger, sizeof (linger))) { u.u_error = EFAULT; return; } so->so_linger = linger; if (so->so_linger) so->so_options &= ~SO_DONTLINGER; else so->so_options |= SO_DONTLINGER; return; } case SIOCGLINGER: { int linger = so->so_linger; if (copyout((caddr_t)&linger, cmdp, sizeof (linger))) { u.u_error = EFAULT; return; } } case SIOCSPGRP: { int pgrp; if (copyin(cmdp, (caddr_t)&pgrp, sizeof (pgrp))) { u.u_error = EFAULT; return; } so->so_pgrp = pgrp; return; } case SIOCGPGRP: { int pgrp = so->so_pgrp; if (copyout((caddr_t)&pgrp, cmdp, sizeof (pgrp))) { u.u_error = EFAULT; return; } } case SIOCDONE: { int flags; if (copyin(cmdp, (caddr_t)&flags, sizeof (flags))) { u.u_error = EFAULT; return; } 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; struct mbuf *m; if (copyin(cmdp, (caddr_t)&oob, sizeof (oob))) { u.u_error = EFAULT; return; } m = m_get(M_DONTWAIT); if (m == 0) { u.u_error = ENOBUFS; return; } m->m_off = MMINOFF; m->m_len = 1; *mtod(m, caddr_t) = 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); if (copyout(mtod(m, caddr_t), cmdp, sizeof (char))) { u.u_error = EFAULT; return; } (void) m_free(m); return; } case SIOCATMARK: { int atmark = (so->so_state&SS_RCVATMARK) != 0; if (copyout((caddr_t)&atmark, cmdp, sizeof (atmark))) { u.u_error = EFAULT; return; } return; } /* routing table update calls */ case SIOCADDRT: case SIOCDELRT: case SIOCCHGRT: { struct rtentry route; if (!suser()) return; if (copyin(cmdp, (caddr_t)&route, sizeof (route))) { u.u_error = EFAULT; return; } u.u_error = rtrequest(cmd, &route); return; } /* type/protocol specific ioctls */ } u.u_error = EOPNOTSUPP; }