#include <stdio.h>
#ifdef	__STDC__
#include <stdarg.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "udp.h"
#include "ax25.h"
#include "lapb.h"
#include "netrom.h"
#include "nr4.h"
#include "proc.h"
#include "usock.h"
#include "socket.h"
#include "config.h"

static void autobind __ARGS((int s,int af));
static int checkaddr __ARGS((int type,char *name,int namelen));
static void rip_recv __ARGS((struct raw_ip *rp));
static void s_trcall __ARGS((struct tcb *tcb,int cnt));
static void s_tscall __ARGS((struct tcb *tcb,int old,int new));
static void s_ttcall __ARGS((struct tcb *tcb,int cnt));
static void s_urcall __ARGS((struct iface *iface,struct udp_cb *udp,int cnt));
static void trdiscard __ARGS((struct tcb *tcb,int cnt));
#ifdef	NETROM
static void s_nrcall __ARGS((struct nr4cb *cb,int cnt));
static void s_nscall __ARGS((struct nr4cb *cb,int old,int new));
static void s_ntcall __ARGS((struct nr4cb *cb,int cnt));
#endif

static int16 Lport = 1024;

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 */
int Nusock = DEFNSOCK;		/* Number of socket entries */

/* The following two variables are needed because there can be only one
 * socket listening on each of the AX.25 modes (I and UI)
 */
#ifdef	AX25
int Axi_sock = -1;	/* Socket number listening for AX25 connections */
static int Axui_sock = -1;	/* Socket number listening for AX25 UI frames */
static struct mbuf *Bcq;	/* Queue of incoming UI frames */

/* Function that handles incoming UI frames from lapb.c */
void
beac_input(iface,src,bp)
struct iface *iface;
char *src;
struct mbuf *bp;
{
	struct mbuf *hdr;
	struct sockaddr_ax *sax;

	if(Axui_sock == -1){
		/* Nobody there to read it */
		free_p(bp);
	} else {
		if((hdr = pushdown(NULLBUF,sizeof(struct sockaddr_ax))) == NULLBUF){
			free_p(bp);
			return;
		}
		sax = (struct sockaddr_ax *)hdr->data;
		sax->sax_family = AF_AX25;
		memcpy(sax->ax25_addr,src,AXALEN);
		strncpy(sax->iface,iface->name,ILEN);
		hdr->next = bp;
		enqueue(&Bcq,hdr);
	}
}
#endif

