[comp.sources.amiga] v89i081: amigatcp - tcp/ip for the amiga, Part02/06

page@swan.ulowell.edu (Bob Page) (03/18/89)

Submitted-by: rminnich@super.org (Ronald G. Minnich)
Posting-number: Volume 89, Issue 81
Archive-name: comm/amigatcp.2

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	icmp.c
#	mbuf.c
#	slip.c
#	smtpcli.c
#	tcpout.c
#	tcpuser.c
#	telnet.c
# This archive created: Fri Mar 17 17:57:17 1989
cat << \SHAR_EOF > icmp.c
/* Internet Control Message Protocol */

#include "machdep.h"
#include "internet.h"
#include "timer.h"
#include "ip.h"
#include "icmp.h"
#include "mbuf.h"

int (*echo_proc)();	/* Handler for Echo Reply messages */

struct icmp_errors icmp_errors;
struct icmp_stats icmp_stats;

/* Process an incoming ICMP packet */
void
icmp_input(bp,protocol,source,dest,tos,length,rxbroadcast)
struct mbuf *bp;	/* Pointer to ICMP message */
char protocol;		/* Should always be ICMP_PTCL */
int32 source;		/* Sender of ICMP message */
int32 dest;		/* Us */
char tos;		/* Type of Service */
int16 length;		/* Length of ICMP message */
char rxbroadcast;
{

	struct icmp *icmph;	/* Pointer to ICMP message */
	struct ip_header *iph;	/* Offending datagram header */
	int16 type;		/* Type of ICMP message */
	int16 ip_len;

	if(rxbroadcast){
		/* Broadcast ICMP packets are to be IGNORED !! */
		icmp_errors.bdcsts++;
		free_p(bp);
		return;
	}
	if(cksum(NULLHEADER,bp,length) != 0){
		/* Bad ICMP checksum; discard */
		icmp_errors.checksum++;
		free_p(bp);
		return;
	}
	/* If the message is fragmented, copy to a contiguous mbuf */
	if(bp->next != NULLBUF){
		struct mbuf *nbp;

		nbp = copy_p(bp,length);
		free_p(bp);
		if(nbp == NULLBUF){
			icmp_errors.nospace++;
			return;
		}
		bp = nbp;
	}
	icmph = (struct icmp *)bp->data;

	/* Process the message. Some messages are passed up to the protocol
	 * module for handling, others are handled here.
	 */
	type = icmph->type & 0xff;
	if(type < ICMP_TYPES)
		icmp_stats.input[type]++;

	switch(type){
	case TIME_EXCEED:	/* Time-to-live Exceeded */
	case DEST_UNREACH:	/* Destination Unreachable */
	case QUENCH:		/* Source Quench */
		iph = (struct ip_header *)(icmph + 1);
		ip_len = (iph->v_ihl & 0xf) * sizeof(int32);

		switch(iph->protocol){
		case TCP_PTCL:
			tcp_icmp(ntohl(iph->source),ntohl(iph->dest),
				icmph->type,icmph->code,(char *)iph + ip_len);
			break;
		}
		break;
	case ECHO:		/* Echo Request */
		/* Change type to ECHO_REPLY, recompute checksum,
		 * and return datagram.
		 */
		icmph->type = ECHO_REPLY;
		icmph->checksum = 0;
		icmph->checksum = cksum(NULLHEADER,bp,length);
		icmp_stats.output[ECHO_REPLY]++;
		ip_send(dest,source,ICMP_PTCL,tos,0,bp,length,0,0);
		return;
	case REDIRECT:		/* Redirect */
	case PARAM_PROB:	/* Parameter Problem */
		break;
       case ECHO_REPLY:		/* Echo Reply */
		if(echo_proc){
			(*echo_proc)(ntohl(iph->source),ntohl(iph->dest),
			 icmph->type,icmph->code,(char *)iph + ip_len);
		}
		break;
	case TIMESTAMP:		/* Timestamp */
	case TIME_REPLY:	/* Timestamp Reply */
	case INFO_RQST:		/* Information Request */
	case INFO_REPLY:	/* Information Reply */
		break;
	}
	free_p(bp);
}
/* Return an ICMP response to the sender of a datagram */
icmp_output(bp,type,code,args)
struct mbuf *bp;		/* Pointer to offending IP header + data */
char type,code;			/* Codes to send */
union icmp_args *args;
{
	struct ip_header *iph;	/* Offending IP header */
	int16 ip_len;		/* Length of offending IP header */

	struct mbuf *reply;	/* Buffer with ICMP reply */
	struct icmp *icmph;	/* ICMP protocol header */
	struct mbuf *data;	/* Returned portion of offending packet */
	int16 dlen;		/* Length of data portion of offending pkt */
	int16 length;		/* Total length of reply */
	extern int32 ip_addr;	/* Our IP address */

	if(type < ICMP_TYPES)
		icmp_stats.output[type]++;

	iph = (struct ip_header *)bp->data;

	if(iph->protocol == ICMP_PTCL){
		icmp_errors.noloop++;
		return;	/* Never send an ICMP message about another ICMP message */
	}
	/* Compute amount of original datagram to return.
	 * We return the original IP header, and up to 8 bytes past that.
	 */
	ip_len = (iph->v_ihl & 0xf) * sizeof(int32);
	dlen = ntohs(iph->length);
	if(dlen > ip_len + 8)
		dlen = ip_len + 8;
	length = sizeof(struct icmp) + dlen;

	/* Allocate ICMP header and fill in */
	if((reply = alloc_mbuf(sizeof(struct icmp))) == NULLBUF){
		/* No space; don't bother */
		icmp_errors.nospace++;
		return;
	}
	reply->cnt = sizeof(struct icmp);
	icmph = (struct icmp *)reply->data;
	icmph->type = type;
	icmph->code = code;
	if(args != (union icmp_args *)NULL)
		icmph->args.unused = args->unused;	/* copies whole union */
	else
		icmph->args.unused = 0;

	/* Link in a copy of the beginning of the original datagram */
	data = copy_p(bp,dlen);
	reply->next = data;	/* Could be NULL if copy fails */

	/* Compute ICMP checksum and send */
	icmph->checksum = 0;
	icmph->checksum = cksum(NULLHEADER,reply,length);

	ip_send(ip_addr,ntohl(iph->source),ICMP_PTCL,iph->tos,0,reply,length,0,0);
}
#ifdef	TRACE
/* ICMP message types */
char *icmptypes[] = {
	"Echo Reply",
	NULLCHAR,
	NULLCHAR,
	"Unreachable",
	"Source Quench",
	"Redirect",
	NULLCHAR,
	NULLCHAR,
	"Echo Request",
	NULLCHAR,
	NULLCHAR,
	"Time Exceeded",
	"Parameter Problem",
	"Timestamp",
	"Timestamp Reply",
	"Information Request",
	"Information Reply"
};

/* ICMP unreachable messages */
char *unreach[] = {
	"Network",
	"Host",
	"Protocol",
	"Port",
	"Fragmentation",
	"Source route"
};
/* ICMP Time exceeded messages */
char *exceed[] = {
	"Time-to-live",
	"Fragment reassembly"
};

/* ICMP redirect messages */
char *redirect[] = {
	"Network",
	"Host",
	"TOS & Network",
	"TOS & Host"
};

