home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3
/
hamradioversion3.0examsandprograms1992.iso
/
misc
/
9q920411
/
socket.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-04-11
|
15KB
|
674 lines
/* Application programming interface routines - based loosely on the
* "socket" model in Berkeley UNIX.
*
* Copyright 1991 Phil Karn, KA9Q
*/
#include <stdio.h>
#ifdef __STDC__
#include <stdarg.h>
#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];
}