/* 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;
	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;
	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;
	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:
		up->cb.local = callocw(1,sizeof(struct loc));
		switch(type){
		case SOCK_STREAM:
			up->type = TYPE_LOCAL_STREAM;
			up->cb.local->hiwat = LOCSFLOW;
			break;
		case SOCK_DGRAM:
			up->type = TYPE_LOCAL_DGRAM;
			up->cb.local->hiwat = LOCDFLOW;
			break;
		default:
			free(up->cb.local);
			errno = ESOCKTNOSUPPORT;
			break;
		}
		break;
#ifdef	AX25
	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;
		}
		strcpy(up->eol,AX_EOL);
		break;
#endif
#ifdef NETROM
	case AF_NETROM:
		switch(type){
		case SOCK_RAW:
			up->type = TYPE_NETROML3;
			up->cb.rnr = raw_nr((char)protocol);
			break;
		case SOCK_SEQPACKET:
			up->type = TYPE_NETROML4;
			break;
		default:
			errno = ESOCKTNOSUPPORT;
			break;
		}
		strcpy(up->eol,AX_EOL);
		break;
#endif
	case AF_INET:
		switch(type){
		case SOCK_STREAM:
			up->type = TYPE_TCP;
			strcpy(up->eol,INET_EOL);
			break;
		case SOCK_DGRAM:
			up->type = TYPE_UDP;
			break;
		case SOCK_RAW:
			up->type = TYPE_RAW;
			up->cb.rip = raw_ip(protocol,rip_recv);
                        up->cb.rip->user = s;
			break;
		default:
			errno = ESOCKTNOSUPPORT;
			break;
		}
		break;
	default:
		errno = EAFNOSUPPORT;
		break;
	}
	if(errno)
		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;
	union sp local;
	struct socket lsock;

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){
		errno = EINVAL;
		return -1;
	}
	if(name == NULLCHAR){
		errno = EFAULT;
		return -1;
	}
	if(up->name != NULLCHAR){
		/* Bind has already been issued */
		errno = EINVAL;
		return -1;
	}
	if(checkaddr(up->type,name,namelen) == -1){
		/* Incorrect length or family for chosen protocol */
		errno = EAFNOSUPP;
		return -1;	
	}
	/* Stash name in an allocated block */
	up->namelen = namelen;
	up->name = mallocw(namelen);
	memcpy(up->name,name,namelen);
	/* Create control block for datagram sockets */
	switch(up->type){
	case TYPE_UDP:
		local.in = (struct sockaddr_in *)up->name;
		lsock.address = local.in->sin_addr.s_addr;
		lsock.port = local.in->sin_port;
		up->cb.udp = open_udp(&lsock,s_urcall);
		up->cb.udp->user = s;
		break;
#ifdef	AX25
	case TYPE_AX25UI:
		if(Axui_sock != -1){
			errno = EADDRINUSE;
			return -1;
		}
		Axui_sock = s;
		break;
#endif
	}
	return 0;
}
/* Post a listen on a socket */
int
listen(s,backlog)
int s;		/* Socket index */
int backlog;	/* 0 for a single connection, 1 for multiple connections */
{
	register struct usock *up;
	union sp local;
	struct socket lsock;

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){
		errno = EINVAL;
		return -1;
	}
	if(up->cb.p != NULLCHAR){
		errno = EISCONN;
		return -1;
	}
	switch(up->type){
	case TYPE_TCP:
		if(up->name == NULLCHAR)
			autobind(s,AF_INET);

		local.in = (struct sockaddr_in *)up->name;
		lsock.address = local.in->sin_addr.s_addr;
		lsock.port = local.in->sin_port;
		up->cb.tcb = open_tcp(&lsock,NULLSOCK,
		 backlog ? TCP_SERVER:TCP_PASSIVE,0,
		s_trcall,s_ttcall,s_tscall,0,s);
		break;
#ifdef AX25
	case TYPE_AX25I:
		if(up->name == NULLCHAR)
			autobind(s,AF_AX25);
		if(s != Axi_sock){
		    errno = EOPNOTSUPP;
		    return -1;
		}
		local.ax = (struct sockaddr_ax *)up->name;
		up->cb.ax25 = open_ax25(NULLIF,local.ax->ax25_addr,NULLCHAR,
		 backlog ? AX_SERVER:AX_PASSIVE,0,
		 s_arcall,s_atcall,s_ascall,s);
		break;
#endif
#ifdef NETROM
	case TYPE_NETROML4:
		if(up->name == NULLCHAR)
			autobind(s,AF_NETROM);
		local.nr = (struct sockaddr_nr *)up->name;
		up->cb.nr4 = open_nr4(&local.nr->nr_addr,NULLNRADDR,
		 backlog ? AX_SERVER:AX_PASSIVE,
		 s_nrcall,s_ntcall,s_nscall,s);
		break;
#endif
	default:
		/* Listen not supported on datagram sockets */
		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;
	union cb cb;
	union sp local,remote;
	struct socket lsock,fsock;
#ifdef	AX25
	struct iface *iface;
#endif

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->type == TYPE_LOCAL_DGRAM || up->type == TYPE_LOCAL_STREAM){
		errno = EINVAL;
		return -1;
	}
	if(peername == NULLCHAR){
		/* Connect must specify a remote address */
		errno = EFAULT;
		return -1;
	}
	if(checkaddr(up->type,peername,peernamelen) == -1){
		errno = EAFNOSUPPORT;
		return -1;
	}
	/* Raw socket control blocks are created in socket() */
	if(up->type != TYPE_RAW && up->type != TYPE_NETROML3 &&
	   up->cb.p != NULLCHAR){
		errno = EISCONN;
		return -1;
	}
	up->peername = mallocw(peernamelen);
	memcpy(up->peername,peername,peernamelen);
	up->peernamelen = peernamelen;

	/* Set up the local socket structures */
	if(up->name == NULLCHAR){
		switch(up->type){
		case TYPE_TCP:
		case TYPE_UDP:
		case TYPE_RAW:
			autobind(s,AF_INET);
			break;
#ifdef	AX25
		case TYPE_AX25I:
		case TYPE_AX25UI:
			autobind(s,AF_AX25);
			break;
#endif
#ifdef	NETROM
		case TYPE_NETROML3:
		case TYPE_NETROML4:
			autobind(s,AF_NETROM);
			break;
#endif
		}
	}
	switch(up->type){
	case TYPE_TCP:
		/* Construct the TCP-style ports from the sockaddr structs */
		local.in = (struct sockaddr_in *)up->name;
		remote.in = (struct sockaddr_in *)up->peername;

		if(local.in->sin_addr.s_addr == INADDR_ANY)
			/* Choose a local address */
			local.in->sin_addr.s_addr =
			 locaddr(remote.in->sin_addr.s_addr);

		lsock.address = local.in->sin_addr.s_addr;
		lsock.port = local.in->sin_port;
		fsock.address = remote.in->sin_addr.s_addr;
		fsock.port = remote.in->sin_port;

		/* Open the TCB in active mode */
		up->cb.tcb = open_tcp(&lsock,&fsock,TCP_ACTIVE,0,
		 s_trcall,s_ttcall,s_tscall,0,s);

		/* Wait for the connection to complete */
		while((cb.tcb = up->cb.tcb) != NULLTCB && cb.tcb->state != TCP_ESTABLISHED){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.tcb == NULLTCB){
			/* Probably got refused */
			free(up->peername);
			up->peername = NULLCHAR;
			errno = ECONNREFUSED;
			return -1;
		}
		break;
	case TYPE_UDP:
#ifdef	AX25
	case TYPE_AX25UI:
#endif
#ifdef	NETROM
	case TYPE_NETROML3:
#endif
	case TYPE_RAW:
		/* Control block already created by bind() */
		break;
#ifdef	AX25
	case TYPE_AX25I:
		local.ax = (struct sockaddr_ax *)up->name;
		remote.ax = (struct sockaddr_ax *)up->peername;
		if((iface = if_lookup(remote.ax->iface)) == NULLIF){
			errno = EINVAL;
			return -1;
		}
		if(local.ax->ax25_addr[0] == '\0'){
			/* The local address was unspecified; set it from
			 * the interface address
			 */
			memcpy(local.ax->ax25_addr,iface->hwaddr,AXALEN);
		}
		/* If we already have an AX25 link we can use it */
		if((up->cb.ax25 = find_ax25(remote.ax->ax25_addr)) != NULLAX25
		   && up->cb.ax25->state != LAPB_DISCONNECTED &&
		   up->cb.ax25->user == -1) {
			up->cb.ax25->user = s;
			up->cb.ax25->r_upcall = s_arcall;
			up->cb.ax25->t_upcall = s_atcall;
			up->cb.ax25->s_upcall = s_ascall;
			if(up->cb.ax25->state == LAPB_CONNECTED
			   || up->cb.ax25->state == LAPB_RECOVERY)
			    	return 0;
		} else
			up->cb.ax25 = open_ax25(iface,local.ax->ax25_addr,
			 remote.ax->ax25_addr,AX_ACTIVE,
			 Axwindow,s_arcall,s_atcall,s_ascall,s);

		/* Wait for the connection to complete */
		while((cb.ax25 = up->cb.ax25) != NULLAX25 && cb.ax25->state != LAPB_CONNECTED){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.ax25 == NULLAX25){
			/* Connection probably already exists */
			free(up->peername);
			up->peername = NULLCHAR;
			errno = ECONNREFUSED;
			return -1;
		}
		break;
#endif
#ifdef	NETROM
	case TYPE_NETROML4:
		local.nr = (struct sockaddr_nr *)up->name;
		remote.nr = (struct sockaddr_nr *)up->peername;
		up->cb.nr4 = open_nr4(&local.nr->nr_addr,&remote.nr->nr_addr,
		 AX_ACTIVE,s_nrcall,s_ntcall,s_nscall,s);

		/* Wait for the connection to complete */
		while((cb.nr4 = up->cb.nr4) != NULLNR4CB && cb.nr4->state != NR4STCON){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.nr4 == NULLNR4CB){
			/* Connection probably already exists */
			free(up->peername);
			up->peername = NULLCHAR;
			errno = ECONNREFUSED;
			return -1;
		}
		break;
#endif
	}
	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;

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->type == TYPE_LOCAL_DGRAM || up->type == TYPE_LOCAL_STREAM){
		errno = EINVAL;
		return -1;
	}
	if(up->cb.p == NULLCHAR){
		errno = EOPNOTSUPP;
		return -1;
	}
	/* Accept is valid only for stream sockets */
	switch(up->type){
	case TYPE_TCP:
#ifdef	AX25
	case TYPE_AX25I:
#endif
#ifdef	NETROM
	case TYPE_NETROML4:
#endif
		break;
	default:
		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 = &Usock[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;
	union cb cb;
	struct socket fsocket;
#ifdef	NETROM
	struct nr3hdr n3hdr;
#endif
	union sp remote;
	struct ip ip;
	struct mbuf *bp;

	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;
	}
	switch(up->type){
	case TYPE_LOCAL_STREAM:
	case TYPE_LOCAL_DGRAM:
		while(up->cb.local != NULLLOC && up->cb.local->q == NULLBUF){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(up->cb.local == NULLLOC){
			errno = EBADF;
			return -1;
		}
		/* For datagram sockets, this will return the
		 * first packet on the queue. For stream sockets,
		 * this will return everything.
		 */
		bp = dequeue(&up->cb.local->q);
		if(up->cb.local->q == NULLBUF
		 && (up->cb.local->flags & LOC_SHUTDOWN))
			close_s(s);
		psignal(up,0);
		cnt = len_p(bp);
		break;
	case TYPE_TCP:
		while((cb.tcb = up->cb.tcb) != NULLTCB
		 && cb.tcb->r_upcall != trdiscard
                 && (cnt = recv_tcp(cb.tcb,&bp,(int16)0)) == -1){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.tcb == NULLTCB){
			/* Connection went away */
			errno = ENOTCONN;
			return -1;
		} else if(cb.tcb->r_upcall == trdiscard){
			/* Receive shutdown has been done */
			errno = ENOTCONN;	/* CHANGE */
			return -1;
		}
		break;
	case TYPE_UDP:
		while((cb.udp = up->cb.udp) != NULLUDP
		&& (cnt = recv_udp(cb.udp,&fsocket,&bp)) == -1){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.udp == NULLUDP){
			/* Connection went away */
			errno = ENOTCONN;
			return -1;
		}
		if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){
			remote.in = (struct sockaddr_in *)from;
			remote.in->sin_family = AF_INET;
			remote.in->sin_addr.s_addr = fsocket.address;
			remote.in->sin_port = fsocket.port;
			*fromlen = SOCKSIZE;
		}
		break;
	case TYPE_RAW:
		while((cb.rip = up->cb.rip) != NULLRIP
		 && cb.rip->rcvq == NULLBUF){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.rip == NULLRIP){
			/* Connection went away */
			errno = ENOTCONN;
			return -1;
		}
		bp = dequeue(&cb.rip->rcvq);
		ntohip(&ip,&bp);

		cnt = len_p(bp);
		if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){
			remote.in = (struct sockaddr_in *)from;
			remote.in->sin_family = AF_INET;
			remote.in->sin_addr.s_addr = ip.source;
			remote.in->sin_port = 0;
			*fromlen = SOCKSIZE;
		}
		break;
#ifdef	AX25
	case TYPE_AX25I:
		while((cb.ax25 = up->cb.ax25) != NULLAX25
		 && (bp = recv_ax25(cb.ax25,(int16)0)) == NULLBUF){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.ax25 == NULLAX25){
			/* Connection went away */
			errno = ENOTCONN;
			return -1;
		}
		cnt = bp->cnt;
		break;
	case TYPE_AX25UI:
		while(s == Axui_sock && Bcq == NULLBUF){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(&Bcq)) != 0){
				return -1;
			}
		}
		if(s != Axui_sock){
			errno = ENOTCONN;
			return -1;
		}
		bp = dequeue(&Bcq);

		if(from != NULLCHAR && fromlen != NULLINT
		   && *fromlen >= sizeof(struct sockaddr_ax)){
			pullup(&bp,from,sizeof(struct sockaddr_ax));
			*fromlen = sizeof(struct sockaddr_ax);
		} else {
			pullup(&bp,NULLCHAR,sizeof(struct sockaddr_ax));
		}
		cnt = len_p(bp);
		break;