int
doicmpstat(argc,argv)
int argc;
char *argv[];
{
	extern struct icmp_errors icmp_errors;
	extern struct icmp_stats icmp_stats;
	register int i;

	printf("chksum err %u no space %u icmp %u bdcsts %u\r\n",
	 icmp_errors.checksum,icmp_errors.nospace,icmp_errors.noloop,
	 icmp_errors.bdcsts);
	printf("type  rcvd  sent\r\n");
	for(i=0;i<ICMP_TYPES;i++){
		if(icmp_stats.input[i] == 0 && icmp_stats.output[i] == 0)
			continue;
		printf("%-6u%-6u%-6u",i,icmp_stats.input[i],
			icmp_stats.output[i]);
		if(icmptypes[i] != NULLCHAR)
			printf("  %s",icmptypes[i]);
		printf("\r\n");
	}
	return 0;
}
/* Dump an ICMP header */
void
icmp_dump(bp,source,dest,check)
struct mbuf *bp;
int32 source,dest;
int check;		/* If 0, bypass checksum verify */
{
	register struct icmp *icmp;
	char *codemsg;
	struct mbuf *ibp;
	int i;
	char tmpbuf;

	if(bp == NULLBUF)
		return;
	/* If packet isn't in a single buffer, make a temporary copy and
	 * note the fact so we free it later
	 */
	if(bp->next != NULLBUF){
		bp = copy_p(bp,len_mbuf(bp));
		tmpbuf = 1;
	} else
		tmpbuf = 0;

	codemsg = NULLCHAR;
	icmp = (struct icmp *)bp->data;
	if(icmp->type <= 16 && icmptypes[icmp->type] != NULLCHAR)
		printf("ICMP: %s",icmptypes[icmp->type]);
	else
		printf("ICMP: type %u",icmp->type);

	switch(icmp->type){
	case DEST_UNREACH:
		if(icmp->code <= 5)
			codemsg = unreach[icmp->code];
		break;
	case REDIRECT:
		if(icmp->code <= 3)
			codemsg = redirect[icmp->code];
		break;
	case TIME_EXCEED:
		if(icmp->code <= 1)
			codemsg = exceed[icmp->code];
		break;
	}	
	if(codemsg != NULLCHAR)
		printf(" %s",codemsg);
	else
		printf(" code %u",icmp->code);

	/* Special case for parameter problem message */
	if(icmp->type == PARAM_PROB)
		printf(" pointer = 0x%x",icmp->args.pointer);

	if(check){
		/* Verify checksum */
		if((i = cksum(NULLHEADER,bp,len_mbuf(bp))) != 0)
			printf(" CHECKSUM ERROR (%u)",i);
	}
	printf("\r\n");
	/* Dump the offending IP header, if any */
	switch(icmp->type){
	case DEST_UNREACH:
	case TIME_EXCEED:
	case PARAM_PROB:
	case QUENCH:
	case REDIRECT:
		printf("Returned ");
		dup_p(&ibp,bp,sizeof(struct icmp),
			len_mbuf(bp) - sizeof(struct icmp));
		ip_dump(ibp);
		free_p(ibp);
	}
	if(tmpbuf)
		free_p(bp);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > mbuf.c
/* Primitive mbuf allocate/free routines */

#ifdef TRACE
#include <stdio.h>
#endif

#include "machdep.h"
#include "mbuf.h"

/* Allocate mbuf with associated buffer of 'size' bytes */
struct mbuf *
alloc_mbuf(size)
register int16 size;
{
	register struct mbuf *bp;
	char *malloc();

	if((bp = (struct mbuf *)malloc((unsigned)(size + sizeof(struct mbuf)))) == NULLBUF)
		return NULLBUF;
	bp->next = bp->anext = NULLBUF;
	if(size != 0){
		bp->data = (char *)(bp + 1);
	} else {
		bp->data = NULLCHAR;
	}
	bp->cnt = 0;
	return bp;
}

/* Free all resources associated with mbuf
 * Return pointer to next mbuf in packet chain
 */
struct mbuf *
free_mbuf(bp)
register struct mbuf *bp;
{
	register struct mbuf *bp1 = NULLBUF;

	if(bp != NULLBUF){
		bp1 = bp->next;
		free((char *)bp);
	}
	return bp1;
}

/* Free packet (a chain of mbufs). Return pointer to next packet on queue,
 * if any
 */
struct mbuf *
free_p(bp)
register struct mbuf *bp;
{
	struct mbuf *abp;

	if(bp == NULLBUF)
		return NULLBUF;
	abp = bp->anext;
	while(bp != NULLBUF)
		bp = free_mbuf(bp);
	return abp;
}		
/* Free entire queue of packets (of mbufs) */
free_q(q)
struct mbuf **q;
{
	register struct mbuf *bp;

	while((bp = dequeue(q)) != NULLBUF)
		free_p(bp);
}

/* Count up the total number of bytes in an mbuf */
int16
len_mbuf(bp)
register struct mbuf *bp;
{
	int cnt;

	cnt = 0;
	while(bp != NULLBUF){
		cnt += bp->cnt;
		bp = bp->next;
	}
	return cnt;
}
/* Count up the number of packets in a queue */
int16
len_q(bp)
register struct mbuf *bp;
{
	register int cnt;

	for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext)
		;
	return cnt;
}
/* Duplicate/enqueue/dequeue operations based on mbufs */

/* Duplicate first 'cnt' bytes of packet starting at 'offset'.
 * This is done without copying data; only the headers are duplicated,
 * but without data segments of their own. The pointers are set up to
 * share the data segments of the original copy. The return pointer is
 * passed back through the first argument, and the return value is the
 * number of bytes actually duplicated.
 */
int16
dup_p(hp,bp,offset,cnt)
struct mbuf **hp;
register struct mbuf *bp;
register int16 offset;
register int16 cnt;
{
	register struct mbuf *cp;
	int16 tot;

	if(cnt == 0 || bp == NULLBUF || hp == (struct mbuf **)NULL){
		if(hp != (struct mbuf **)NULL)
			*hp = NULLBUF;
		return 0;
	}
	if((*hp = cp = alloc_mbuf(0)) == NULLBUF){
		return 0;
	}
	/* Skip over leading mbufs that are smaller than the offset */
	while(bp != NULLBUF && bp->cnt <= offset){
		offset -= bp->cnt;
		bp = bp->next;
	}
	if(bp == NULLBUF){
		free_mbuf(cp);
		*hp = NULLBUF;
		return 0;	/* Offset was too big */
	}
	tot = 0;
	for(;;){
		cp->data = bp->data + offset;
		cp->cnt = min(cnt,bp->cnt - offset);
		offset = 0;
		cnt -= cp->cnt;
		tot += cp->cnt;
		bp = bp->next;
		if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF)
			break;
		cp = cp->next;
	}
	return tot;
}
/* Copy first 'cnt' bytes of packet into a new, single mbuf */
struct mbuf *
copy_p(bp,cnt)
register struct mbuf *bp;
register int16 cnt;
{
	register struct mbuf *cp;
	register char *wp;
	register int16 n;

	if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF)
		return NULLBUF;
	wp = cp->data;
	while(cnt != 0 && bp != NULLBUF){
		n = min(cnt,bp->cnt);
		bcopy(bp->data,wp,n);
		wp += n;
		cp->cnt += n;
		cnt -= n;
		bp = bp->next;
	}
	return cp;
}
/* Copy and delete "cnt" bytes from beginning of packet. Return number of
 * bytes actually pulled off
 */
int16
pullup(bph,buf,cnt)
struct mbuf **bph;
char *buf;
int16 cnt;
{
	register struct mbuf *bp;
	int16 n,tot;

	tot = 0;
	if(bph == (struct mbuf **)NULL)
		return 0;
	while(*bph != NULLBUF && cnt != 0){
		bp = *bph;
		n = min(cnt,bp->cnt);
		if(buf != NULLCHAR){
			bcopy(bp->data,buf,n);
			buf += n;
		}
		tot += n;
		cnt -= n;
		bp->data += n;
		bp->cnt -= n;		
		if(bp->cnt == 0){
			*bph = free_mbuf(bp);
		}
	}
	return tot;
}
/* Append mbuf to end of mbuf chain */
int16
append(bph,bp)
struct mbuf **bph;
struct mbuf *bp;
{
	register struct mbuf *p;

	if(bph == (struct mbuf **)NULL || bp == NULLBUF)
		return;
	if(*bph == NULLBUF){
		/* First one on chain */
		*bph = bp;
	} else {
		for(p = *bph ; p->next != NULLBUF ; p = p->next)
			;
		p->next = bp;
	}
}
/* Append packet to end of packet queue */
void
enqueue(q,bp)
struct mbuf **q;
struct mbuf *bp;
{
	register struct mbuf *p;
	char i_state;

	if(q == (struct mbuf **)NULL || bp == NULLBUF)
		return;
	i_state = disable();
	if(*q == NULLBUF){
		/* List is empty, stick at front */
		*q = bp;
	} else {
		for(p = *q ; p->anext != NULLBUF ; p = p->anext)
			;
		p->anext = bp;
	}
	restore(i_state);
}
/* Unlink a packet from the head of the queue */
struct mbuf *
dequeue(q)
register struct mbuf **q;
{
	register struct mbuf *bp;
	char i_state;

	if(q == (struct mbuf **)NULL)
		return NULLBUF;
	i_state = disable();
	if((bp = *q) != NULLBUF){
		*q = bp->anext;
		bp->anext = NULLBUF;
	}
	restore(i_state);
	return bp;
}	

