/* Application programming interface routines - based loosely on the * "socket" model in Berkeley UNIX. * * Copyright 1991 Phil Karn, KA9Q */ #include #ifdef __STDC__ #include #endif #include "global.h" #include "mbuf.h" #include "netuser.h" #include "proc.h" #include "lzw.h" #include "usock.h" #include "socket.h" char *Socktypes[] = { "Not Used", "TCP", "UDP", "AX25 I", "AX25 UI", "Raw IP", "NETROM3", "NETROM", "Loc St", "Loc Dg" }; char Badsocket[] = "Bad socket"; struct usock *Usock; /* Socket entry array */ /* Initialize user socket array */ void sockinit() { if(Usock != NULLUSOCK) return; /* Already initialized */ Usock = (struct usock *)callocw(Nusock,sizeof(struct usock)); } /* Create a user socket, return socket index * The mapping to actual protocols is as follows: * * * ADDRESS FAMILY Stream Datagram Raw Seq. Packet * * AF_INET TCP UDP IP * AF_AX25 I-frames UI-frames * AF_NETROM NET/ROM L3 NET/ROM L4 * AF_LOCAL stream loopback packet loopback */ int socket(af,type,protocol) int af; /* Address family */ int type; /* Stream or datagram */ int protocol; /* Used for raw IP sockets */ { register struct usock *up; struct socklink *sp; int s; for(up=Usock;up < &Usock[Nusock];up++) if(up->type == NOTUSED) break; if(up == &Usock[Nusock]){ /* None left */ errno = EMFILE; return -1; } up->refcnt = 1; s = up - Usock + SOCKBASE; errno = 0; up->noblock = 0; up->rdysock = -1; up->cb.p = NULLCHAR; up->peername = up->name = NULLCHAR; up->namelen = up->peernamelen = 0; up->owner = Curproc; up->obuf = NULLBUF; up->tos = 0; memset(up->errcodes,0,sizeof(up->errcodes)); memset(up->eol,0,sizeof(up->eol)); up->flush = '\n'; /* default is line buffered */ switch(af){ case AF_LOCAL: switch(type){ case SOCK_STREAM: up->type = TYPE_LOCAL_STREAM; break; case SOCK_DGRAM: up->type = TYPE_LOCAL_DGRAM; break; default: errno = ESOCKTNOSUPPORT; break; } break; case AF_AX25: switch(type){ case SOCK_STREAM: up->type = TYPE_AX25I; break; case SOCK_DGRAM: up->type = TYPE_AX25UI; break; default: errno = ESOCKTNOSUPPORT; break; } break; case AF_NETROM: switch(type){ case SOCK_RAW: up->type = TYPE_NETROML3; break; case SOCK_SEQPACKET: up->type = TYPE_NETROML4; break; default: errno = ESOCKTNOSUPPORT; break; } break; case AF_INET: switch(type){ case SOCK_STREAM: up->type = TYPE_TCP; break; case SOCK_DGRAM: up->type = TYPE_UDP; break; case SOCK_RAW: up->type = TYPE_RAW; break; default: errno = ESOCKTNOSUPPORT; break; } break; default: errno = EAFNOSUPPORT; break; } /* Look for entry in protocol table */ for(sp = Socklink;sp->type != -1;sp++){ if(up->type == sp->type) break; } up->sp = sp; if(sp->type == -1 || sp->socket == NULLFP ||(*sp->socket)(up,protocol) == -1){ errno = ESOCKTNOSUPPORT; return -1; } return s; } /* Attach a local address/port to a socket. If not issued before a connect * or listen, will be issued automatically */ int bind(s,name,namelen) int s; /* Socket index */ char *name; /* Local name */ int namelen; /* Length of name */ { register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(name == NULLCHAR){ errno = EFAULT; return -1; } if(up->name != NULLCHAR){ /* Bind has already been issued */ errno = EINVAL; return -1; } sp = up->sp; if(sp->check != NULLFP && (*sp->check)(name,namelen) == -1){ /* Incorrect length or family for chosen protocol */ errno = EAFNOSUPPORT; return -1; } /* Stash name in an allocated block */ up->namelen = namelen; up->name = mallocw(namelen); memcpy(up->name,name,namelen); /* a bind routine is optional - don't fail if it isn't present */ if(sp->bind != NULLFP && (*sp->bind)(up) == -1){ errno = EOPNOTSUPP; return -1; } return 0; } /* Post a listen on a socket */ int listen(s,backlog) int s; /* Socket index */ int backlog; /* 0 for a single connection, !=0 for multiple connections */ { register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->cb.p != NULLCHAR){ errno = EISCONN; return -1; } sp = up->sp; /* Fail if listen routine isn't present */ if(sp->listen == NULLFP || (*sp->listen)(up,backlog) == -1){ errno = EOPNOTSUPP; return -1; } return 0; } /* Initiate active open. For datagram sockets, merely bind the remote address. */ int connect(s,peername,peernamelen) int s; /* Socket index */ char *peername; /* Peer name */ int peernamelen; /* Length of peer name */ { register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(peername == NULLCHAR){ /* Connect must specify a remote address */ errno = EFAULT; return -1; } sp = up->sp; /* Check name format, if checking routine is available */ if(sp->check != NULLFP && (*sp->check)(peername,peernamelen) == -1){ errno = EAFNOSUPPORT; return -1; } if(up->peername != NULLCHAR) free(up->peername); up->peername = mallocw(peernamelen); memcpy(up->peername,peername,peernamelen); up->peernamelen = peernamelen; /* a connect routine is optional - don't fail if it isn't present */ if(sp->connect != NULLFP && (*sp->connect)(up) == -1){ return -1; } return 0; } /* Wait for a connection. Valid only for connection-oriented sockets. */ int accept(s,peername,peernamelen) int s; /* Socket index */ char *peername; /* Peer name */ int *peernamelen; /* Length of peer name */ { int i; register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->cb.p == NULLCHAR){ errno = EOPNOTSUPP; return -1; } sp = up->sp; /* Fail if accept flag isn't set */ if(sp->accept == FALSE){ errno = EOPNOTSUPP; return -1; } /* Wait for the state-change upcall routine to signal us */ while(up->cb.p != NULLCHAR && up->rdysock == -1){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(up->cb.p == NULLCHAR){ /* Blown away */ errno = EBADF; return -1; } i = up->rdysock; up->rdysock = -1; up = itop(i); if(peername != NULLCHAR && peernamelen != NULL){ *peernamelen = min(up->peernamelen,*peernamelen); memcpy(peername,up->peername,*peernamelen); } return i; } /* Low-level receive routine. Passes mbuf back to user; more efficient than * higher-level functions recv() and recvfrom(). Datagram sockets ignore * the len parameter. */ int recv_mbuf(s,bpp,flags,from,fromlen) int s; /* Socket index */ struct mbuf **bpp; /* Place to stash receive buffer */ int flags; /* Unused; will control out-of-band data, etc */ char *from; /* Peer address (only for datagrams) */ int *fromlen; /* Length of peer address */ { register struct usock *up; int cnt; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->ibuf != NULLBUF){ /* Return input buffer */ cnt = len_p(up->ibuf); if(bpp != NULLBUFP) *bpp = up->ibuf; else free_p(up->ibuf); up->ibuf = NULLBUF; return cnt; } sp = up->sp; /* Fail if recv routine isn't present */ if(sp->recv == NULLFP || (cnt = (*sp->recv)(up,bpp,from,fromlen)) == -1){ errno = EOPNOTSUPP; return -1; } return cnt; } /* Low level send routine; user supplies mbuf for transmission. More * efficient than send() or sendto(), the higher level interfaces. * The "to" and "tolen" parameters are ignored on connection-oriented * sockets. * * In case of error, bp is freed so the caller doesn't have to worry about it. */ int send_mbuf(s,bp,flags,to,tolen) int s; /* Socket index */ struct mbuf *bp; /* Buffer to send */ int flags; /* not currently used */ char *to; /* Destination, only for datagrams */ int tolen; /* Length of destination */ { register struct usock *up; int cnt; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ free_p(bp); errno = EBADF; return -1; } sp = up->sp; /* Fail if send routine isn't present (shouldn't happen) */ if(sp->send == NULLFP){ free_p(bp); return -1; } #ifndef notdef if(up->obuf != NULLBUF){ /* If there's unflushed output, send it. * Note the importance of clearing up->obuf * before the recursive call! */ struct mbuf *bp1; bp1 = up->obuf; up->obuf = NULLBUF; send_mbuf(s,bp1,flags,to,tolen); } #endif /* The proto send routine is expected to free the buffer * we pass it even if the send fails */ if((cnt = (*sp->send)(up,bp,to)) == -1){ errno = EOPNOTSUPP; return -1; } return cnt; } /* Return local name passed in an earlier bind() call */ int getsockname(s,name,namelen) int s; /* Socket index */ char *name; /* Place to stash name */ int *namelen; /* Length of same */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(name == NULLCHAR || namelen == (int *)NULL){ errno = EFAULT; return -1; } if(up->name == NULLCHAR){ /* Not bound yet */ *namelen = 0; return 0; } if(up->name != NULLCHAR){ *namelen = min(*namelen,up->namelen); memcpy(name,up->name,*namelen); } return 0; } /* Get remote name, returning result of earlier connect() call. */ int getpeername(s,peername,peernamelen) int s; /* Socket index */ char *peername; /* Place to stash name */ int *peernamelen; /* Length of same */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->peername == NULLCHAR){ errno = ENOTCONN; return -1; } if(peername == NULLCHAR || peernamelen == (int *)NULL){ errno = EFAULT; return -1; } *peernamelen = min(*peernamelen,up->peernamelen); memcpy(peername,up->peername,*peernamelen); return 0; } /* Return length of protocol queue, either send or receive. */ int socklen(s,rtx) int s; /* Socket index */ int rtx; /* 0 = receive queue, 1 = transmit queue */ { register struct usock *up; struct socklink *sp; int len = -1; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->cb.p == NULLCHAR){ errno = ENOTCONN; return -1; } if(rtx < 0 || rtx > 1){ errno = EINVAL; return -1; } sp = up->sp; /* Fail if qlen routine isn't present */ if(sp->qlen == NULLFP || (len = (*sp->qlen)(up,rtx)) == -1){ errno = EOPNOTSUPP; return -1; } return len; } /* Force retransmission. Valid only for connection-oriented sockets. */ int sockkick(s) int s; /* Socket index */ { register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } sp = up->sp; /* Fail if kick routine isn't present */ if(sp->kick == NULLFP){ errno = EOPNOTSUPP; return -1; } if((*sp->kick)(up) == -1) return -1; return 0; } /* Change owner of socket, return previous owner */ struct proc * sockowner(s,newowner) int s; /* Socket index */ struct proc *newowner; /* Process table address of new owner */ { register struct usock *up; struct proc *pp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return NULLPROC; } pp = up->owner; if(newowner != NULLPROC) up->owner = newowner; return pp; } /* Close down a socket three ways. Type 0 means "no more receives"; this * replaces the incoming data upcall with a routine that discards further * data. Type 1 means "no more sends", and obviously corresponds to sending * a TCP FIN. Type 2 means "no more receives or sends". This I interpret * as "abort the connection". */ int shutdown(s,how) int s; /* Socket index */ int how; /* (see above) */ { register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->cb.p == NULLCHAR){ errno = ENOTCONN; return -1; } sp = up->sp; /* Just close the socket if special shutdown routine not present */ if(sp->shut == NULLFP){ close_s(s); } else if((*sp->shut)(up,how) == -1){ return -1; } psignal(up,0); return 0; } /* Close a socket, freeing it for reuse. Try to do a graceful close on a * TCP socket, if possible */ int close_s(s) int s; /* Socket index */ { register struct usock *up; struct socklink *sp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(--up->refcnt > 0) return 0; /* Others are still using it */ usflush(s); if(up->ibuf != NULLBUF){ free_p(up->ibuf); up->ibuf = NULLBUF; } /* Call proto-specific close routine if there is one */ if((sp = up->sp) != NULL && sp->close != NULLFP) (*sp->close)(up); if(up->zout != NULLLZW || up->zin != NULLLZW) lzwfree(up); free(up->name); free(up->peername); up->cb.p = NULLCHAR; up->name = up->peername = NULLCHAR; up->type = NOTUSED; up->sp = NULL; psignal(up,0); /* Wake up anybody doing an accept() or recv() */ return 0; } /* Increment reference count for specified socket */ int usesock(s) int s; { struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } up->refcnt++; return 0; } /* Blow away all sockets belonging to a certain process. Used by killproc(). */ void freesock(pp) struct proc *pp; { register struct usock *up; register int i; for(i=SOCKBASE;i < Nusock+SOCKBASE;i++){ up = itop(i); if(up != NULLUSOCK && up->type != NOTUSED && up->owner == pp) shutdown(i,2); } } /* Set Internet type-of-service to be used */ int settos(s,tos) int s,tos; { struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } up->tos = tos; return 0; } /* Return a pair of mutually connected sockets in sv[0] and sv[1] */ int socketpair(af,type,protocol,sv) int af; int type; int protocol; int sv[]; { struct usock *up0, *up1; if(sv == NULLINT){ errno = EFAULT; return -1; } if(af != AF_LOCAL){ errno = EAFNOSUPPORT; return -1; } if(type != SOCK_STREAM && type != SOCK_DGRAM){ errno = ESOCKTNOSUPPORT; return -1; } if((sv[0] = socket(af,type,protocol)) == -1) return -1; if((sv[1] = socket(af,type,protocol)) == -1){ close_s(sv[0]); return -1; } up0 = itop(sv[0]); up1 = itop(sv[1]); up0->cb.local->peer = up1; up1->cb.local->peer = up0; return sv[1]; }