#endif
#ifdef NETROM
	case TYPE_NETROML3:
		while((cb.rnr = up->cb.rnr) != NULLRNR
		 && cb.rnr->rcvq == NULLBUF){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.rnr == NULLRNR){
			/* Connection went away */
			errno = ENOTCONN;
			return -1;
		}
		bp = dequeue(&cb.rnr->rcvq);
		ntohnr3(&n3hdr,&bp);
		cnt = len_p(bp);
		if(from != NULLCHAR && fromlen != NULLINT
		   && *fromlen >= sizeof(struct sockaddr_nr)){
			remote.nr = (struct sockaddr_nr *)from;
			remote.nr->nr_family = AF_NETROM;
			/* The callsign of the local user is not part of
			   NET/ROM level 3, so that field is not used here */
			memcpy(remote.nr->nr_addr.node,n3hdr.source,AXALEN);
			*fromlen = sizeof(struct sockaddr_nr);
		}
		break;
	case TYPE_NETROML4:
		while((cb.nr4 = up->cb.nr4) != NULLNR4CB
		 && (bp = recv_nr4(cb.nr4,(int16)0)) == NULLBUF){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.nr4 == NULLNR4CB){
			/* Connection went away */
			errno = ENOTCONN;
			return -1;
		}
		cnt = bp->cnt;
		break;