/* Copy user data into an mbuf */
struct mbuf *
qdata(data,cnt)
char *data;
int16 cnt;
{
	register struct mbuf *bp;

	if((bp = alloc_mbuf(cnt)) == NULLBUF)
		return NULLBUF;
	bcopy(data,bp->data,cnt);
	bp->cnt = cnt;
	return bp;
}
/* Copy mbuf data into user buffer */
int16
dqdata(bp,buf,cnt)
struct mbuf *bp;
char *buf;
unsigned cnt;
{
	unsigned n,tot;
	struct mbuf *bp1;

	if(buf == NULLCHAR)
		return 0;
	
	tot = 0;
	for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){
		n = min(bp1->cnt,cnt);
		bcopy(bp1->data,buf,n);
		cnt -= n;
		buf += n;
		tot += n;
	}
	free_p(bp);
	return tot;
}
#ifdef	TRACE
/* Comment this out if your library already has this function */
#define	isprint(c)	((c) >= ' ' && (c) < 0x7f)		/* Assumes ASCII */

/* Dump an mbuf in hex */
void
hexdump(bp)
struct mbuf *bp;
{
	register struct mbuf *tbp;
	int16 n;
	int16 address;
	void fmtline();

	if(bp == NULLBUF)
		return;
	tbp = copy_p(bp,len_mbuf(bp));
	address = 0;
	while(tbp->cnt != 0){
		n = min(tbp->cnt,16);
		fmtline(address,tbp->data,n);
		address += n;
		tbp->data += n;
		tbp->cnt -= n;
	}
	free_p(tbp);
	fflush(stdout);
}
/* Dump an mbuf in ascii */
void
asciidump(bp)
struct mbuf *bp;
{
	struct mbuf *tbp;
	char c;
	int16 tot;

	if(bp == NULLBUF)
		return;
	tbp = copy_p(bp,len_mbuf(bp));
	tot = 0;
	while(pullup(&tbp,&c,1) == 1){
		if((tot % 64) == 0)
			printf("%04x  ",tot);
		if(isprint(c))
			putchar(c);
		else
			putchar('.');
		tot++;
		if((tot % 64) == 0)
			printf("\r\n");
	}
	if((tot % 64) != 0)
		printf("\r\n");
	free_p(tbp);
	fflush(stdout);
}
/* Print a buffer up to 16 bytes long in formatted hex with ascii
 * translation, e.g.,
 * 0000: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  0123456789:;<=>?
 */
void
fmtline(addr,buf,len)
int16 addr;
char *buf;
int16 len;
{
	char line[80];
	char *aptr,*cptr;
	int16 c;
	void ctohex();

	for(cptr = line;cptr < &line[80];cptr++)
		*cptr = ' ';
	ctohex(line,(int16)hibyte(addr));
	ctohex(line+2,(int16)lobyte(addr));
	aptr = &line[6];
	cptr = &line[55];
	while(len-- != 0){
		c = *buf++ & 0xff;
		ctohex(aptr,c);
		aptr += 3;
		c &= 0x7f;
		if(isprint(c)){
			*cptr++ = c;
		} else {
			*cptr++ = '.';
		}
	}
	*cptr++ = '\r';
	*cptr++ = '\n';
#ifdef	AMIGA
	*cptr = '\0';
	printf(line);
#else
	fwrite(line,1,(unsigned)(cptr-line),stdout);
#endif
}
/* Convert byte to two ascii-hex characters */
static
void
ctohex(buf,c)
char *buf;
int16 c;
{
	static char hex[] = "0123456789abcdef";

	buf[0] = hex[hinibble(c)];
	buf[1] = hex[lonibble(c)];
}
#endif
SHAR_EOF
cat << \SHAR_EOF > slip.c
/* Send and receive IP datagrams on serial lines. Compatible with SLIP
 * under Berkeley Unix.
 */
#ifdef	TRACE
#include <stdio.h>
#endif
#include "machdep.h"
#ifdef	TRACE
#include "trace.h"
#endif
#include "mbuf.h"
#include "iface.h"
#include "ax25.h"
#include "slip.h"
#ifdef	AMIGA
#include "amiga.h"
#else
#include "pc.h"
#endif

int slip_send();
int doslip();
int asy_output();

/* Slip level control structure */
struct slip slip[ASY_MAX];
unsigned nasy;

/* Send routine for point-to-point slip
 * This is a trivial function since there is no slip link-level header
 */
int
slip_send(bp,interface,gateway,precedence,delay,throughput,reliability)
struct mbuf *bp;		/* Buffer to send */
struct interface *interface;	/* Pointer to interface control block */
int32 gateway;			/* Ignored (SLIP is point-to-point) */
char precedence;
char delay;
char throughput;
char reliability;
{
	/* Queue a frame on the slip output queue and start transmitter */

	if(interface == NULLIF){
		free_p(bp);
		return;
	}
#ifdef	TRACE
	if(trace & TRACE_SLIP){
		printf("%s sent:\r\n",interface->name);
		if((trace & TRACE_HDR) > 2)
			ip_dump(bp);
		if(trace & TRACE_DUMP)
			hexdump(bp);
		if(trace & TRACE_ASCII)
			asciidump(bp);
		fflush(stdout);
	}
#endif
	slipq(interface->dev,bp);
}
/* Encode a packet in slip framing, put on link output queue, and kick
 * transmitter
 */
slipq(dev,bp)
int16 dev;		/* Serial line number */
struct mbuf *bp;	/* Buffer to be sent */
{
	register struct slip *sp;
	struct mbuf *slip_encode();

	if((bp = slip_encode(bp)) == NULLBUF)
		return;	

	sp = &slip[dev];
	enqueue(&sp->sndq,bp);
	sp->sndcnt++;
	if(sp->tbp == NULLBUF)
		asy_start(dev);
}
/* Start output, if possible, on asynch device dev */
static
asy_start(dev)
int16 dev;
{
	register struct slip *sp;

	if(!stxrdy(dev))
		return;		/* Transmitter not ready */

	sp = &slip[dev];
	if(sp->tbp != NULLBUF){
		/* transmission just completed */
		free_p(sp->tbp);
		sp->tbp = NULLBUF;
	}
	if(sp->sndq == NULLBUF)
		return;	/* No work */

	sp->tbp = dequeue(&sp->sndq);
	sp->sndcnt--;
	asy_output(dev,sp->tbp->data,sp->tbp->cnt);
}
/* Encode a packet in SLIP format */
static
struct mbuf *
slip_encode(bp)
struct mbuf *bp;
{
	struct mbuf *lbp;	/* Mbuf containing line-ready packet */
	register char *cp;
	register int cnt;
	char c;

	/* Allocate output mbuf that's twice as long as the packet.
	 * This is a worst-case guess (consider a packet full of FR_ENDs!)
	 */
	lbp = alloc_mbuf(2*len_mbuf(bp) + 2);
	if(lbp == NULLBUF){
		/* No space; drop */
		free_p(bp);
		return NULLBUF;
	}
	cp = lbp->data;
	cnt = 0;

	/* Flush out any line garbage */
	*cp++ = FR_END;
	cnt++;

	/* Copy input to output, escaping special characters */
	while(pullup(&bp,&c,1) == 1){
		switch(c & 0xff){
		case FR_ESC:
			*cp++ = FR_ESC;
			*cp++ = T_FR_ESC;
			cnt += 2;
			break;
		case FR_END:
			*cp++ = FR_ESC;
			*cp++ = T_FR_END;
			cnt += 2;
			break;
		default:
			*cp++ = c;
			cnt++;
		}
	}
	*cp++ = FR_END;
	cnt++;
	lbp->cnt = cnt;
	return lbp;
}
/* Process incoming bytes in SLIP format
 * When a buffer is complete, return it; otherwise NULLBUF
 */
static
struct mbuf *
slip_decode(dev,c)
int dev;	/* Slip unit number */
char c;		/* Incoming character */
{
	struct mbuf *bp,*nbp;
	register struct slip *sp;

	sp = &slip[dev];
	switch(c & 0xff){
	case FR_END:
		if(sp->rbp != NULLBUF){
			/* Kick upstairs */
			bp = sp->rbp;
			sp->rbp = NULLBUF;
			sp->rcnt = 0;
			/* Copy into contiguous buffer, if necessary */
			if(bp->next != NULLBUF){
				nbp = copy_p(bp,len_mbuf(bp));
				free_p(bp);
				bp = nbp;
			}
			return bp;
		}
		return NULLBUF;
	case FR_ESC:
		sp->escaped = 1;
		return NULLBUF;
	}
	if(sp->escaped){
		sp->escaped = 0;
		switch(c & 0xff){
		case T_FR_ESC:
			c = FR_ESC;
			break;
		case T_FR_END:
			c = FR_END;
			break;
		default:
			sp->errors++;
		}
	}
	if(sp->rcnt == SLIP_MTU){
		/* Packet is too large, drop it and start over */
		free_p(sp->rbp);
		sp->rbp = NULLBUF;
		sp->rcnt = 0;
		return NULLBUF;
	}
	/* We reach here with a character for the buffer;
	 * make sure there's space for it
	 */
	if(sp->rbp == NULLBUF){
		/* Allocate first mbuf for new packet */
		if((sp->rbp1 = sp->rbp = alloc_mbuf(SLIP_ALLOC)) == NULLBUF)
			return NULLBUF; /* No memory, drop */
		sp->rcp = sp->rbp->data;
	} else if(sp->rbp1->cnt == SLIP_ALLOC){
		/* Current mbuf is full; link in another */
		if((sp->rbp1->next = alloc_mbuf(SLIP_ALLOC)) == NULLBUF){
			/* No memory, drop whole thing */
			free_p(sp->rbp);
			sp->rbp = NULLBUF;
			sp->rcnt = 0;
			return NULLBUF;
		}
		sp->rbp1 = sp->rbp1->next;
		sp->rcp = sp->rbp1->data;
	}
	/* Store the character, increment fragment and total
	 * byte counts
	 */
	*sp->rcp++ = c;
	sp->rbp1->cnt++;
	sp->rcnt++;
	return NULLBUF;
}
/* Process SLIP line I/O */
int
doslip(interface)
struct interface *interface;
{
	char c;
	struct mbuf *bp;
	int16 dev;

	dev = interface->dev;
	/* Process any pending input */
	while(asy_recv(dev,&c,1) != 0)
		if((bp = slip_decode(dev,c)) != NULLBUF)
			(*slip[dev].recv)(interface,bp);

	/* Kick the transmitter if it's idle */
	if(stxrdy(dev))
		asy_start(dev);
}
/* Unwrap incoming SLIP packets -- trivial operation since there's no
 * link level header
 */
slip_recv(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
#ifdef	TRACE
	if(trace & TRACE_SLIP){
		printf("%s recv:\r\n",interface->name);
		if((trace & TRACE_HDR) > 2)
			ip_dump(bp);
		if(trace & TRACE_DUMP)
			hexdump(bp);
		if(trace & TRACE_ASCII)
			asciidump(bp);
		fflush(stdout);
	}
#endif
	ip_route(bp,0);
} 
/* Attach a serial interface to the system
 * argv[0]: hardware type, must be "asy"
 * argv[1]: I/O address, e.g., "0x3f8"
 * argv[2]: vector, e.g., "4"
 * argv[3]: mode, may be:
 *	    "slip" (point-to-point SLIP)
 *	    "ax25" (AX.25 UI frame format in SLIP for raw TNC)
 * argv[4]: interface label, e.g., "sl0"
 * argv[5]: receiver ring buffer size in bytes
 * argv[6]: maximum transmission unit, bytes
 * argv[7]: interface speed, e.g, "9600"
 */
asy_attach(argc,argv)
int argc;
char *argv[];
{
	register struct interface *if_asy;
	extern struct interface *ifaces;
	int dev;
	char *malloc(), *calloc();
	int asy_init();
	int asy_send();
	int doslip();
	int asy_stop();
	int ax_send();
	int kiss_recv();
	int kiss_output();

	if(nasy >= ASY_MAX){
		printf("Too many asynch controllers\r\n");
		return -1;
	}
	dev = nasy++;

	/* Initialize hardware-level control structure */
	asy[dev].addr = htoi(argv[1]);
	asy[dev].vec = htoi(argv[2]);

	/* Create interface structure and fill in details */
	if_asy = (struct interface *)calloc(1, sizeof(struct interface));

	if_asy->name = malloc(strlen(argv[4])+1);
	strcpy(if_asy->name,argv[4]);
	if_asy->mtu = atoi(argv[6]);
	if_asy->dev = dev;
	if_asy->recv = doslip;
	if_asy->stop = asy_stop;

	if(strcmp(argv[3],"slip") == 0){
		if_asy->send = slip_send;
		if_asy->output = NULLFP;
		if_asy->flags = 0;
		slip[dev].recv = slip_recv;
	}
	else if(strcmp(argv[3],"ax25") == 0){
		if_asy->send = ax_send;
		if_asy->output = kiss_output;
		if_asy->flags = IF_BROADCAST;
		if(if_asy->hwaddr == NULLCHAR)
			if_asy->hwaddr = malloc(AXALEN);
		bcopy((char *)&mycall,if_asy->hwaddr,AXALEN);
		slip[dev].recv = kiss_recv;
	}
	else {
		printf("Mode %s unknown for interface %s\r\n",
			argv[3],argv[4]);
		free((char *)if_asy);
		return -1;
	}
	if_asy->next = ifaces;
	ifaces = if_asy;
	asy_init(dev,(unsigned)atoi(argv[5]));
	asy_speed(dev,atoi(argv[7]));
}
SHAR_EOF
cat << \SHAR_EOF > smtpcli.c
/* smtpcli.c
 *	Client routines for Simple Mail Transfer Protocol ala RFC821
 *	A.D. Barksdale Garbee II, aka Bdale, N3EUA
 *	Copyright 1986 Bdale Garbee, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 */

#include <stdio.h>
#include "machdep.h"
#include "netuser.h"
#include "mbuf.h"
#include "timer.h"
#include "tcp.h"
#include "smtp.h"

extern int16 lport;			/* local port placeholder */
int32 aton();
static void sendit();
static struct timer smtpcli_t;
char *index(),*rindex();

/* init routine called when program fired up */
smtpclinit()
{
	int dosmtptick();

	smtpcli_t.func = (void (*)())dosmtptick;/* what to call on timeout */
	smtpcli_t.arg = 0;			/* dummy value */
	smtpcli_t.start = SMTPCLITIME;		/* set timer duration */
	start_timer(&smtpcli_t);		/* and fire it up */
}

/* this is the routine that gets called every so often to do outgoing mail
   processing */
int
dosmtptick()
{
	char 	lfilename[LINELEN],
		tmpstring[LINELEN],
		wfilename[13],
		*ptr;
	FILE *lfile;
	struct smtp_msg *mp;
	struct socket lsocket, fsocket;
	char *calloc(),*malloc();
	void smtp_rec(), smtp_cts(), smtp_state();

/*	printf("DOSMTPTICK() entered\n");	*/
	lsocket.address = ip_addr;	/* our ip address */
	fsocket.port = SMTP_PORT;
/* if lock file exists in mqueue dir, return */
	sprintf(lfilename,"%s%s",MAILQDIR,"lockfile");
	if ((lfile = fopen(lfilename,"x")) == NULL)
		return;
/* get next work filename from mqueue directory */
	sprintf(tmpstring,"%s%s",MAILQDIR,"*.wrk");
#ifndef	AMIGA
	filedir(tmpstring,0,wfilename);
#endif
	if (wfilename[0] == '\0')
		return;	/* no work to be done */
/* if we have work, rebuild the exact (non-wild) filename */
	mp = (struct smtp_msg *)calloc(1,sizeof (struct smtp_msg));
	sprintf(tmpstring,"%s%s",MAILQDIR,wfilename);
	ptr = &tmpstring[0];
	mp->filename = malloc((unsigned)strlen(ptr)+1);
	strcpy(mp->filename,ptr);
/*	printf("work file name: %s\n",mp->filename);	/* debug only */
	mp->wfile = fopen(mp->filename,"r");
/*   get ip address, from stuff, to stuff */
	fgets(tmpstring,LINELEN,mp->wfile);	/* read target ip addr */
/*	printf("target ip addr: %s\n",tmpstring);	*/
	fgets(mp->toaddr,LINELEN,mp->wfile);		/* who to */
	rip(mp->toaddr);
/*	printf("addressee: %s\n",mp->toaddr);		*/
	fgets(mp->fromaddr,LINELEN,mp->wfile);		/* who from */
	rip(mp->fromaddr);
/*	printf("sender: %s\n",mp->fromaddr);		*/
	fclose(mp->wfile);
/* set up the rest of the socket info from what we got */
	fsocket.address = aton(tmpstring);	/* destination ip address */
	lsocket.port = lport++;			/* next unused port */
/*   open smtp connection */
	mp->state = CLI_OPEN_STATE;		/* init state placeholder */
/*	printf("Opening TCP connection for SMTP client\n");	*/
	mp->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,1024,
			smtp_rec,smtp_cts,smtp_state,0,(int *)mp);
	mp->tcb->user = (int *)mp;		/* Upward pointer */