#endif
	}
	if(bpp != NULLBUFP)
		*bpp = bp;
	else
		free_p(bp);
	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;
	union cb cb;
	union sp local,remote;
	struct socket lsock,fsock;
	int cnt;

	if((up = itop(s)) == NULLUSOCK){
		free_p(bp);
		errno = EBADF;
		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
	if(up->name == NULLCHAR){
		/* Automatic local name assignment for datagram sockets */
		switch(up->type){
		case TYPE_LOCAL_STREAM:
		case TYPE_LOCAL_DGRAM:
			break;	/* no op */
		case TYPE_UDP:
		case TYPE_RAW:
			autobind(s,AF_INET);
			break;
#ifdef	AX25
		case TYPE_AX25UI:
			autobind(s,AF_AX25);
			break;
#endif
#ifdef	NETROM
		case TYPE_NETROML3:
		case TYPE_NETROML4:
			autobind(s,AF_NETROM);
			break;
#endif
		default:
			free_p(bp);
			errno = ENOTCONN;
			return -1;
		}
	}
	cnt = len_p(bp);
	switch(up->type){
	case TYPE_LOCAL_DGRAM:
		enqueue(&up->cb.local->q,bp);
		psignal(up,0);
		/* If high water mark has been reached, block */
		while(len_q(up->cb.local->q) >= up->cb.local->hiwat){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		break;
	case TYPE_LOCAL_STREAM:
		append(&up->cb.local->q,bp);
		psignal(up,0);
		/* If high water mark has been reached, block */
		while(len_p(up->cb.local->q) >= up->cb.local->hiwat){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		break;
	case TYPE_TCP:
		if((cb.tcb = up->cb.tcb) == NULLTCB){
			free_p(bp);
			errno = ENOTCONN;
			return -1;
		}		
		cnt = send_tcp(cb.tcb,bp);

		while((cb.tcb = up->cb.tcb) != NULLTCB &&
		 cb.tcb->sndcnt > cb.tcb->window){
			/* Send queue is full */
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
 		if(cb.tcb == NULLTCB){
			errno = ENOTCONN;
			return -1;
		}
		break;
	case TYPE_UDP:
		local.in = (struct sockaddr_in *)up->name;
		lsock.address = local.in->sin_addr.s_addr;
		lsock.port = local.in->sin_port;
		if(to != NULLCHAR)
			remote.in = (struct sockaddr_in *)to;
		else if(up->peername != NULLCHAR)
			remote.in = (struct sockaddr_in *)up->peername;
		else {
			errno = ENOTCONN;
			return -1;
		}	
		fsock.address = remote.in->sin_addr.s_addr;
		fsock.port = remote.in->sin_port;
		send_udp(&lsock,&fsock,0,0,bp,0,0,0);
		break;
	case TYPE_RAW:
		local.in = (struct sockaddr_in *)up->name;
		if(to != NULLCHAR)
			remote.in = (struct sockaddr_in *)to;
		else if(up->peername != NULLCHAR)
			remote.in = (struct sockaddr_in *)up->peername;
		else {
			errno = ENOTCONN;
			return -1;
		}	
		ip_send(local.in->sin_addr.s_addr,remote.in->sin_addr.s_addr,
			(char)up->cb.rip->protocol,0,0,bp,0,0,0);
		break;
#ifdef	AX25
	case TYPE_AX25I:
		if((cb.ax25 = up->cb.ax25) == NULLAX25){
			free_p(bp);
			errno = ENOTCONN;
			return -1;
		}
		send_ax25(cb.ax25,bp,PID_NO_L3);

		while((cb.ax25 = up->cb.ax25) != NULLAX25 &&
		 len_q(cb.ax25->txq) * cb.ax25->paclen > cb.ax25->window){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				return -1;
			}
		}
		if(cb.ax25 == NULLAX25){
			errno = EBADF;
			return -1;
		}
		break;
	case TYPE_AX25UI:
		local.ax = (struct sockaddr_ax *)up->name;
		if(to != NULLCHAR)
			remote.ax = (struct sockaddr_ax *)to;
		else if(up->peername != NULLCHAR)
			remote.ax = (struct sockaddr_ax *)up->peername;
		else {
			errno = ENOTCONN;
			return -1;
		}
		ax_output(if_lookup(remote.ax->iface),
			  remote.ax->ax25_addr,
			  local.ax->ax25_addr,PID_NO_L3,bp);
		break;
#endif
#ifdef NETROM
	case TYPE_NETROML3:
		if(len_p(bp) > NR4MAXINFO) {
			free_p(bp);
			errno = EMSGSIZE;
			return -1;
		}
		local.nr = (struct sockaddr_nr *)up->name;
		if(to != NULLCHAR)
			remote.nr = (struct sockaddr_nr *)to;
		else if(up->peername != NULLCHAR)
			remote.nr = (struct sockaddr_nr *)up->peername;
		else {
			errno = ENOTCONN;
			return -1;
		}	
		/* The NETROM username is always ignored in outgoing traffic */
		nr_sendraw(remote.nr->nr_addr.node,
		 up->cb.rnr->protocol,up->cb.rnr->protocol,bp);
		break;
	case TYPE_NETROML4:
		if((cb.nr4 = up->cb.nr4) == NULLNR4CB) {
			free_p(bp);
			errno = ENOTCONN;
			return -1;
		}
		if(len_p(bp) > NR4MAXINFO){ /* reject big packets */
			free_p(bp);
			errno = EMSGSIZE;
			return -1;
		}
		send_nr4(cb.nr4,bp);

		while((cb.nr4 = up->cb.nr4) != NULLNR4CB &&
		 cb.nr4->nbuffered >= cb.nr4->window){
			if(up->noblock){
				errno = EWOULDBLOCK;
				return -1;
			} else if((errno = pwait(up)) != 0){
				errno = EINTR;
				return -1;
			}
		}
		if(cb.nr4 == NULLNR4CB){
			errno = EBADF;
			return -1;
		}
		break;
#endif
	}	
	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;
	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;
	}
	switch(up->type){
	case TYPE_LOCAL_DGRAM:
		len = len_q(up->cb.local->q);
		break;
	case TYPE_LOCAL_STREAM:
		len = len_p(up->cb.local->q)
		 + len_p(up->ibuf) + len_p(up->obuf);
		break;
	case TYPE_TCP:
		switch(rtx){
		case 0:
			len = up->cb.tcb->rcvcnt + len_p(up->ibuf);
			break;
		case 1:
			len = up->cb.tcb->sndcnt + len_p(up->obuf);
			break;
		}
		break;
	case TYPE_UDP:
		switch(rtx){
		case 0:
			len = up->cb.udp->rcvcnt;
			break;
		case 1:
			len = 0;
			break;
		}
		break;
#ifdef	AX25
	case TYPE_AX25I:
		switch(rtx){
		case 0:
			len = len_p(up->cb.ax25->rxq) + len_p(up->ibuf);
			break;
		case 1:	/* Number of packets, not bytes */
			len = len_q(up->cb.ax25->txq);
			if(up->obuf != NULLBUF)
				len++;
		}
		break;
	case TYPE_AX25UI:
		switch(rtx){	
		case 0:
			len = len_q(Bcq);
			break;
		case 1:
			len = 0;		
			break;
		}
		break;
#endif
#ifdef NETROM
	case TYPE_NETROML3:
		switch(rtx){	
		case 0:
			len = len_q(up->cb.rnr->rcvq);
			break;
		case 1:
			len = 0;		
		}
		break;
	case TYPE_NETROML4:
		switch(rtx){
		case 0:
			len = len_p(up->cb.nr4->rxq) + len_p(up->obuf);
			break;
		case 1:	/* Number of packets, not bytes */
			len = len_q(up->cb.nr4->txq);
			if(up->obuf != NULLBUF)
				len++;
			break;
		}
		break;
#endif
	case TYPE_RAW:
		switch(rtx){	
		case 0:
			len = len_q(up->cb.rip->rcvq);
			break;
		case 1:
			len = 0;		
			break;
		}
		break;
	}
	return len;
}
/* Force retransmission. Valid only for connection-oriented sockets. */
int
sockkick(s)
int s;	/* Socket index */
{
	register struct usock *up;

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){
		errno = EINVAL;
		return -1;
	}
	if(up->cb.p == NULLCHAR){
		errno = ENOTCONN;
		return -1;
	}
	switch(up->type){
	case TYPE_TCP:
		kick_tcp(up->cb.tcb);
		break;
#ifdef	AX25
	case TYPE_AX25I:
		kick_ax25(up->cb.ax25);
		break;
#endif
#ifdef NETROM
	case TYPE_NETROML4:
		kick_nr4(up->cb.nr4);
		break;
#endif
	default:
		/* Datagram sockets can't be kicked */
		errno = EOPNOTSUPP;
		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;

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->cb.p == NULLCHAR){
		errno = ENOTCONN;
		return -1;
	}
	switch(up->type){
	case TYPE_LOCAL_DGRAM:
	case TYPE_LOCAL_STREAM:
		if(up->cb.local->q == NULLBUF)
			close_s(s);
		else
			up->cb.local->flags = LOC_SHUTDOWN;
		break;
	case TYPE_TCP:
		switch(how){
		case 0:	/* No more receives -- replace upcall */
			up->cb.tcb->r_upcall = trdiscard;
			break;
		case 1:	/* Send EOF */
			close_tcp(up->cb.tcb);
			break;
		case 2:	/* Blow away TCB */
			reset_tcp(up->cb.tcb);
			up->cb.tcb = NULLTCB;
			break;
		}
		break;
#ifdef	AX25
	case TYPE_AX25UI:
		close_s(s);
		break;
	case TYPE_AX25I:
		switch(how){
		case 0:
		case 1:	/* Attempt regular disconnect */
			disc_ax25(up->cb.ax25);
			break;
		case 2: /* Blow it away */
			reset_ax25(up->cb.ax25);
			up->cb.ax25 = NULLAX25;
			break;
		}
		break;		
#endif
#ifdef	NETROM
	case TYPE_NETROML3:
		close_s(s);
		break;
	case TYPE_NETROML4:
		switch(how){
		case 0:
		case 1:	/* Attempt regular disconnect */
			disc_nr4(up->cb.nr4);
			break;
		case 2: /* Blow it away */
			reset_nr4(up->cb.nr4);
			up->cb.nr4 = NULLNR4CB;
			break;
		}
		break;		
#endif
	case TYPE_RAW:
	case TYPE_UDP:
		close_s(s);
		break;
	default:
		errno = EOPNOTSUPP;
		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;

	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;
	}
	switch(up->type){
	case TYPE_LOCAL_STREAM:
	case TYPE_LOCAL_DGRAM:
		free_q(&up->cb.local->q);
		free(up->cb.local);
		break;
	case TYPE_TCP:
		if(up->cb.tcb != NULLTCB){	/* In case it's been reset */
			up->cb.tcb->r_upcall = trdiscard;
			/* Tell the TCP_CLOSED upcall there's no more socket */
			up->cb.tcb->user = -1;
			close_tcp(up->cb.tcb);
		}
		break;
	case TYPE_UDP:
		if(up->cb.udp != NULLUDP){
			del_udp(up->cb.udp);
		}
		break;
#ifdef	AX25
	case TYPE_AX25I:
		if(up->cb.ax25 != NULLAX25){
			/* Tell the TCP_CLOSED upcall there's no more socket */
			up->cb.ax25->user = -1;
			disc_ax25(up->cb.ax25);
		}
		break;
	case TYPE_AX25UI:
		Axui_sock = -1;
		free_q(&Bcq);
		psignal(&Bcq,0);	/* Unblock any reads */
		break;
#endif
#ifdef	NETROM
	case TYPE_NETROML3:
		del_rnr(up->cb.rnr);
		break;
	case TYPE_NETROML4:
		if(up->cb.nr4 != NULLNR4CB){
			/* Tell the TCP_CLOSED upcall there's no more socket */
			up->cb.nr4->user = -1;
			disc_nr4(up->cb.nr4);
		}
		break;
#endif
	case TYPE_RAW:
		del_ip(up->cb.rip);
		break;
	default:
		errno = EOPNOTSUPP;
		return -1;
	}
	free(up->name);
	free(up->peername);

	up->cb.p = NULLCHAR;
	up->name = up->peername = NULLCHAR;
	up->type = NOTUSED;
	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=0;i < Nusock;i++){
		up = &Usock[i];
		if(up->type != NOTUSED && up->owner == pp)
			shutdown(i,2);
	}
}
/* Start of internal subroutines */