/*	printf("releasing lock\n");		*/
	if (lfile != NULL) {			/* release lock */
		fclose(lfile);
		unlink(lfilename);
	}
}

/* replace terminating end of line marker(s) with null */
rip(s)
char *s;
{
	char *c;

	c=s;
	while (*c != '\0') {
		switch (*c) {
		case '\r':
		case '\n':
			*c='\0';
			break;
		default:
			c++;
			break;
		}
	}
}

/* this is the master state machine that handles a single SMTP transaction */
smtp_transaction(mp)
struct smtp_msg *mp;
{
	char tmpstring[LINELEN];	/* where we build command lines */

/*	printf("SMTP_TRANSACTION() called, state=%u\n",mp->state);	*/
	if (affirmative(mp)) {
		switch(mp->state) {
		case CLI_OPEN_STATE:
			mp->state = CLI_MAIL_STATE;
			/* issue MAIL command */
/*			printf("FROMADDR = %s\n",mp->fromaddr);		*/
			sprintf(tmpstring,"mail from:<%s>\r\n",mp->fromaddr);
			sendit(mp,tmpstring);
			break;			
		case CLI_MAIL_STATE:
			mp->state = CLI_RCPT_STATE;
			/* issue RCPT command */
			sprintf(tmpstring,"rcpt to:<%s>\r\n",mp->toaddr);
			sendit(mp,tmpstring);
			break;
		case CLI_RCPT_STATE:
			mp->state = CLI_SEND_STATE;
			/* open text file */
			strcpy(tmpstring,mp->filename);
			strcpy(index(tmpstring,'.'),".txt");
/*			printf("text filename: %s",tmpstring);		*/
			mp->tfile = fopen(tmpstring,"r");
			/* issue DATA command */
			sprintf(tmpstring,"data\r\n");
			sendit(mp,tmpstring);
			break;
		case CLI_SEND_STATE:
			/* the transmitter upcall routine will advance the
			   state pointer on end of file, so we do nada... */
			break;
		case CLI_UNLK_STATE:
			unlink(mp->filename);	/* unlink workfile */
			/* close and unlink the textfile */
			fclose(mp->tfile);
			strcpy(tmpstring,mp->filename);
			strcpy(index(tmpstring,'.'),".txt");
			unlink(tmpstring);
			mp->state = CLI_QUIT_STATE;
			/* issue a quit command */
			sprintf(tmpstring,"quit\r\n");
			sendit(mp,tmpstring);
			break;
		case CLI_QUIT_STATE:
			/* either start next transaction, or quit */
			close_tcp(mp->tcb);	/* close up connection */
			break;
		}
	} else {	/* if we get here, means we got a negative reply */
			/* for the moment, just let that hose us... */
		mp->state = CLI_QUIT_STATE;
		/* issue a quit command */
		sprintf(tmpstring,"quit\r\n");
		sendit(mp,tmpstring);
	}
}

/* return true if the passed string contains a positive response code */
affirmative(mp)
struct smtp_msg *mp;
{
	/* 2 is always good, 3 is ok if we've just sent 'data' command */
	if ((*mp->buf = '2') || 
           ((*mp->buf = '3') && (mp->state = CLI_DATA_STATE)))
		return 1;
	else 	return 0;
}

/* smtp receiver upcall routine.  fires up the state machine to parse input */
static
void
smtp_rec(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct smtp_msg *mp;
	char *inet_ntoa(), c;
	struct mbuf *bp;
	/* may want a void line here for procedures used */

/*	printf("SMTP_REC called\n");		*/
	mp = (struct smtp_msg *)tcb->user;	/* point to our struct */
	recv_tcp(tcb,&bp,cnt);	/* suck up chars from low level routine */

	/* Assemble input line in buffer, return if incomplete */
	while(pullup(&bp,&c,1) == 1) {
		switch(c) {
		case '\r':	/* strip cr's */
			continue;
		case '\n':	/* line is finished, go do it! */
			mp->buf[mp->cnt] = '\0';
			smtp_transaction(mp);
			break;
		default:	/* other chars get added to buffer */
			mp->buf[mp->cnt++] = c;
			break;
		}
	}
}

/* smtp transmitter ready upcall routine.  twiddles cts flag */
static 
void
smtp_cts(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct smtp_msg *mp;
	struct mbuf *bp;
	char tmpstring[LINELEN];
	char *cp;
	int c;

/*	printf("SMTP_CTS called\n");		*/
	mp = (struct smtp_msg *)tcb->user;	/* point to our struct */

	/* don't do anything until/unless we're supposed to be sending */
	if(mp->state != CLI_SEND_STATE) return;

	if((bp = alloc_mbuf(cnt)) == NULLBUF){
		/* Hard to know what to do here */
		return;
	}
	cp = bp->data;
	while(cnt > 1 && (c = getc(mp->tfile)) != EOF){
		*cp++ = c;
		bp->cnt++;
		cnt--;
	}
	if(bp->cnt != 0)
		send_tcp(tcb,bp);
	else
		free_p(bp);

	if(cnt > 1){	/* EOF seen */
		sprintf(tmpstring,"\r\n.\r\n");
		sendit(mp,tmpstring);
		mp->state = CLI_UNLK_STATE;
	}
}

/* smtp state change upcall routine.  cans connection on error */
static
void
smtp_state(tcb,old,new)
struct tcb *tcb;
char old,new;
{
	struct smtp_msg *mp;

/*	printf("SMTP_STATE called, state=%u\n",new);	*/
	mp = (struct smtp_msg *)tcb->user;
	switch(new) {
	case ESTABLISHED:
		mp->state = CLI_OPEN_STATE;	/* shouldn't be needed */
		break;
	case CLOSE_WAIT:
		close_tcp(tcb);			/* shut things down */
			/* may want to do something here to shut down
			   the rest of the transaction? */
		break;
	case CLOSED:
		del_tcp(tcb);			/* hosed for good */
		if(mp->filename != NULLCHAR)
			free(mp->filename);
		free((char *)mp);
		break;
	}
}

/* Send message back to server */
static
void
sendit(mp,message)
struct smtp_msg *mp;
char *message;
{
	struct mbuf *bp,*qdata();

/*	printf("SENDIT called: %s",message);		*/
	bp = qdata(message,(int16)strlen(message));
	send_tcp(mp->tcb,bp);
}
SHAR_EOF
cat << \SHAR_EOF > tcpout.c
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"

int16 tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */

/* Send a segment on the specified connection. One gets sent only
 * if there is data to be sent or if "force" is non zero
 */