/* Raw IP receive upcall routine */
static void
rip_recv(rp)
struct raw_ip *rp;
{
	psignal(itop(rp->user),1);
	pwait(NULL);
}

/* UDP receive upcall routine */
static void
s_urcall(iface,udp,cnt)
struct iface *iface;
struct udp_cb *udp;
int cnt;
{
	psignal(itop(udp->user),1);
	pwait(NULL);
}
/* TCP receive upcall routine */
static void
s_trcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
	/* Wake up anybody waiting for data, and let them run */
	psignal(itop(tcb->user),1);
	pwait(NULL);
}
/* TCP transmit upcall routine */
static void
s_ttcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
	/* Wake up anybody waiting to send data, and let them run */
	psignal(itop(tcb->user),1);
	pwait(NULL);
}
/* TCP state change upcall routine */
static void
s_tscall(tcb,old,new)
struct tcb *tcb;
int old,new;
{
	int s,ns;
	struct usock *up,*nup,*oup;
	union sp sp;

	s = tcb->user;
	oup = up = itop(s);

	switch(new){
	case TCP_CLOSED:
		/* Clean up. If the user has already closed the socket,
		 * then up will be null (s was set to -1 by the close routine).
		 * If not, then this is an abnormal close (e.g., a reset)
		 * and clearing out the pointer in the socket structure will
		 * prevent any further operations on what will be a freed
		 * control block. Also wake up anybody waiting on events
		 * related to this tcb so they will notice it disappearing.
		 */
		if(up != NULLUSOCK){
			up->cb.tcb = NULLTCB;
			up->errcodes[0] = tcb->reason;
			up->errcodes[1] = tcb->type;
			up->errcodes[2] = tcb->code;
		}
		del_tcp(tcb);
		break;
	case TCP_SYN_RECEIVED:
		/* Handle an incoming connection. If this is a server TCB,
		 * then we're being handed a "clone" TCB and we need to
		 * create a new socket structure for it. In either case,
		 * find out who we're talking to and wake up the guy waiting
		 * for the connection.
		 */
		if(tcb->flags.clone){
			/* Clone the socket */
			ns = socket(AF_INET,SOCK_STREAM,0);
			nup = &Usock[ns];
			ASSIGN(*nup,*up);
			tcb->user = ns;
			nup->cb.tcb = tcb;
			/* Allocate new memory for the name areas */
			nup->name = mallocw(SOCKSIZE);
			nup->peername = mallocw(SOCKSIZE);
			/* Store the new socket # in the old one */
			up->rdysock = ns;
			up = nup;
			s = ns;
		} else {
			/* Allocate space for the peer's name */
			up->peername = mallocw(SOCKSIZE);
			/* Store the old socket # in the old socket */
			up->rdysock = s;
		}
		/* Load the addresses. Memory for the name has already
		 * been allocated, either above or in the original bind.
		 */
		sp.p = up->name;
		sp.in->sin_family = AF_INET;
		sp.in->sin_addr.s_addr = up->cb.tcb->conn.local.address;
		sp.in->sin_port = up->cb.tcb->conn.local.port;
		up->namelen = SOCKSIZE;

		sp.p = up->peername;
		sp.in->sin_family = AF_INET;
		sp.in->sin_addr.s_addr = up->cb.tcb->conn.remote.address;
		sp.in->sin_port = up->cb.tcb->conn.remote.port;
		up->peernamelen = SOCKSIZE;

		/* Wake up the guy accepting it, and let him run */
		psignal(oup,1);
		pwait(NULL);
		break;
	default:	/* Ignore all other state transitions */
		break;
	}
	psignal(up,0);	/* In case anybody's waiting */
}
/* Discard data received on a TCP connection. Used after a receive shutdown or
 * close_s until the TCB disappears.
 */
static void
trdiscard(tcb,cnt)
struct tcb *tcb;
int cnt;
{
	struct mbuf *bp;

	recv_tcp(tcb,&bp,(int16)cnt);
	free_p(bp);
}
#ifdef	AX25
/* AX.25 receive upcall */
void
s_arcall(axp,cnt)
struct ax25_cb *axp;
int cnt;
{
	int ns;
	struct usock *up,*nup,*oup;
	union sp sp;

	up = itop(axp->user);
	/* When AX.25 data arrives for the first time the AX.25 listener
	   is notified, if active. If the AX.25 listener is a server its
	   socket is duplicated in the same manner as in s_tscall().
	 */
	if (Axi_sock != -1 && axp->user == -1) {
		oup = up = itop(Axi_sock);
		/* From now on, use the same upcalls as the listener */
		axp->t_upcall = up->cb.ax25->t_upcall;
		axp->r_upcall = up->cb.ax25->r_upcall;
		axp->s_upcall = up->cb.ax25->s_upcall;
		if (up->cb.ax25->flags.clone) {
			/* Clone the socket */
			ns = socket(AF_AX25,SOCK_STREAM,0);
			nup = &Usock[ns];
			ASSIGN(*nup,*up);
			axp->user = ns;
			nup->cb.ax25 = axp;
			/* Allocate new memory for the name areas */
			nup->name = mallocw(sizeof(struct sockaddr_ax));
			nup->peername = mallocw(sizeof(struct sockaddr_ax));
			/* Store the new socket # in the old one */
			up->rdysock = ns;
			up = nup;
		} else {
			axp->user = Axi_sock;
			del_ax25(up->cb.ax25);
			up->cb.ax25 = axp;
			/* Allocate space for the peer's name */
			up->peername = mallocw(sizeof(struct sockaddr_ax));
			/* Store the old socket # in the old socket */
			up->rdysock = Axi_sock;
		}
		/* Load the addresses. Memory for the name has already
		 * been allocated, either above or in the original bind.
		 */
		sp.p = up->name;
		sp.ax->sax_family = AF_AX25;
		memcpy(sp.ax->ax25_addr,axp->local,AXALEN);
		memcpy(sp.ax->iface,axp->iface->name,ILEN);
		up->namelen = sizeof(struct sockaddr_ax);

		sp.p = up->peername;
		sp.ax->sax_family = AF_AX25;
		memcpy(sp.ax->ax25_addr,axp->remote,AXALEN);
		memcpy(sp.ax->iface,axp->iface->name,ILEN);
		up->peernamelen = sizeof(struct sockaddr_ax);
		/* Wake up the guy accepting it, and let him run */
		psignal(oup,1);
		pwait(NULL);
		return;
	}
	/* Wake up anyone waiting, and let them run */
	psignal(up,1);
	pwait(NULL);
}
/* AX.25 transmit upcall */
void
s_atcall(axp,cnt)
struct ax25_cb *axp;
int cnt;
{
	/* Wake up anyone waiting, and let them run */
	psignal(itop(axp->user),1);
	pwait(NULL);
}
/* AX25 state change upcall routine */
void
s_ascall(axp,old,new)
register struct ax25_cb *axp;
int old,new;
{
	int s;
	struct usock *up;

	s = axp->user;
	up = itop(s);

	switch(new){
	case LAPB_DISCONNECTED:
		/* Clean up. If the user has already closed the socket,
		 * then up will be null (s was set to -1 by the close routine).
		 * If not, then this is an abnormal close (e.g., a reset)
		 * and clearing out the pointer in the socket structure will
		 * prevent any further operations on what will be a freed
		 * control block. Also wake up anybody waiting on events
		 * related to this block so they will notice it disappearing.
		 */
		if(up != NULLUSOCK){
			up->errcodes[0] = axp->reason;
			up->cb.ax25 = NULLAX25;
		}
		del_ax25(axp);
		break;
	default:	/* Other transitions are ignored */
		break;
	}
	psignal(up,0);	/* In case anybody's waiting */
}
#endif
#ifdef NETROM
/* NET/ROM receive upcall routine */
static void
s_nrcall(cb,cnt)
struct nr4cb *cb;
int cnt;
{
	/* Wake up anybody waiting for data, and let them run */
	psignal(itop(cb->user),1);
	pwait(NULL);
}
/* NET/ROM transmit upcall routine */
static void
s_ntcall(cb,cnt)
struct nr4cb *cb;
int cnt;
{
	/* Wake up anybody waiting to send data, and let them run */
	psignal(itop(cb->user),1);
	pwait(NULL);
}
/* NET/ROM state change upcall routine */
static void
s_nscall(cb,old,new)
struct nr4cb *cb;
int old,new;
{
	int s,ns;
	struct usock *up,*nup,*oup;
	union sp sp;

	s = cb->user;
	oup = up = itop(s);

 	if(new == NR4STDISC && up != NULLUSOCK){
		/* Clean up. If the user has already closed the socket,
		 * then up will be null (s was set to -1 by the close routine).
		 * If not, then this is an abnormal close (e.g., a reset)
		 * and clearing out the pointer in the socket structure will
		 * prevent any further operations on what will be a freed
		 * control block. Also wake up anybody waiting on events
		 * related to this cb so they will notice it disappearing.
		 */
 		up->cb.nr4 = NULLNR4CB;
 		up->errcodes[0] = cb->dreason;
 	}
 	if(new == NR4STCON && old == NR4STDISC){
		/* Handle an incoming connection. If this is a server cb,
		 * then we're being handed a "clone" cb and we need to
		 * create a new socket structure for it. In either case,
		 * find out who we're talking to and wake up the guy waiting
		 * for the connection.
		 */
		if(cb->clone){
			/* Clone the socket */
			ns = socket(AF_NETROM,SOCK_SEQPACKET,0);
			nup = &Usock[ns];
			ASSIGN(*nup,*up);
			cb->user = ns;
			nup->cb.nr4 = cb;
			cb->clone = 0; /* to avoid getting here again */
			/* Allocate new memory for the name areas */
			nup->name = mallocw(sizeof(struct sockaddr_nr));
			nup->peername = mallocw(sizeof(struct sockaddr_nr));
			/* Store the new socket # in the old one */
			up->rdysock = ns;
			up = nup;
			s = ns;
		} else {
			/* Allocate space for the peer's name */
			up->peername = mallocw(sizeof(struct sockaddr_nr));
			/* Store the old socket # in the old socket */
			up->rdysock = s;
		}
		/* Load the addresses. Memory for the name has already
		 * been allocated, either above or in the original bind.
		 */
		sp.p = up->name;
		sp.nr->nr_family = AF_NETROM;
		ASSIGN(sp.nr->nr_addr,up->cb.nr4->local);
		up->namelen = sizeof(struct sockaddr_nr);

		sp.p = up->peername;
		sp.nr->nr_family = AF_NETROM;
		ASSIGN(sp.nr->nr_addr,up->cb.nr4->remote);
		up->peernamelen = sizeof(struct sockaddr_nr);

		/* Wake up the guy accepting it, and let him run */
		psignal(oup,1);
		pwait(NULL);
	}
 	/* Ignore all other state transitions */	
	psignal(up,0);	/* In case anybody's waiting */
}
#endif