void
tcp_output(tcb)
register struct tcb *tcb;
{
	struct pseudo_header ph;
	struct mbuf *hbp;
	int16 hsize;	/* Size of header */
	struct tcp_header *tcph;
	struct mss *mssp;
	int16 ssize;	/* Size of current segment being sent,
			 * including SYN and FIN flags */
	int16 dsize;	/* Size of segment less SYN and FIN */
	int16 usable;	/* Usable window */
	int16 sent;	/* Sequence count (incl SYN/FIN) already in the pipe */

	if(tcb == NULLTCB)
		return;

	switch(tcb->state){
	case LISTEN:
	case CLOSED:
		return;	/* Don't send anything */
	}
	for(;;){
		sent = tcb->snd.ptr - tcb->snd.una;

		/* If this is a retransmission, send only the oldest segment
		 * (first-only retransmission policy)
		 */
		if(tcb->retry != 0 && sent != 0)
			break;

		/* There can only be one outstanding segment in this state
		 * since the other end would reject any segment without SYN
		 */
		if(tcb->state == SYN_SENT && sent != 0)
			break;
		if(tcb->snd.wnd == 0){
			/* Allow only one closed-window probe at a time */
			if(sent != 0)
				break;
			/* Force a closed-window probe */
			usable = 1;
		} else {
			/* usable window = offered window - unacked bytes in transit */
			usable = tcb->snd.wnd - sent;

			/* John Nagle's "single outstanding segment" rule.
			 * Allow only one segment in the pipeline unless there is enough
			 * unsent data to form at least one maximum-sized segment.
			 */
			if(sent != 0 && tcb->sndcnt - sent < tcb->mss){
				usable = 0;
			}
			/* Silly window avoidance. Don't send anything if the usable window
			 * is less than a quarter of the offered window.
			 * This test comes into play only when the offered window is at
			 * least 4 times the MSS; otherwise Nagle's test is sufficient
			 * to prevent SWS.
		 	 */
			else if(usable < tcb->snd.wnd/4){
				usable = 0;
			}
		}
		/* Compute size of segment to send. This is either the usable
		 * window, the mss, or the amount we have on hand, whichever is less.
		 * (I don't like optimistic windows)
		 */
		ssize = min(tcb->sndcnt - sent,usable);
		ssize = min(ssize,tcb->mss);
		dsize = ssize;

		if(ssize == 0 && tcb->force == 0)
			break;		/* No need to send anything */

		/* Determine size of TCP header and allocate mbuf.
		 * If sending SYN, allow space for the MSS option
		 */
		switch(tcb->state){
		case SYN_SENT:
		case SYN_RECEIVED:
			hsize = sizeof(struct tcp_header) + sizeof(struct mss);
			break;
		default:
			hsize = sizeof(struct tcp_header);
			break;
		}
		if((hbp = alloc_mbuf(hsize)) == NULLBUF)
			break;	/* No room to form a packet */

		tcb->force = 0;	/* Only one forced segment! */
		hbp->cnt = hsize;

		tcph = (struct tcp_header *)hbp->data;
		tcph->source = htons(tcb->conn.local.port);
		tcph->dest = htons(tcb->conn.remote.port);
		tcph->offset = hsize/sizeof(int32) << DSHIFT;
		tcph->flags = 0;

		/* Set the SYN and ACK flags according to the state we're in. It is
		 * assumed that if this segment is associated with a state transition,
		 * then the state change will already have been made. This allows
		 * this routine to be called from a retransmission timeout with
		 * force=1.
		 * If SYN is being sent, adjust the dsize counter so we'll
		 * try to get the right amount of data off the send queue.
		 */
		switch(tcb->state){
		case SYN_SENT:
		case SYN_RECEIVED:
			if(tcb->snd.ptr == tcb->iss){
				tcph->flags = SYN;
				dsize--;
			}
			/* Also send MSS */
			mssp = (struct mss *)(tcph + 1);
			mssp->kind = MSS_KIND;
			mssp->length = MSS_LENGTH;
			mssp->mss = htons(tcp_mss);
		}
		switch(tcb->state){
		case SYN_RECEIVED:
		case ESTABLISHED:
		case CLOSE_WAIT:
		case FINWAIT2:
		case TIME_WAIT:
		case CLOSING:
		case FINWAIT1:
		case LAST_ACK:
			tcph->flags |= ACK;
			break;
		}
		tcph->seq = htonl(tcb->snd.ptr);
		tcph->ack = htonl(tcb->rcv.nxt);
		tcph->wnd = htons(tcb->rcv.wnd);
		tcph->checksum = 0;
		tcph->up = 0;

		/* Now try to extract some data from the send queue.
		 * Since SYN and FIN occupy sequence space and are reflected
		 * in sndcnt but don't actually sit in the send queue,
		 * dup_p will return one less than dsize if a FIN needs to be sent.
		 */
		if(dsize != 0){
			if(dup_p(&hbp->next,tcb->sndq,sent,dsize) != dsize){
				/* We ran past the end of the send queue; send a FIN */
				tcph->flags |= FIN;
				dsize--;
			}
		}
		/* If the entire send queue will now be in the pipe, set the
		 * push flag
		 */
		if(dsize != 0 && sent + ssize == tcb->sndcnt)
			tcph->flags |= PSH;

		tcb->snd.ptr += ssize;
		/* If this is the first transmission of a range of sequence
		 * numbers, record it so we'll accept acknowledgments
		 * for it later
		 */
		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
			tcb->snd.nxt = tcb->snd.ptr;
		/* Fill in fields of pseudo IP header */
		ph.source = tcb->conn.local.address;
		ph.dest = tcb->conn.remote.address;
		ph.protocol = TCP_PTCL;
		ph.zero = 0;
		ph.length = hsize + dsize;

		/* Compute checksum over pseudo-header, TCP header and data,
		 * and pass it off to IP
		 */
		tcph->checksum = cksum(&ph,hbp,ph.length);

		/* If we're sending some data or flags, start retransmission
		 * timer if it isn't already running.
		 */
		if(ssize != 0){
			if(tcb->timer.state != TIMER_RUN){
				/* Never initialize the timer with zero; it won't run! */
				tcb->timer.start = max(tcb->timer.start,1);
				start_timer(&tcb->timer);
			}
			/* If round trip timer isn't running, start it */
			if(seq_ge(tcb->snd.una,tcb->rttseq))
				tcb->rttseq = tcb->snd.ptr;
		}
		ip_send(tcb->conn.local.address,tcb->conn.remote.address,
		 TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
	}
}
SHAR_EOF
cat << \SHAR_EOF > tcpuser.c
/* User calls to TCP */
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "ip.h"
#include "tcp.h"

int16 tcp_window = DEF_WND;

struct tcb *
open_tcp(lsocket,fsocket,active,window,r_upcall,t_upcall,s_upcall,tos,user)
struct socket *lsocket;	/* Local socket */
struct socket *fsocket;	/* Remote socket */
int active;		/* Active/passive */
int16 window;		/* Receive window (and send buffer) sizes */
void (*r_upcall)();	/* Function to call when data arrives */
void (*t_upcall)();	/* Function to call when ok to send more data */
void (*s_upcall)();	/* Function to call when connection state changes */
char tos;
int *user;		/* User linkage area */
{
	struct connection conn;
	register struct tcb *tcb;
	void send_syn();

	if(lsocket == NULLSOCK){
		net_error = INVALID;
		return NULLTCB;
	}
	conn.local.address = lsocket->address;
	conn.local.port = lsocket->port;
	if(fsocket != NULLSOCK){
		conn.remote.address = fsocket->address;
		conn.remote.port = fsocket->port;
	} else {
		conn.remote.address = 0;
		conn.remote.port = 0;
	}
	if((tcb = lookup_tcb(&conn)) == NULLTCB){
		if((tcb = create_tcb(&conn)) == NULLTCB){
			net_error = NO_SPACE;
			return NULLTCB;
		}
	} else if(tcb->state != LISTEN){
		net_error = CON_EXISTS;
		return NULLTCB;
	}
	tcb->user = user;
	if(window != 0)
		tcb->window = tcb->rcv.wnd = window;
	else
		tcb->window = tcb->rcv.wnd = tcp_window;
	tcb->r_upcall = r_upcall;
	tcb->t_upcall = t_upcall;
	tcb->s_upcall = s_upcall;
	tcb->tos = tos;
	if(!active){
		setstate(tcb,LISTEN);
		return tcb;
	}
	/* Send SYN, go into SYN_SENT state */
	send_syn(tcb);
	setstate(tcb,SYN_SENT);
	tcp_output(tcb);
	tcp_stat.conout++;
	return tcb;
}
/* User send routine */
int
send_tcp(tcb,bp)
register struct tcb *tcb;
struct mbuf *bp;
{
	int16 cnt;

	if(tcb == NULLTCB || bp == NULLBUF){
		free_p(bp);
		net_error = INVALID;
		return -1;
	}
	cnt = len_mbuf(bp);
#ifdef	TIGHT
	/* If this would overfill our send queue, reject it entirely */
	if(tcb->sndcnt + cnt > tcb->window){
		free_p(bp);
		net_error = WOULDBLK;
		return -1;
	}
#endif
	switch(tcb->state){
	case CLOSED:
		free_p(bp);
		net_error = NO_CONN;
		return -1;
	case LISTEN:	/* Change state from passive to active */
		send_syn(tcb);
		setstate(tcb,SYN_SENT);	/* Note fall-thru */
	case SYN_SENT:
	case SYN_RECEIVED:
	case ESTABLISHED:
	case CLOSE_WAIT:
		append(&tcb->sndq,bp);
		tcb->sndcnt += cnt;
		tcp_output(tcb);
		break;
	case FINWAIT1:
	case FINWAIT2:
	case CLOSING:
	case LAST_ACK:
	case TIME_WAIT:
		free_p(bp);
		net_error = CON_CLOS;
		return -1;
	}
	return cnt;
}
/* User receive routine */
int
recv_tcp(tcb,bp,cnt)
register struct tcb *tcb;
struct mbuf **bp;
int16 cnt;
{
	if(tcb == NULLTCB || bp == (struct mbuf **)NULL){
		net_error = INVALID;
		return -1;
	}
	/* cnt == 0 means "I want it all" */
	if(cnt == 0)
		cnt = tcb->rcvcnt;
	/* If there's something on the queue, just return it regardless
	 * of the state we're in.
	 */
	if(tcb->rcvcnt != 0){
		/* See if the user can take all of it */
		if(tcb->rcvcnt <= cnt){
			cnt = tcb->rcvcnt;
			*bp = tcb->rcvq;
			tcb->rcvq = NULLBUF;
		} else {
			if((*bp = alloc_mbuf(cnt)) == NULLBUF){
				net_error = NO_SPACE;
				return -1;
			}
			pullup(&tcb->rcvq,(*bp)->data,cnt);
			(*bp)->cnt = cnt;
		}
		tcb->rcvcnt -= cnt;
		tcb->rcv.wnd += cnt;
		/* Do a window update if it was closed */
		if(cnt == tcb->rcv.wnd){
			tcb->force = 1;
			tcp_output(tcb);
		}
		return cnt;
	} else {
		/* If there's nothing on the queue, our action depends on what state
		 * we're in (i.e., whether or not we're expecting any more data).
		 * If no more data is expected, then simply return 0; this is
		 * interpreted as "end of file".
		 */
		switch(tcb->state){
		case LISTEN:
		case SYN_SENT:
		case SYN_RECEIVED:
		case ESTABLISHED:
		case FINWAIT1:
		case FINWAIT2:
			*bp = NULLBUF;
			net_error = WOULDBLK;
			return -1;
		case CLOSED:
		case CLOSE_WAIT:
		case CLOSING:
		case LAST_ACK:
		case TIME_WAIT:
			*bp = NULLBUF;
			return 0;
		}
	}
	return 0;	/* Not reached, but lint doesn't know that */
}
/* This really means "I have no more data to send". It only closes the
 * connection in one direction, and we can continue to receive data
 * indefinitely.
 */
int
close_tcp(tcb)
register struct tcb *tcb;
{
	if(tcb == NULLTCB){
		net_error = INVALID;
		return -1;
	}
	switch(tcb->state){
	case LISTEN:
	case SYN_SENT:
		close_self(tcb,NORMAL);
		return 0;
	case SYN_RECEIVED:
	case ESTABLISHED:
		tcb->sndcnt++;
		tcb->snd.nxt++;
		setstate(tcb,FINWAIT1);
		tcp_output(tcb);
		return 0;
	case CLOSE_WAIT:
		tcb->sndcnt++;
		tcb->snd.nxt++;
		setstate(tcb,LAST_ACK);
		tcp_output(tcb);
		return 0;
	case FINWAIT1:
	case FINWAIT2:
	case CLOSING:
	case LAST_ACK:
	case TIME_WAIT:
		net_error = CON_CLOS;
		return -1;
	}
	return -1;	/* "Can't happen" */
}
/* Delete TCB, free resources. The user is not notified, even if the TCB is
 * not in the CLOSED state. This function should normally be called by the
 * user only in response to a state change upcall to CLOSED state.
 */
int
del_tcp(tcb)
register struct tcb *tcb;
{
	void unlink_tcb();
	struct reseq *rp,*rp1;

	if(tcb == NULLTCB){
		net_error = INVALID;
		return -1;
	}
	unlink_tcb(tcb);
	stop_timer(&tcb->timer);
	for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
		rp1 = rp->next;
		free_p(rp->bp);
		free((char *)rp);
	}
	tcb->reseq = NULLRESEQ;
	free_p(tcb->rcvq);
	free_p(tcb->sndq);
	free((char *)tcb);
	return 0;
}
/* Do printf on a tcp connection */
/*VARARGS*/
tprintf(tcb,message,arg1,arg2)
struct tcb *tcb;
char *message,*arg1,*arg2;
{
	struct mbuf *bp;
	int16 len;
	char *cp,*index();

	if(tcb == NULLTCB)
		return 0;

	len = strlen(message) + 10;	/* fudge factor */
	if((cp = index(message,'%')) != NULLCHAR){
		/* What a gross hack! */
		len += strlen(arg1);
		if((cp = index(cp+1,'%')) != NULLCHAR){
			/* I don't believe I'm writing this */
			len += strlen(arg2);
		}
	}
	bp = alloc_mbuf(len);
	len = sprintf(bp->data,message,arg1,arg2);
	bp->cnt = strlen(bp->data);
	send_tcp(tcb,bp);
	return len;
}
SHAR_EOF
cat << \SHAR_EOF > telnet.c
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "timer.h"
#include "internet.h"
#include "icmp.h"
#include "netuser.h"
#include "tcp.h"
#include "telnet.h"
#include "session.h"

extern char nospace[];
int refuse_echo = 0;
int unix_line_mode = 0;    /* if true turn <cr> to <nl> when in line mode */

#ifdef	DEBUG
char *t_options[] = {
	"Transmit Binary",
	"Echo",
	"",
	"Suppress Go Ahead",
	"",
	"Status",
	"Timing Mark"
};
#endif

/* Execute user telnet command */
int
dotelnet(argc,argv)
int argc;
char *argv[];
{
	void t_state(),rcv_char();
	char *inet_ntoa(),*calloc();
	int32 aton();
	int send_tel();
        int unix_send_tel();
	struct session *s,*newsession();
	struct telnet *tn;
	struct tcb *tcb;
	struct socket lsocket,fsocket;


	lsocket.address = ip_addr;
	lsocket.port = lport++;
	fsocket.address = aton(argv[1]);
	if(argc < 3)
		fsocket.port = TELNET_PORT;
	else
		fsocket.port = atoi(argv[2]);

	/* Allocate a session descriptor */
	if((s = newsession()) == NULLSESSION){
		printf("Too many sessions\r\n");
		return 1;
	}
	s->type = TELNET;
	if ((refuse_echo == 0) && (unix_line_mode != 0)) {
		s->parse = unix_send_tel;
	} else {
		s->parse = send_tel;
	}
	current = s;

	/* Create and initialize a Telnet protocol descriptor */
	if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
		printf(nospace);
		s->type = FREE;
		return 1;
	}
	tn->session = s;	/* Upward pointer */
	tn->state = TS_DATA;
	s->cb.telnet = tn;	/* Downward pointer */

	tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,0,
	 rcv_char,NULLVFP,t_state,0,(int *)tn);
	if(tcb == NULLTCB || tcb->state == CLOSED){
		/* This is actually a bit dirty here. About the only time the
		 * state will be closed here is if we tried to connect to
		 * ourselves and got RST'ed.  If this is true then the close
		 * upcall will already have freed the TCB and telnet block,
		 * so we're looking at the TCB after it's back on the heap.
		 */
		return 0;
	}
	tn->tcb = tcb;	/* Downward pointer */
	go();
	return 0;
}