/* Verify address family and length according to the socket type */
static int
checkaddr(type,name,namelen)
int type;
char *name;
int namelen;
{
	register struct sockaddr *sp;

	sp = (struct sockaddr *)name;
	/* Verify length and address family according to protocol */
	switch(type){
	case TYPE_TCP:
	case TYPE_UDP:
		if(sp->sa_family != AF_INET
		 || namelen != sizeof(struct sockaddr_in))
			return -1;
		break;
#ifdef	AX25
	case TYPE_AX25I:
	case TYPE_AX25UI:
		if(sp->sa_family != AF_AX25
		 || namelen != sizeof(struct sockaddr_ax))
			return -1;
		break;
#endif
#ifdef	NETROM
	case TYPE_NETROML3:
	case TYPE_NETROML4:
		if(sp->sa_family != AF_NETROM
		 || namelen != sizeof(struct sockaddr_nr))
			return -1;
		break;
#endif
	}
	return 0;
}
/* Issue an automatic bind of a local address */
static void
autobind(s,af)
int s,af;
{
	char buf[MAXSOCKSIZE];
	union sp sp;

	sp.p = buf;
	switch(af){
	case AF_INET:
		sp.in->sin_family = AF_INET;
		sp.in->sin_addr.s_addr = INADDR_ANY;
		sp.in->sin_port = Lport++;
		bind(s,sp.p,sizeof(struct sockaddr_in));
		break;
#ifdef	AX25
	case AF_AX25:
		sp.ax->sax_family = AF_AX25;
		sp.ax->ax25_addr[0] = '\0';	/* Unspecified */
		bind(s,sp.p,sizeof(struct sockaddr_ax));
		break;
#endif
#ifdef	NETROM
	case AF_NETROM:
		sp.nr->nr_family = AF_NETROM;
		memcpy(sp.nr->nr_addr.user,Mycall,AXALEN);
		memcpy(sp.nr->nr_addr.node,Mycall,AXALEN);
		bind(s,sp.p,sizeof(struct sockaddr_nr));
		break;
#endif
	}
}