/* Process typed characters */
int
unix_send_tel(buf,n)
char *buf;
int16 n;
{
	int i;

	for (i=0; (i<n) && (buf[i] != '\r'); i++)
		;
	if (buf[i] == '\r') {
		buf[i] = '\n';
		n = i+1;
	}
	send_tel(buf,n);
}
int
send_tel(buf,n)
char *buf;
int16 n;
{
	struct mbuf *bp,*qdata();
	if(current == NULLSESSION || current->cb.telnet == NULLTN
	 || current->cb.telnet->tcb == NULLTCB)
		return;
	bp = qdata(buf,n);
	send_tcp(current->cb.telnet->tcb,bp);
}

/* Process incoming TELNET characters */
int
tel_input(tn,bp)
register struct telnet *tn;
struct mbuf *bp;
{
	char c;
	int ci;
	void doopt(),dontopt(),willopt(),wontopt(),answer();
#ifdef	FAST	/* DON'T USE -- Aztec memchr() routine is broken */
	char *memchr();

	/* Optimization for very common special case -- no command chars */
	if(tn->state == TS_DATA){
		while(bp != NULLBUF && memchr(bp->data,IAC,bp->cnt) == NULLCHAR){
			fflush(stdout);
			write(1,bp->data,bp->cnt);
			bp = free_mbuf(bp);
		}
	}
#endif
	while(pullup(&bp,&c,1) == 1){
		ci = c & 0xff;
		switch(tn->state){
		case TS_DATA:
			if(ci == IAC){
				tn->state = TS_IAC;
			} else {
				if(!tn->remote[TN_TRANSMIT_BINARY])
					c &= 0x7f;
				putchar(c);
			}
			break;
		case TS_IAC:
			switch(ci){
			case WILL:
				tn->state = TS_WILL;
				break;
			case WONT:
				tn->state = TS_WONT;
				break;
			case DO:
				tn->state = TS_DO;
				break;
			case DONT:
				tn->state = TS_DONT;
				break;
			case IAC:
				putchar(c);
				tn->state = TS_DATA;
				break;
			default:
				tn->state = TS_DATA;
				break;
			}
			break;
		case TS_WILL:
			willopt(tn,ci);
			tn->state = TS_DATA;
			break;
		case TS_WONT:
			wontopt(tn,ci);
			tn->state = TS_DATA;
			break;
		case TS_DO:
			doopt(tn,ci);
			tn->state = TS_DATA;
			break;
		case TS_DONT:
			dontopt(tn,ci);
			tn->state = TS_DATA;
			break;
		}
	}
}

/* Telnet receiver upcall routine */
void
rcv_char(tcb,cnt)
register struct tcb *tcb;
int16 cnt;
{
	struct mbuf *bp;
	struct telnet *tn;

	if((tn = (struct telnet *)tcb->user) == NULLTN){
		/* Unknown connection; ignore it */
		return;
	}
	/* Hold output if we're not the current session */
	if(mode != CONV_MODE || current == NULLSESSION || current->cb.telnet != tn)
		return;

	if(recv_tcp(tcb,&bp,cnt) > 0)
		tel_input(tn,bp);

	fflush(stdout);
}

/* State change upcall routine */
void
t_state(tcb,old,new)
register struct tcb *tcb;
char old,new;
{
	struct telnet *tn;
	char notify = 0;
	extern char *tcpstates[];
	extern char *reasons[];
	extern char *unreach[];
	extern char *exceed[];

	/* Can't add a check for unknown connection here, it would loop
	 * on a close upcall! We're just careful later on.
	 */
	tn = (struct telnet *)tcb->user;

	if(current != NULLSESSION && current->type == TELNET && current->cb.telnet == tn)
		notify = 1;

	switch(new){
	case CLOSE_WAIT:
		if(notify)
			printf("%s\r\n",tcpstates[new]);
		close_tcp(tcb);
		break;
	case CLOSED:	/* court adjourned */
		if(notify){
			printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
			if(tcb->reason == NETWORK){
				switch(tcb->type){
				case DEST_UNREACH:
					printf(": %s unreachable",unreach[tcb->code]);
					break;
				case TIME_EXCEED:
					printf(": %s time exceeded",exceed[tcb->code]);
					break;
				}
			}
			printf(")\r\n");
			cmdmode();
		}
		del_tcp(tcb);
		if(tn != NULLTN)
			free_telnet(tn);
		break;
	default:
		if(notify)
			printf("%s\r\n",tcpstates[new]);
		break;
	}
	fflush(stdout);
}
/* Delete telnet structure */
static
free_telnet(tn)
struct telnet *tn;
{
	if(tn->session != NULLSESSION)
		freesession(tn->session);

	if(tn != NULLTN)
		free((char *)tn);
}

/* The guts of the actual Telnet protocol: negotiating options */
static
void
willopt(tn,opt)
struct telnet *tn;
int opt;
{
	int ack;
	void answer();

#ifdef	DEBUG
	printf("recv: will ");
	if(opt <= NOPTIONS)
		printf("%s\r\n",t_options[opt]);
	else
		printf("%u\r\n",opt);
#endif
	
	switch(opt){
	case TN_TRANSMIT_BINARY:
	case TN_ECHO:
	case TN_SUPPRESS_GA:
		if(tn->remote[opt] == 1)
			return;		/* Already set, ignore to prevent loop */
		if(opt == TN_ECHO){
			if(refuse_echo){
				/* User doesn't want to accept */
				ack = DONT;
				break;
			} else
				raw();		/* Put tty into raw mode */
		}
		tn->remote[opt] = 1;
		ack = DO;			
		break;
	default:
		ack = DONT;	/* We don't know what he's offering; refuse */
	}
	answer(tn,ack,opt);
}
static
void
wontopt(tn,opt)
struct telnet *tn;
int opt;
{
	void answer();

#ifdef	DEBUG
	printf("recv: wont ");
	if(opt <= NOPTIONS)
		printf("%s\r\n",t_options[opt]);
	else
		printf("%u\r\n",opt);
#endif
	if(opt <= NOPTIONS){
		if(tn->remote[opt] == 0)
			return;		/* Already clear, ignore to prevent loop */
		tn->remote[opt] = 0;
		if(opt == TN_ECHO)
			cooked();	/* Put tty into cooked mode */
	}
	answer(tn,DONT,opt);	/* Must always accept */
}
static
void
doopt(tn,opt)
struct telnet *tn;
int opt;
{
	void answer();
	int ack;

#ifdef	DEBUG
	printf("recv: do ");
	if(opt <= NOPTIONS)
		printf("%s\r\n",t_options[opt]);
	else
		printf("%u\r\n",opt);
#endif
	switch(opt){
#ifdef	FUTURE	/* Use when local options are implemented */
		if(tn->local[opt] == 1)
			return;		/* Already set, ignore to prevent loop */
		tn->local[opt] = 1;
		ack = WILL;
		break;
#endif
	default:
		ack = WONT;	/* Don't know what it is */
	}
	answer(tn,ack,opt);
}
static
void
dontopt(tn,opt)
struct telnet *tn;
int opt;
{
	void answer();

#ifdef	DEBUG
	printf("recv: dont ");
	if(opt <= NOPTIONS)
		printf("%s\r\n",t_options[opt]);
	else
		printf("%u\r\n",opt);
#endif
	if(opt <= NOPTIONS){
		if(tn->local[opt] == 0){
			/* Already clear, ignore to prevent loop */
			return;
		}
		tn->local[opt] = 0;
	}
	answer(tn,WONT,opt);
}
static
void
answer(tn,r1,r2)
struct telnet *tn;
int r1,r2;
{
	struct mbuf *bp,*qdata();
	char s[3];

#ifdef	DEBUG
	switch(r1){
	case WILL:
		printf("sent: will ");
		break;
	case WONT:
		printf("sent: wont ");
		break;
	case DO:
		printf("sent: do ");
		break;
	case DONT:
		printf("sent: dont ");
		break;
	}
	if(r2 <= 6)
		printf("%s\r\n",t_options[r2]);
	else
		printf("%u\r\n",r2);
#endif

	s[0] = IAC;
	s[1] = r1;
	s[2] = r2;
	bp = qdata(s,(int16)3);
	send_tcp(tn->tcb,bp);
}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.