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

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

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

#	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:
#	amiga.c
#	amigadev.c
#	iproute.c
#	telnetp.c
# This archive created: Fri Mar 17 17:58:48 1989
cat << \SHAR_EOF > amiga.c
/*
 *  Modifications to existing pc.c module are --
 *
 *  Copyright (c) 1987
 *  Louis A. Mamakos
 *
 *  This work, or any derivations thereof may be used for non-commercial
 *  purposes only.  So there.
 */

/* OS- and machine-dependent stuff for the Commodore-Amiga 1000 */

#define	AMIGAVERSION	"3"

#include <exec/types.h>
#include <functions.h>		
/* for Manx Aztec C, get func returns */
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/io.h>

#include <devices/console.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <intuition/intuition.h>

#include <stdio.h>
#include "machdep.h"
#include "amiga.h"
#include "mbuf.h"
#include "internet.h"
#include "iface.h"
#include "cmdparse.h"
#include "slip.h"
#include "timer.h"
#include "netuser.h"
#include "ip.h"
#include "tcp.h"
#ifdef	TRACE
#include "trace.h"
#endif

static char *copyright_notice[2] = {
	"AMIGA port of KA9Q TCP/IP (C) Copyright 1987 Louis A. Mamakos\r\n",
	"for non-commercial, non-profic use only\r\n"};

struct asy asy[ASY_MAX];

void *malloc();

void setiss();

/* Interface list header */
struct interface *ifaces;

struct IntuitionBase *IntuitionBase;
static char banner[80];
static struct NewWindow nw = {
	0, 0, 640, 200,		/* left, top, (max) width, (max) height */
	0, 1,			/* detail pen, block pen */
	0,			/* IDCMP flags */
	SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING |
	    SIZEBBOTTOM | ACTIVATE | NOCAREREFRESH,	/* window flags */
	NULL, NULL,		/* gadget, checkmark */
	(UBYTE *)&banner[0],	/* title of window */
	NULL, NULL,		/* screen, bitmap */
	200, 50, -1, -1,	/* sizing limits */
	WBENCHSCREEN,		/* on the workbench */
};

APTR	oldwindowptr;
struct Process *mytask;

struct MsgPort *keyboard, *consinp, *consoutp, *serinp, *seroutp,
		      *timerp;
struct IOExtSer serin, serout;
struct IOStdReq consin, consout;
struct timerequest tr;
struct Window *win;

char InputCharacter;

int timeropen, serialopen;
#ifdef AMIGADEVDRV
int DeviceSignal;
#endif
struct timer worktimer;		/* this is NOT a timer.device timer */
void worker();
#ifdef LATTICE
extern struct { short error; char *msg; } os_errlist[];
extern int _OSERR, os_nerr;
#endif
static
clean(why)
	char *why;
{
	int i;

#ifdef AMIGADEVDRV
	if (DeviceSignal >= 0)
		FreeSignal(DeviceSignal);
#endif
	if (timeropen)
		CloseDevice(&tr);
	if (serialopen)
		CloseDevice(&serin);
	if (win)
		CloseWindow(win);
	if (consinp)
		DeletePort(consinp);
	if (consoutp)
		DeletePort(consoutp);
	if (serinp)
		DeletePort(serinp);
	if (seroutp)
		DeletePort(seroutp);
	if (timerp)
		DeletePort(timerp);

	mytask->pr_WindowPtr = oldwindowptr;

	if (why) {
           myoserr(why);
	}
	exit(0);
}
myoserr(why)
char *why;
{
  int i;
		fprintf(stderr, "%s: ", why); 
#ifdef LATTICE
		fprintf(stderr, "%d: ", _OSERR);

		for(i = 0; os_errlist[i].error < os_nerr; i++)
		  if (os_errlist[i].error == _OSERR)
			fprintf(stderr, os_errlist[i].msg);
#endif
		fprintf(stderr, "\r\n");
}
/* Called at startup time to set up console I/O, memory heap */
ioinit()
{
	extern char major_rev[], minor_rev[];
	struct Screen *scr;

	mytask = (struct Process *) FindTask((char *) NULL);
	oldwindowptr = mytask->pr_WindowPtr;
	mytask->pr_WindowPtr = (APTR) -1;	/* disable DOS requestors */

	if ((IntuitionBase = (struct IntuitionBase *)
	   OpenLibrary("intuition.library", 33L)) == NULL)
		clean("No intuition: Version 1.2 of Amiga Systems Software required");

	sprintf(banner,
#ifdef	AMIGADEVDRV
	"KA9Q Internet Protocol Package, v%s.%s (Amiga version %sD)",
#else
	"KA9Q Internet Protocol Package, v%s.%s (Amiga version %s)",
#endif
		major_rev, minor_rev, AMIGAVERSION);

	/*
	 *  Try to determine the size of the workbench screen
	 */
	scr = malloc(sizeof(struct Screen));
	if (scr==NULL)
		clean("Can't alloc screen");

	if (GetScreenData(scr, (ULONG) sizeof(struct Screen),
			  WBENCHSCREEN, NULL) == TRUE) {
		nw.Width = scr->Width;
		nw.Height = scr->Height-20;
		nw.TopEdge = 19;
	} else
		fprintf(stderr, "Can't GetScreenData()\n");

	free((char *)scr);
	if ((win = OpenWindow(&nw)) == NULL)
		clean("Can't open window");

	if ((consinp = CreatePort("net:console in", 0L)) == NULL)
		clean("Can't create console port");

	if ((consoutp = CreatePort("net:console out", 0L)) == NULL)
		clean("Can't create console port");

	if ((timerp = CreatePort("net:timer", 0L)) == NULL)
		clean("Can't create timer port");

	consin.io_Data = (APTR) win;
	consin.io_Length = sizeof(struct Window);

	if (OpenDevice("console.device", 0L, &consin, 0L) != 0L)
		clean("Can't open console device");

	consout = consin;

	consin.io_Message.mn_ReplyPort = consinp;
	consin.io_Length = 1;
	consin.io_Data = (APTR) &InputCharacter;
	consin.io_Command = CMD_READ;
	SendIO(&consin);
	consout.io_Message.mn_ReplyPort = consoutp;
	consout.io_Command = CMD_WRITE;

	/* create and start up timer */
	tr.tr_node.io_Message.mn_ReplyPort = timerp;
	if (OpenDevice("timer.device", UNIT_VBLANK, &tr, 0L) != 0L)
		clean("Can't open timer");
#ifdef AMIGADEVDRV
	if ((DeviceSignal = AllocSignal(-1)) == -1)
	  clean("Can't allocate device signal");
#endif
	timeropen++;
	tr.tr_node.io_Command = TR_GETSYSTIME;
	DoIO(&tr);
#ifdef	DEBUG
	printf("System time is %ld\n", tr.tr_time.tv_secs);
#endif
	setiss(tr.tr_time.tv_secs);
	tr.tr_node.io_Command = TR_ADDREQUEST;
	tr.tr_time.tv_secs = 0;
	tr.tr_time.tv_micro = MSPTICK*1000L;	/* convert to microseconds */
	SendIO(&tr);

	set_timer(&worktimer, 1500);	/* set for 1.5 seconds */
	worktimer.func = worker;
#ifdef	AMIGADEVDRV
	DriverInit();			/* install internet.device driver */
#endif
	start_timer(&worktimer);
}

/* Called just before exiting to restore console state */
iostop()
{
	while(ifaces != NULLIF){
		if(ifaces->stop != NULLFP)
			(*ifaces->stop)(ifaces);
		ifaces = ifaces->next;
	}
#ifdef	AMIGADEVDRV
	DriverShutdown();
#endif
	clean((char *)0);
}

#define	BUFMAXCNT	150
static char conbuf[BUFMAXCNT];
static int concnt = 0;

int
amigaputchar(c)
	char c;
{
	conbuf[concnt++] = c;
	if ((c == '\n') || (concnt == BUFMAXCNT))
		amigaflush();
	return c;
}

amigaflush()
{
	if (concnt == 0)
		return;
	consout.io_Data = (APTR) conbuf;
	consout.io_Length = concnt;
	consout.io_Command = CMD_WRITE;
	DoIO(&consout);
	concnt = 0;
}
	
/*
 *  Begin terrible, horrible hack.  All output should be printed upon (into?)
 *  the window we opened before.  Here goes nothing...
 */
void
printf(a, b, c, d, e, f, g, h, i, j, k)
	char *a;
	int b, c, d, e, f, g, h, i, j, k;
{
	if (concnt)
		amigaflush();

	sprintf(conbuf, a, b, c, d, e, f, g, h, i, j, k);
	consout.io_Data = (APTR) conbuf;
	consout.io_Length = strlen(conbuf);
	consout.io_Command = CMD_WRITE;
	DoIO(&consout);		/* no use in doing this async */
}

	
/* check active connections and update titles */
void
check_connections()
{
	extern struct tcb *tcbs[NTCB];
	register struct tcb *tcb;
	register int i;
	int newlisten, newopn;
	static int listen = -1, opn = -1;
	static msg[80];

	newlisten = newopn = 0;

	for(i=0; i<NTCB; i++)
		for(tcb=tcbs[i]; tcb != NULLTCB; tcb = tcb->next)
			if (tcb->state == LISTEN)
				newlisten++;
			else
				newopn++;

	if (newlisten != listen || newopn != opn) {
		listen = newlisten;
		opn = newopn;
		sprintf(msg,
	   "Amiga Port by WA3YMH (TCP: listen: %d   open: %d)", listen, opn);
		SetWindowTitles(win, -1L, msg);
	}
}

/* called every second or so */
void
worker()
{
	check_connections();
	start_timer(&worktimer);
}

#if	0
/* processes any messages that Intuition sends us */
void
Do_Intuition_Message(m)
	register struct IntuiMessage *m;
{
	ULONG class;
	USHORT code, qualifier;

	class = m->Class;
	code = m->Code;
	qualifier = m->Qualifier;
	ReplyMsg((struct Message *) m);	   /* reply msg back to Intuition */
	switch (class) {
	case INTUITICKS:
		check_connections();		
		break;
	}
}
#endif

	
/*
 * wait for something to happen
 */
eihalt()
{
	register struct IntuiMessage *msg;
	static ULONG mask = 0;

	if (mask == 0L)
		mask = 	1L << consinp->mp_SigBit |
			1L << serinp->mp_SigBit |
			1L << timerp->mp_SigBit |
#ifdef AMIGDEVDRV
			1L << DeviceSignal	|
#endif
#if	0
			1L << win->UserPort->mp_SigBit |
#endif
			1L << seroutp->mp_SigBit;

	(void) Wait(mask);
#if	0
	while (msg = (struct IntuiMessage *)GetMsg(win->UserPort))
		Do_Intuition_Message(msg);
#endif
}

/* checks the time then ticks and updates ISS */
void
check_time()
{
	int32 iss();

	if (CheckIO(&tr)) {
		WaitIO(&tr);
		(void) GetMsg(timerp);
		tick();
		(void)iss();
		tr.tr_time.tv_secs = 0;
		tr.tr_time.tv_micro = MSPTICK*1000L;
					/* convert to microseconds */
		SendIO(&tr);
	}
}

/* Initialize asynch port "dev" */


/*
 *  We will make the bold and rash assumption that the asy link will be used
 *  for slip and slip-like stuff.  That is, we assume that there is an
 *  an end-of-frame character that we can have the serial.device driver look
 *  for.  Thus, we can fire up a single I/O request and have the whole frame
 *  come back at once.
 */
int
asy_init(dev, bufsize)
int16 dev;
unsigned bufsize;
{
	char serinitstr[1];
	int serinitlen;

	serinitstr[0] = FR_END;	/* initialize initialization string to a */
	serinitlen = 1;		/* frame end to flush receiver */

	if (serialopen) {
		printf("\namiga: Error - serial device already open.\n");
		return 0;
	}
	if ((serinp = CreatePort("net:serin", 0L)) == NULL)
		clean("Can't create serial input port");

	if ((seroutp = CreatePort("net:serout", 0L)) == NULL)
		clean("Can't create serial output port");

	/*
	 * Open serial device.
	 */
	serin.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;  /* ? */
	serin.io_Status = 0;
	serin.io_RBufLen = bufsize;
	asy[dev].speed = serin.io_Baud = 2400;	/* default speed */
	if (OpenDevice("serial.device", 0L, &serin, 0L) != 0L)
		clean("Can't open serial device");
	serialopen++;
	serin.IOSer.io_Message.mn_ReplyPort = serinp;
	serout = serin;
	serout.IOSer.io_Message.mn_ReplyPort = seroutp;
	asy[dev].buflen = bufsize;
	/* alloc input buffer */
	if((asy[dev].input_buffer = malloc(asy[dev].buflen)) == NULL)
		clean("Can't allocate serial input buf");
	serin.IOSer.io_Data = (APTR) asy[dev].input_buffer;
	serin.IOSer.io_Length = asy[dev].buflen;
	asy[dev].input_len = 0;		/* clear input buffer */
	serin.io_SerFlags = SERF_XDISABLED|SERF_EOFMODE|SERF_RAD_BOOGIE;
	serin.IOSer.io_Flags = 0;
	SendIO(&serin);
	serout.IOSer.io_Data = (APTR) serinitstr;
	serout.IOSer.io_Length = serinitlen;
	serout.IOSer.io_Command = CMD_WRITE;
	serout.IOSer.io_Flags = 0;
	serout.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
	DoIO(&serout);
}

int
asy_stop(iface)
struct interface *iface;
{
	if (iface->dev >= ASY_MAX) {
		fprintf(stderr, "asy_stop: bad dev %d\n", iface->dev);
		return;
	}
	AbortIO(&serin);
	AbortIO(&serout);
	CloseDevice(&serin);
	free(asy[iface->dev].input_buffer);	/* release buffer */
	serialopen--;
}

/* Set asynch line speed */
int
asy_speed(dev,speed)
int dev;
int speed;
{
	if (serialopen == 0)
		return;

	AbortIO(&serin);
	WaitIO(&serin);
	(void) GetMsg(serinp);
	asy[dev].speed = serin.io_Baud = speed;
	serin.io_ReadLen = 8;
	serin.io_WriteLen = 8;
	serin.io_StopBits = 1;
	serin.io_TermArray.TermArray0 =	serin.io_TermArray.TermArray1 = 
	   (ULONG)FR_END << 24 | (ULONG)FR_END << 16 | FR_END << 8 | FR_END;
	serin.IOSer.io_Command = SDCMD_SETPARAMS;
	serin.IOSer.io_Flags = 0;
	DoIO(&serin);
	if (serin.IOSer.io_Error)
		printf("Bad I/O status %d on SETPARAMS\n",
					serin.IOSer.io_Error);

	serin.IOSer.io_Command = CMD_READ;
	serin.IOSer.io_Data = (APTR) asy[dev].input_buffer;
	serin.IOSer.io_Length = asy[dev].buflen;
	asy[dev].input_len = 0;		/* clear input buffer */
	serin.io_SerFlags = SERF_XDISABLED|SERF_EOFMODE|SERF_RAD_BOOGIE;
	serin.IOSer.io_Flags = 0;	/* no quick I/O */
	SendIO(&serin);
}

/* Send a buffer to serial transmitter */
asy_output(dev,buf,cnt)
unsigned dev;
char *buf;
unsigned short cnt;
{
	/*
	 *  We 'know' that the transmitter is ready since we would not have
	 *  been called unless the previous I/O has been completed.
	 */
	WaitIO(&serout);
	GetMsg(seroutp);
	serout.IOSer.io_Data = (APTR) buf;
	serout.IOSer.io_Length = cnt;
	serout.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
	serout.IOSer.io_Flags = 0;	/* no quick I/O */
	serout.IOSer.io_Command = CMD_WRITE;
	SendIO(&serout);
}

/* Read characters from the keyboard, translating them to "real" ASCII
 * If none are ready, return the -1 from kbraw()
 */
kbread()
{
	char c;

	if (CheckIO(&consin)) {
		WaitIO(&consin);
		(void) GetMsg(consinp);
		c = InputCharacter;
		consin.io_Length = 1;
		consin.io_Data = (APTR) &InputCharacter;
		consin.io_Command = CMD_READ;
		SendIO(&consin);		/* start next read up */
		return (c & 0xff);
	}

	return -1;		/* nuthin here */
}

/* Receive characters from asynch line
 * Returns count of characters read
 */
unsigned
asy_recv(dev,buf,cnt)
int dev;
char *buf;
unsigned cnt;
{
	register int actual = 0;
	register long error;

	if (asy[dev].input_len == 0) {		/* if buffer is empty.. */
		if (CheckIO(&serin) == NULL)	/* see if I/O has completed */
			return 0;		/* nope, not yet. */
		if (error = WaitIO(&serin))
			printf("(SERIN) WaitIO returns %d\n", error);
		if (serin.IOSer.io_Error)
			printf("Bad I/O stat %d (SERIN)\n",
					serin.IOSer.io_Error);
		(void) GetMsg(serinp);
		/* input has completed.  fill in state variables */
		asy[dev].input_len = serin.IOSer.io_Actual;
		asy[dev].input_p = asy[dev].input_buffer;
#ifdef	TRACE
		if (trace & 0x40000000) {
			int a, n, l = asy[dev].input_len;
			unsigned char *b = asy[dev].input_buffer;

			a = 0;
			printf("Raw serial input:\r\n");
			while (l) {
				n = min(l, 16);
				fmtline(a, b, n);
				a += n;
				b += n;
				l -= n;
			}
			fflush(stdout);
		}
#endif			
	}

	if (asy[dev].input_len) {	/* any chars in buffer left?  */
		actual = min(asy[dev].input_len, cnt);
		if (actual == 1)
			*buf = *asy[dev].input_p;	/* usual case */
		else
			movmem(asy[dev].input_p, buf, actual);
		asy[dev].input_len -= actual;
		asy[dev].input_p += actual;
	}

	if (asy[dev].input_len == 0) {		/* if buffer is now empty */
		serin.IOSer.io_Command = CMD_READ;
		serin.IOSer.io_Data = (APTR) asy[dev].input_buffer;
		serin.IOSer.io_Length = asy[dev].buflen;
		serin.io_SerFlags = 
			SERF_XDISABLED|SERF_EOFMODE|SERF_RAD_BOOGIE;
		serin.IOSer.io_Flags = 0;	/* no quick I/O */
		SendIO(&serin);
	}

	return actual;
}

int
stxrdy(dev)
{
	return (CheckIO(&serout) != NULL);
}

/* Create a directory listing in a temp file and return the resulting file
 * descriptor. If full == 1, give a full listing; else return just a list
 * of names.
 *
 * This function is very dependent on the workings of Aztec standard I/O;
 * it uses their mechanism for generating and deleting temporary files.
 */
FILE *
dir(path,full)
char *path;
int full;
{
	/*return (FILE *)NULL;*/
	return(0L);
}

#if	0
bcmp(a,b,n)
register char *a,*b;
register int16 n;
{
	while(n-- != 0){
		if(*a++ != *b++)
			return 1;
	}
	return 0;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > amigadev.c
/*
 *  Copyright (C) 1987
 *  Louis A. Mamakos  WA3YMH
 *  All rights reserved.
 *
 *  This code may not be redistributed, sold, included on any collection of
 *  software which is sold.  Use of this software is restricted to inclusion
 *  in the KA9Q TCP/IP software package for use on a Commodore-Amiga system.
 *  Commercial use is prohibited.  Only educational and Amateur Packet Radio
 *  use is allowed.
 */

#ifdef	AMIGADEVDRV
/*
 *  This module is the meat of the Amiga 'internet.device' device driver.  There
 *  are assembly language stubs in devstub.asm that call this module when user
 *  program access the device driver.  Remember: the tasks running this code are
 *  not our own!
 */

#include <stdio.h>

/* Amiga system definitions */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <exec/errors.h>


/* get definitions of KA9Q TCP/IP protocol stuff... */

#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "icmp.h"
#include "ip.h"
#include "tcp.h"
#include "trace.h"
#include "session.h"
/* device driver specific definitions */
#define ListEmpty(x)	(! ((x)->lh_Head->ln_Succ))

#include "inetdev.h"
#ifdef TRACE 
#define tracedev(x) \
	if (trace & TRACE_DEVICE) printf(x)

#define tracedev2(x,y) \
	if (trace & TRACE_DEVICE) printf(x,y)

#define tracedev3(x,y,z) \
	if (trace & TRACE_DEVICE) printf(x,y,z)

#define tracedev4(x,y,z,zz) \
	if (trace & TRACE_DEVICE) printf(x,y,z,zz)
#endif
char *malloc();

extern void DSClose(), DSBeginIO(), DSAbortIO();
extern struct InternetBase *DSOpen();
extern long DSExpunge();

void indev_tcp_r_upcall(), indev_tcp_t_upcall(), indev_s_upcall();
struct SignalSemaphore INLock;
struct Library *MakeLibrary();

/* for open requests */
int nopens; /* from iface */
struct IOINETReq *iob = NULL;
int unit_spec;
struct InternetBase * dev;
int OpenIt = 0, 
    CN1 = 0,
    IOpenedIt = 0;

extern int DeviceSignal;
extern struct Process *mytask;

printlist(l)
struct List *l;
{
  printf("head %x tail %x tailpred %x\n", l->lh_Head, l->lh_Tail, 
		l->lh_TailPred);
}
/*
 *  Initialize and install the Amiga 'internet.device'.
 */
void
DriverInit()
{
	char *foo[10];
	int success;
	static int WeWereHere;
	int x;
	tracedev("DriverInit");
	x = WeWereHere; 
	WeWereHere = 1;
	if (x)
	  {
		printf("you tried to add the driver twice!!!\n");
		return;
	  }
	foo[0] = (char *) &DSOpen;
	foo[1] = (char *) &DSClose;
	foo[2] = (char *) &DSExpunge;
	foo[3] = (char *) NULL;
	foo[4] = (char *) &DSBeginIO;
	foo[5] = (char *) &DSAbortIO;
		/* add any other custom routines here */
	foo[6] = (char *) -1;

	InternetBase = (struct InternetBase *)
		MakeLibrary(&foo[0], (char *) NULL, (char *) NULL,
			(long) sizeof(struct InternetBase), (char *) NULL);

	if (InternetBase == (struct InternetBase *) 0) {
		/* display alert? */
		return;
	}

	InitSemaphore(&(INLock));
	ObtainSemaphore(&(INLock));
	InitSemaphore(&(InternetBase->ib_lock));
	InternetBase->ib_lock.ss_Link.ln_Pri = 0;
	InternetBase->ib_lock.ss_Link.ln_Name = "internet.device lock";

	NewList(&InternetBase->ib_Units);
	InternetBase->ib_Units.lh_Type = NT_UNKNOWN;

	InternetBase->lib.lib_Node.ln_Type = NT_DEVICE;
	InternetBase->lib.lib_Node.ln_Pri = 0;
	InternetBase->lib.lib_Node.ln_Name = "internet.device";
	InternetBase->lib.lib_Flags = LIBF_CHANGED | LIBF_SUMUSED;
	InternetBase->lib.lib_Version = IN_VERSION;
	InternetBase->lib.lib_Revision = IN_REVISION;
	InternetBase->lib.lib_IdString =
			 (APTR) "internet.device   23 May 1987\r\n";

	success = AddDevice(InternetBase);
	Savea4();
	OpenIt = 0;
	IOpenedIt = 0;
	nopens = 1;
	CN1 = 5;
	if (success != 0)
          myoserr("driver open");
	printf("driver added returned %d\n",success);

}

void
DriverShutdown()
{
	long error;
	extern long *RemoveDevice();

	if (!InternetBase)
		return;

	if (error = RemDevice(InternetBase))
		printf("Can't remove device: error %ld\n", error);

}

struct InternetBase *
NetDevOpen(mdev, munit_spec, miob, mflags)
	struct InternetBase *mdev;
	struct IOINETReq *miob;
	ULONG munit_spec, mflags;
{

	iob = miob;
	dev = mdev;
	unit_spec = munit_spec;

	if (IOpenedIt == 1)
	  {
	    CN1++;
	    return NULL;
	  }
	OpenIt = 1;
	Permit();
	while (IOpenedIt == 0);
	Forbid();
	IOpenedIt = 0;
	return dev;
}
check_driver()
{
	register struct INET_Unit *unit;
	register struct tcb *tcb;
	if (OpenIt != 0)
        {

	printf("open request!\n");
	
	switch (unit_spec) {
		case INET_UNIT_TCP:
		case INET_UNIT_UDP:
			break;
		default:
			iob->io_Error = IOERR_OPENFAIL;
			iob->io_Device = NULL;
			iob->io_Unit = NULL;
			OpenIt = 0;
			IOpenedIt = 1;
			goto doneopen;
	}

	if ((unit = (struct INET_Unit *)
			malloc(sizeof(struct INET_Unit))) == NULL) {
		iob->io_Error = IOERR_OPENFAIL;
		OpenIt = 0;
		IOpenedIt = 1;
		goto doneopen;
	}
	tracedev2("malloc ok %x\n", unit);
	iob->io_Unit = unit;
	iob->io_Device = (struct Device *)dev;
	dev->lib.lib_OpenCnt++;

	unit->iu_Unit.ln_Type = NT_UNKNOWN;
	unit->iu_Unit.ln_Pri = 0;

	NewList(&unit->iu_Input);
	NewList(&unit->iu_Output);
printf("newlist ok\n");
	unit->iu_Input.lh_Type = NT_UNKNOWN;	/* gee, what do we really */
	unit->iu_Output.lh_Type = NT_UNKNOWN;	/* call these... */
	unit->iu_user = iob->io_Offset;		/* always returned in Offset */
	unit->iu_Act_Input = NULL;
	unit->iu_Act_Output = NULL;
	iob->io_lsocket.address = ip_addr;
	iob->io_lsocket.port = lport++;	
	AddTail(&InternetBase->ib_Units, &unit->iu_Unit);
	/* perform protocol specific open functions */
	printf("addtail\n");

	switch (unit_spec) {
	case INET_UNIT_TCP:
/*		Forbid(); */
		tcb = open_tcp(&(iob->io_lsocket), &(iob->io_fsocket),
			(USHORT) iob->io_Offset, (USHORT) iob->io_TCP_Window,
			indev_tcp_r_upcall, indev_tcp_t_upcall, indev_s_upcall,
			iob->io_INET_TOS, (char *)unit);

/*		Permit();*/
		if (tcb == NULL)
			goto fail;
		unit->iu_Unit.ln_Name = "TCP Connection";
		unit->iu_type = INET_UNIT_TCP;
		unit->iu_ccb = tcb;
printf("cpopen is %d\n", tcb);
		break;

	default:
	   fail:
		iob->io_Error = IOERR_OPENFAIL;
		dev->lib.lib_OpenCnt--;
		Remove(unit);
		free(unit);
		OpenIt = 0;
		IOpenedIt = 1;
		goto doneopen;
	}
        tracedev2("dev is %d\n", dev);
	OpenIt = 0;
	IOpenedIt = 1;
  }
doneopen:
  /* spin until that other guy is all done. We will not get through
   * this spin until the other guy has done a Forbid() and
   * then a Permit(), since the IopenedIt gets cleared 
   * AFTER the Forbid(). Sorry i do not use semaphores but
   * i do not have 1.2 autodocs so am not totally up on their
   * use.
   */
  while (IOpenedIt);
}
CheckTcp()
{
  struct Node *head = InternetBase->ib_Units.lh_Head;

  struct INET_Unit *unit = (struct INET_Unit *) head;
  struct tcb *tcb;
  /* let the other guys in */
  ReleaseSemaphore(&(INLock));
  eihalt();
  ObtainSemaphore(&(INLock));
  tracedev("start checktcp\n");
  tracedev4("heda %x Pred is %x Succ is %x\n", head,head->ln_Pred, head->ln_Succ);
  for (;unit->iu_Unit.ln_Succ;unit = unit->iu_Unit.ln_Succ)
    {
      tracedev3("checktcp: %x Succ %d\n", unit, unit->iu_Unit.ln_Succ);
      if (unit->iu_type != INET_UNIT_TCP)
        {
          tracedev("not a tcp\n");
          continue;
        }      
      tcb = (struct tcb *) unit->iu_ccb;
      if (tcb == NULL)
        {
          tracedev("NULL tcb in unit\n");  
          continue;
        }
      if (tcb->state == ESTABLISHED)
        {
          tracedev("unit state is established!\n");
/*          continue;*/
        }
     tracedev("do the upcall\n");
     do_tupcall(tcb, 512); /* for now- it wil do the right thing */
     if (tcb->rcvcnt > 0)
       do_rupcall(tcb, tcb->rcvcnt);
  }
  tracedev("done checktcp\n");
/*  ReleaseSemaphore(&(INLock));*/
}
void DevClose(dev, iob)
	struct InternetBase *dev;
	struct IOINETReq *iob;
{
	register struct INET_Unit *unit;
        struct tcb *tcb;

        unit = iob->io_Unit;
        tcb = unit->iu_ccb;
	del_tcp(tcb);
	Remove(unit);
	free(unit);
	iob->io_Unit = NULL;

	iob->io_Device = (struct Device *)dev;
	dev->lib.lib_OpenCnt--;

	/* remove iu_Unit from ib_Units list */
	/* decrement library use count */
	/* free unit structure */
	/* delete TCP/UDP connection del_tcp()/del_udp() */
}

long DevExpunge(dev)
	struct InternetBase *dev;
{
	register char *m;
	register long len;


	if (InternetBase->lib.lib_OpenCnt) {
		InternetBase->lib.lib_Flags |= LIBF_DELEXP;
		return 0;
	}
	Remove(InternetBase);	/* remove from library list */
	len = InternetBase->lib.lib_NegSize + InternetBase->lib.lib_PosSize;
	m = (char *) ((ULONG)InternetBase - InternetBase->lib.lib_NegSize);
	FreeMem(m, len);
	return 0;
}

#define	C_IMMED		(1<<0)
#define	C_READ		(1<<1)
#define	C_WRITE		(1<<2)
void cmd_Invalid(), cmd_Reset(), cmd_Read(), cmd_Write(), cmd_Update(),
	cmd_Clear(), cmd_Stop(), cmd_Start(), cmd_Flush(), PerformIO();

struct Commands {
	void	(*cmd_func)();
	int	cmd_flags;
} commands [] = {
	{ cmd_Invalid, 	C_IMMED		},	/* invalid	*/
	{ cmd_Reset,	C_IMMED		},	/* CMD_RESET	*/
	{ cmd_Read,	C_READ		},	/* CMD_READ	*/
	{ cmd_Write,	C_WRITE		},	/* CMD_WRITE	*/
	{ cmd_Update,	C_WRITE 	},	/* CMD_UPDATE	*/
	{ cmd_Clear,	C_IMMED		},	/* CMD_CLEAR	*/
	{ cmd_Stop,	C_IMMED		},	/* CMD_STOP	*/
	{ cmd_Start,	C_IMMED		},	/* CMD_START	*/
	{ cmd_Flush,	C_IMMED		},	/* CMD_FLUSH	*/
};

/*  define last valid command */
#define	MAX_IO_COMMAND	CMD_FLUSH


/* BeginIO is called to begin processing of the I/O request */

void DevBeginIO(iob, dev)
	struct IOINETReq *iob;
	struct InternetBase *dev;
{
	register struct Commands *cmd;
	register struct INET_Unit *unit = iob->io_Unit;
	ObtainSemaphore(&(INLock));
	if (iob->io_Command > MAX_IO_COMMAND) {
		cmd_Invalid(iob, iob->io_Unit);
		goto done;
	}
	tracedev("io. ObtainSme\n");

	tracedev("got it\n");
	cmd = &commands[iob->io_Command];
	tracedev2("cmd is %d\n",iob->io_Command);
	tracedev2("flags %d\n", iob->io_Flags);
	if ((cmd->cmd_flags & C_IMMED) == 0) {

		/*
		 *  Code for commands which can queue
		 */

		if ((cmd->cmd_flags & C_READ)/* && (unit->iu_Act_Input)*/) {
			AddTail(&unit->iu_Input, iob);
			iob->io_Flags &= ~IOF_QUICK;
			iob->io_Message.mn_Node.ln_Type = NT_MESSAGE;
			tracedev3("added %d to read queue of unit %d\n", iob, unit);
			goto done;
		}

		if ((cmd->cmd_flags & C_WRITE)/* && (unit->iu_Act_Output)*/) {
			AddTail(&unit->iu_Output, iob);
			iob->io_Flags &= ~IOF_QUICK;
			iob->io_Message.mn_Node.ln_Type = NT_MESSAGE;
			tracedev3("added %d to write queue of unit %d\n", iob, unit);
			goto done;
		}
	}
	PerformIO(iob, unit);
done: 	tracedev4("flags QUI %x ~QUI %x  %d\n", IOF_QUICK, ~IOF_QUICK,
			iob->io_Flags);
  Signal(mytask, DeviceSignal);
  ReleaseSemaphore(&(INLock));
}

void DevAbortIO(iob, dev)
	struct IOINETReq *iob;
	struct InternetBase *dev;
{
	printf("DevAbortIo\n");
}

void
PerformIO(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = 0;
	iob->io_Actual = 0;
	(*commands[iob->io_Command].cmd_func)(iob, unit);
}

void
TermIO(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	struct tcb *tcb;
        tcb = (struct tcb *) unit->iu_ccb;
	iob->io_OldState = iob->io_State;
	iob->io_State = tcb->state;
	if ((iob->io_Flags & IOF_QUICK) == 0)
		/* not quick I/O */
		ReplyMsg(&iob->io_Message);
}

void
cmd_Invalid(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}

void
cmd_Reset(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}

void
cmd_Read(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}

void
cmd_Write(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}

void
cmd_Update(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}

void
cmd_Clear(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}


void
cmd_Stop(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}


void
cmd_Start(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}


void
cmd_Flush(iob, unit)
	struct IOINETReq *iob;
	struct INET_Unit *unit;
{
	iob->io_Error = IOERR_NOCMD;
	TermIO(iob, unit);
}


/* TCP receiver upcall routine.  Called with TCB pointer and number of bytes
   available */


do_rupcall(tcb, cnt)
	struct tcb *tcb;
	int16 cnt;
{
	struct mbuf *bp;
	register struct INET_Unit *unit = (struct INET_Unit *) tcb->user;
	int amount, recamount;
	struct IOINETReq *iob;
/*	ObtainSemaphore(&(INLock));*/
	if (ListEmpty(&(unit->iu_Input)))
	  goto done;
	iob = unit->iu_Act_Input = unit->iu_Input.lh_Head;
	tracedev4("dev rupcall iob %d unit %d tcb%d\n",iob, unit, tcb);
	if (iob != NULL)
	  {
	    Remove(iob);
            amount = min(cnt, iob->io_Length);
	    tracedev3("call recv_tcp %d bytes avail %d\n", amount, cnt);
	    recamount = recv_tcp(tcb, &bp, amount);
	    iob->io_Actual = dqdata(bp, iob->io_Data, recamount);
	    tracedev2("recv_tcp after got %d bytes\n", iob->io_Actual);
	    TermIO(iob, unit);
/*	    ReplyMsg(&(iob->io_Message));*/
	  }
done:	
/*  ReleaseSemaphore(&(INLock));        */
}
/* TCP receiver upcall routine.  Called with TCB pointer and number of bytes
   available */

void
indev_tcp_r_upcall(tcb, cnt)
	struct tcb *tcb;
	int16 cnt;
{
/*	ObtainSemaphore(&(INLock));*/
	do_rupcall(tcb, 512);
/*  ReleaseSemaphore(&(INLock));        */
}

/* TCP transmitter upcall routine.  Called with TCB pointer and number of bytes
   free in send window */


do_tupcall(tcb, avail)
	struct tcb *tcb;
	int16 avail;
{
	struct mbuf *bp, *qdata();
	register struct INET_Unit *unit = (struct INET_Unit *) tcb->user;
	int amount;
	struct IOINETReq *iob;

	if (ListEmpty(&(unit->iu_Output)))
	  goto done;
	tracedev("non-empty Output\n");
	iob = unit->iu_Act_Output = unit->iu_Output.lh_Head;
	tracedev4("dev tupcall iob %d unit %d tcb%d\n",iob, unit, tcb);
	if (iob != NULL)
	  {
	    Remove(iob);

            amount = min(avail, iob->io_Length);
	    tracedev3("t_upcall- send_tcp for addr %x %d bytes\n",iob->io_Data,
			amount);
	    bp = qdata(iob->io_Data, amount);
	    iob->io_Actual = send_tcp(tcb, bp);
	    tracedev2("send_tcp after got %d bytes\n", iob->io_Actual);
	    TermIO(iob, unit);
/*	    ReplyMsg(&(iob->io_Message));*/
	    unit->iu_Act_Output = NULL;
	  }
done:


}
void
indev_tcp_t_upcall(tcb, avail)
	struct tcb *tcb;
	int16 avail;
{
/*	ObtainSemaphore(&(INLock));*/
	do_tupcall(tcb, avail);
/*  ReleaseSemaphore(&(INLock));        */

}

void
indev_s_upcall(tcb, old, new)
	struct tcb *tcb;
	char old, new;
{
	register struct INET_Unit *unit = (struct INET_Unit *) tcb->user;
	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.
	 */

	if(unit != NULL)
		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");
		}
		del_tcp(tcb);
		break;
	default:
		if(notify)
			printf("%s\r\n",tcpstates[new]);
		break;
	}
	fflush(stdout);

}
#endif
SHAR_EOF
cat << \SHAR_EOF > iproute.c
/* Lower half of IP, consisting of gateway routines
 * Includes routing and options processing code
 */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "internet.h"
#include "timer.h"
#include "netuser.h"
#include "ip.h"
#include "icmp.h"
#include "iface.h"
#ifdef	TRACE
#include "trace.h"
#endif

struct route *routes[32][NROUTE];	/* Routing table */
struct route r_default;			/* Default route entry */

int32 ip_addr;
struct ip_stats ip_stats;

/* Route an IP datagram. This is the "hopper" through which all IP datagrams,
 * coming or going, must pass.
 *
 * This router is a temporary hack, since it only does host-specific or
 * default routing (no hierarchical routing yet).
 *
 * "rxbroadcast" is set to indicate that the packet came in on a subnet
 * broadcast. The router will kick the packet upstairs regardless of the
 * IP destination address.
 */
void
ip_route(bp,rxbroadcast)
struct mbuf *bp;
char rxbroadcast;	/* True if packet had link broadcast address */
{
	register struct ip_header *ip;	/* IP header being processed */
	int16 ip_len;			/* IP header length */
	int16 buflen;			/* Length of mbuf */
	int16 length;			/* Total datagram length */
	int32 target;			/* Target IP address */
	int32 gateway;			/* Gateway IP address */
	register struct route *rp;	/* Route table entry */
	struct route *rt_lookup();
	int opi;			/* Index into options field */
	int opt_len;			/* Length of current option */
	int strict;			/* Strict source routing flag */
	struct mbuf *sbp;		/* IP header for fragmenting */
	int16 fl_offs;			/* fl_offs field of datagram */
	int16 offset;			/* Offset of fragment */
	char precedence;		/* Extracted from tos field */
	char delay;
	char throughput;
	char reliability;

	ip_stats.total++;
	buflen = len_mbuf(bp);
	if(buflen < sizeof(struct ip_header)){
		/* The packet is shorter than a legal IP header */
		ip_stats.runt++;
		free_p(bp);
		return;
	}
	ip = (struct ip_header *)bp->data;
	length = ntohs(ip->length);
	if(buflen > length){
		/* Packet has excess garbage (e.g., Ethernet padding); trim */
		if(bp->next == NULLBUF){
			/* One mbuf, just adjust count */
			bp->cnt = length;
		} else {
			struct mbuf *nbp;
			/* Copy to a new one */
			nbp = copy_p(bp,length);
			free((char *)bp);
			bp = nbp;
			ip = (struct ip_header *)bp->data;
		}
	}
	ip_len = lonibble(ip->v_ihl) * sizeof(int32);
	if(ip_len < sizeof(struct ip_header)){
		/* The IP header length field is too small */
		ip_stats.length++;
		free_p(bp);
		return;
	}
	if(cksum(NULLHEADER,bp,ip_len) != 0){
		/* Bad IP header checksum; discard */
		ip_stats.checksum++;
		free_p(bp);
		return;
	}
	if(hinibble(ip->v_ihl) != IPVERSION){
		/* We can't handle this version of IP */
		ip_stats.version++;
		free_p(bp);
		return;
	}
	/* See if it's a broadcast or addressed to us, and kick it upstairs */
	if(ntohl(ip->dest) == ip_addr || rxbroadcast){
#ifdef	GWONLY
	/* We're only a gateway, we have no host level protocols */
		if(!rxbroadcast)
			icmp_output(bp,DEST_UNREACH,PROT_UNREACH,(union icmp_args *)NULL);
		free_p(bp);
#else
#ifdef	TRACE
		if(trace & TRACE_SELF && ntohl(ip->source) == ip_addr){
			printf("loopback:\r\n");
			if((trace & TRACE_HDR) > 2)
				ip_dump(bp);
			if(trace & TRACE_DUMP)
				hexdump(bp);
			fflush(stdout);
		}
#endif
		ip_recv(bp,rxbroadcast);
#endif
		return;
	}
	/* If we get here, we must forward the packet.
	 * Process options, if any. Also compute length of secondary IP
	 * header in case fragmentation is needed later
	 */
	strict = 0;
	for(opi = sizeof(struct ip_header);opi < ip_len; opi += opt_len){
		char *opt;	/* Points to current option */
		int opt_type;	/* Type of current option */
		int pointer;	/* Pointer field of current option */
		int32 *addr;	/* Pointer to an IP address field in option */

		opt = (char *)ip + opi;
		opt_type = opt[0] & OPT_NUMBER;

		/* Handle special 1-byte do-nothing options */
		if(opt_type == IP_EOL)
			break;		/* End of options list, we're done */
		if(opt_type == IP_NOOP){
			opt_len = 1;	/* No operation, skip to next option */
			continue;
		}
		/* Other options have a length field */
		opt_len = opt[1] & 0xff;

		/* Process options */
		switch(opt_type){
		case IP_SSROUTE:/* Strict source route & record route */
			strict = 1;
		case IP_LSROUTE:/* Loose source route & record route */
			/* Source routes are ignored unless the datagram appears to
			 * be for us
			 */
			if(ntohl(ip->dest) != ip_addr)
				continue;
		case IP_RROUTE:	/* Record route */
			pointer = (opt[2] & 0xff) - 1;
			if(pointer + sizeof(int32) <= opt_len){
				/* Insert our address in the list */
				addr = (int32 *)&opt[pointer];
				if(opt_type != IP_RROUTE)
					/* Old value is next dest only for source routing */
					ip->dest = *addr;
				*addr = htonl(ip_addr);
				opt[2] += 4;
			} else {
				/* Out of space; return a parameter problem and drop */
				union icmp_args icmp_args;

				icmp_args.unused = 0;
				icmp_args.pointer = sizeof(struct ip_header) + opi;
				icmp_output(bp,PARAM_PROB,0,&icmp_args);
				free_p(bp);
				return;
			}
			break;
		}
	}
	/* Decrement TTL and discard if zero */
	if(--ip->ttl == 0){
		/* Send ICMP "Time Exceeded" message */
		icmp_output(bp,TIME_EXCEED,0,NULLICMP);
		free_p(bp);
		return;
	}
	/* Note this address may have been modified by source routing */
	target = ntohl(ip->dest);

	/* Look up target address in routing table */
	if((rp = rt_lookup(target)) == NULLROUTE){
		/* No route exists, return unreachable message */
		icmp_output(bp,DEST_UNREACH,HOST_UNREACH,NULLICMP);
		free_p(bp);
		return;
	}
	/* Find gateway; zero gateway in routing table means "send direct" */
	if(rp->gateway == (int32)0)
		gateway = target;
	else
		gateway = rp->gateway;

	if(strict && gateway != target){
		/* Strict source routing requires a direct entry */
		icmp_output(bp,DEST_UNREACH,ROUTE_FAIL,NULLICMP);
		free_p(bp);
		return;
	}
	precedence = PREC(ip->tos);
	delay = ip->tos & DELAY;
	throughput = ip->tos & THRUPUT;
	reliability = ip->tos & RELIABILITY;

	if(length <= rp->interface->mtu){
		/* Datagram smaller than interface MTU; send normally */
		/* Recompute header checksum */
		ip->checksum = 0;
		ip->checksum = cksum(NULLHEADER,bp,ip_len);
		(*rp->interface->send)(bp,rp->interface,gateway,
			precedence,delay,throughput,reliability);
		return;
	}
	/* Fragmentation needed */
	fl_offs = ntohs(ip->fl_offs);
	if(fl_offs & DF){
		/* Don't Fragment set; return ICMP message and drop */
		icmp_output(bp,DEST_UNREACH,FRAG_NEEDED,NULLICMP);
		free_p(bp);
		return;
	}
	/* Create copy of IP header for each fragment */
	sbp = copy_p(bp,ip_len);
	pullup(&bp,NULLCHAR,ip_len);
	length -= ip_len;

	/* Create fragments */
	offset = (fl_offs & F_OFFSET) << 3;
	while(length != 0){
		int16 fragsize;		/* Size of this fragment's data */
		struct mbuf *f_header;	/* Header portion of fragment */
		struct ip_header *fip;	/* IP header */
		struct mbuf *f_data;	/* Data portion of fragment */

		f_header = copy_p(sbp,ip_len);
		fip = (struct ip_header *)f_header->data;
		fip->fl_offs = htons(offset >> 3);
		if(length + ip_len <= rp->interface->mtu){
			/* Last fragment; send all that remains */
			fragsize = length;
		} else {
			/* More to come, so send multiple of 8 bytes */
			fragsize = (rp->interface->mtu - ip_len) & 0xfff8;
			fip->fl_offs |= htons(MF);
		}
		fip->length = htons(fragsize + ip_len);
		/* Recompute header checksum */
		fip->checksum = 0;
		fip->checksum = cksum(NULLHEADER,f_header,ip_len);

		/* Extract portion of data and link in */
		f_data = copy_p(bp,fragsize);
		pullup(&bp,NULLCHAR,fragsize);
		f_header->next = f_data;

		(*rp->interface->send)(f_header,rp->interface,gateway,
			precedence,delay,throughput,reliability);
		offset += fragsize;
		length -= fragsize;
	}
	free_p(sbp);
}

/* Add an entry to the IP routing table. Returns 0 on success, -1 on failure */
int
rt_add(target,bits,gateway,metric,interface)
int32 target;	/* Target IP address prefix */
unsigned bits;	/* Size of target address prefix in bits (0-32) */
int32 gateway;
int metric;
struct interface *interface;
{
	struct route *rp,**hp,*rt_lookup();
	int16 hash_ip(),i;
	char *malloc();

	if(interface == NULLIF)
		return -1;

	/* Zero bits refers to the default route */
	if(bits == 0){
		rp = &r_default;
	} else {
		if(bits > 32)
			bits = 32;

		/* Mask off don't-care bits */
		for(i=31;i >= bits;i--)
			target &= ~(0x80000000 >> i);

		/* Search appropriate chain for existing entry */
		for(rp = routes[bits-1][hash_ip(target)];rp != NULLROUTE;rp = rp->next){
			if(rp->target == target)
				break;
		}
	}
	if(rp == NULLROUTE){
		/* The target is not already in the table, so create a new
		 * entry and put it in.
		 */
		if((rp = (struct route *)malloc(sizeof(struct route))) == NULLROUTE)
			return -1;	/* No space */
		/* Insert at head of table */
		rp->prev = NULLROUTE;
		hp = &routes[bits-1][hash_ip(target)];
		rp->next = *hp;
		if(rp->next != NULLROUTE)
			rp->next->prev = rp;
		*hp = rp;
	}
	rp->target = target;
	rp->gateway = gateway;
	rp->metric = metric;
	rp->interface = interface;
	return 0;
}

/* Remove an entry from the IP routing table. Returns 0 on success, -1
 * if entry was not in table.
 */
int
rt_drop(target,bits)
int32 target;
unsigned bits;
{
	register struct route *rp;
	struct route *rt_lookup();
	unsigned i;

	if(bits == 0){
		/* Nail the default entry */
		r_default.interface = NULLIF;
		return 0;
	}
	if(bits > 32)
		bits = 32;

	/* Mask off don't-care bits */
	for(i=31;i > bits;i--)
		target &= ~(0x80000000 >> i);

	/* Search appropriate chain for existing entry */
	for(rp = routes[bits-1][hash_ip(target)];rp != NULLROUTE;rp = rp->next){
		if(rp->target == target)
			break;
	}
	if(rp == NULLROUTE)
		return -1;	/* Not in table */

	if(rp->next != NULLROUTE)
		rp->next->prev = rp->prev;
	if(rp->prev != NULLROUTE)
		rp->prev->next = rp->next;
	else
		routes[bits-1][hash_ip(target)] = rp->next;

	free((char *)rp);
	return 0;
}

/* Compute hash function on IP address */
static int16
hash_ip(addr)
register int32 addr;
{
	register int16 ret;

	ret = hiword(addr);
	ret ^= loword(addr);
	ret %= NROUTE;
	return ret;
}
#ifndef	GWONLY
/* Given an IP address, return the MTU of the local interface used to
 * reach that destination. This is used by TCP to avoid local fragmentation
 */
int16
ip_mtu(addr)
int32 addr;
{
	register struct route *rp;
	struct route *rt_lookup();

	rp = rt_lookup(addr);
	if(rp != NULLROUTE && rp->interface != NULLIF)
		return rp->interface->mtu;
	else
		return 0;
}
#endif
/* Look up target in hash table, matching the entry having the largest number
 * of leading bits in common. Return default route if not found;
 * if default route not set, return NULLROUTE
 */
static struct route *
rt_lookup(target)
int32 target;
{
	register struct route *rp;
	int16 hash_ip();
	unsigned bits;

	for(bits = 32;bits != 0; bits--){
		if(bits != 32)
			target &= ~(0x80000000 >> bits);
		for(rp = routes[bits-1][hash_ip(target)];rp != NULLROUTE;rp = rp->next){
			if(rp->target == target)
				return rp;
		}
	}
	if(r_default.interface != NULLIF)
		return &r_default;
	else
		return NULLROUTE;
}
/* Internet checksum routines
 * Improved portability courtesy Rick Spanbauer, WB2CFV
 */
#define SLOWCHECK
#ifdef SLOWCHECK
/*
 * Word aligned linear buffer checksum routine.  Called from mbuf checksum
 * routine with simple args.  Intent is that this routine may be replaced
 * by assembly language routine for speed if so desired.
 */
static int16
lcsum(sum, wp, len)
register int32 sum;
register int16 *wp;
int16 len;
{
	register int16 csum;

	while(len-- != 0)
		sum += *wp++;
	while((csum = sum >> 16) != 0)
		sum = csum + (sum & 0xffff);
	return sum & 0xffff;
}
#endif SLOWCHECK

/* Perform end-around-carry adjustment */
static int16
eac(sum)
register int32 sum;	/* Carries in high order 16 bits */
{
	register int16 csum;

	while((csum = sum >> 16) != 0)
		sum = csum + (sum & 0xffff);
	return sum;	/* Chops to 16 bits */
}
/* Checksum a mbuf chain, with optional pseudo-header */
int16
cksum(ph,m,len)
struct pseudo_header *ph;
register struct mbuf *m;
int16 len;
{
	register unsigned int cnt, total;
	register int32 sum, csum;
	register unsigned char *up;

	sum = 0l;

	/* Sum pseudo-header, if present */
	if(ph != NULLHEADER){
		sum = hiword(ph->source);
		sum += loword(ph->source);
		sum += hiword(ph->dest);
		sum += loword(ph->dest);
		sum += ph->protocol & 0xff;
		sum += ph->length;
		/* Swapping the sum is equivalent to summing the swapped
		 * elements, but faster. Do end-around-carry first.
		 */
		sum = htons(eac(sum));
	}
	/* Now do each mbuf on the chain */
	for(total = 0; m != NULLBUF && total < len; m = m->next) {
		cnt = min(m->cnt, len - total);
		up = (unsigned char *)m->data;

		/* Handle odd leading byte */
		if(((long)up) & 1){
			csum = (int16)ntohs(*up++);
			cnt--;
		} else
			csum = 0;

		/* Handle odd trailing byte */
		if(cnt & 1)
			csum += (int16)ntohs(up[--cnt]<<8);

		if(cnt != 0){
			/* Have the primitive checksumming routine do most of
			 * the work. At this point, up is guaranteed to be on
			 * a short boundary and cnt is guaranteed to be even
			 */
			csum = lcsum(csum, (unsigned short *)up, cnt >> 1);
		}
		/* If the mbuf we just did wasn't on a word boundary within
		 * the whole packet, then byteswap the checksum for this mbuf
		 */
		if((total&1) ^ (((long)m->data)&1)){
			csum = eac(csum);
			csum = (csum >> 8) + ((csum&0xff) << 8);
		}
		sum += csum;
		total += m->cnt;
	} 
	/* Do final end-around carry, complement and return */
	return ~eac(sum) & 0xffff;
}
#ifdef	TRACE
#include "trace.h"

void
ip_dump(bp)
struct mbuf *bp;
{
	void tcp_dump(),udp_dump(),icmp_dump();
	register struct ip_header *ip;
	int32 source,dest;
	int16 ip_len;
	int16 length;
	struct mbuf *tbp;
	int16 offset;
	int i;
	int check;
	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;

	ip = (struct ip_header *)bp->data;
	ip_len = lonibble(ip->v_ihl) * sizeof(int32);
	length = ntohs(ip->length);
	offset = (ntohs(ip->fl_offs) & F_OFFSET) << 3 ;
	source = ntohl(ip->source);
	dest = ntohl(ip->dest);
	printf("IP: %s",inet_ntoa(source));
	printf("->%s len %u ihl %u ttl %u prot %u",
		inet_ntoa(dest),length,ip_len,ip->ttl & 0xff,
		ip->protocol & 0xff);

	if(ip->tos != 0)
		printf(" tos %u",ip->tos);
	if(offset != 0 || (ntohs(ip->fl_offs) & MF))
		printf(" id %u offs %u",ntohs(ip->id),offset);

	if(ntohs(ip->fl_offs) & DF)
		printf(" DF");
	if(ntohs(ip->fl_offs) & MF){
		printf(" MF");
		check = 0;	/* Bypass host-level checksum verify */
	} else {
		check = 1;
	}

	if((i = cksum(NULLHEADER,bp,ip_len)) != 0)
		printf(" CHECKSUM ERROR (%u)",i);
	printf("\r\n");

	if((trace & TRACE_HDR) > 3){
		if(offset == 0){
			dup_p(&tbp,bp,ip_len,length - ip_len);
			switch(ip->protocol & 0xff){
			case TCP_PTCL:
				tcp_dump(tbp,source,dest,check);
				break;
			case UDP_PTCL:
				udp_dump(tbp,source,dest,check);
				break;
			case ICMP_PTCL:
				icmp_dump(tbp,source,dest,check);
				break;
			}
			free_p(tbp);
		}
	}
	if(tmpbuf)
		free_p(bp);
	fflush(stdout);
}
/* Dump IP routing table
 * Dest              Length    Interface    Gateway          Metric
 * 192.001.002.003   32        sl0          192.002.003.004       4
 */
int
dumproute()
{
	register unsigned int i,bits;
	register struct route *rp;

	printf("Dest              Length    Interface    Gateway          Metric\r\n");
	if(r_default.interface != NULLIF){
		printf("default           0         %-13s",
		 r_default.interface->name);
		if(r_default.gateway != 0)
			printf("%-17s",inet_ntoa(r_default.gateway));
		else
			printf("%-17s","");
		printf("%6u\r\n",r_default.metric);
	}
	for(bits=1;bits<=32;bits++){
		for(i=0;i<NROUTE;i++){
			for(rp = routes[bits-1][i];rp != NULLROUTE;rp = rp->next){
				printf("%-18s",inet_ntoa(rp->target));
				printf("%-10u",bits);
				printf("%-13s",rp->interface->name);
				if(rp->gateway != 0)
					printf("%-17s",inet_ntoa(rp->gateway));
				else
					printf("%-17s","");
				printf("%6u\r\n",rp->metric);
			}
		}
	}
	return 0;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > telnetp.c
#include <stdio.h>
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <exec/errors.h>
#include <proto/exec.h>
#include <devices/console.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <intuition/intuition.h>
#include <dos.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"
#include "inetdev.h"
#include "inetlib.h"
#define DEBUG
struct Process *mytask;
APTR	oldwindowptr;
struct IntuitionBase *IntuitionBase;
char banner[80] = "telnet window";
static struct NewWindow nw = {
	0, 0, 640, 200,		/* left, top, (max) width, (max) height */
	0, 1,			/* detail pen, block pen */
	0,			/* IDCMP flags */
	SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING |
	    SIZEBBOTTOM | ACTIVATE | NOCAREREFRESH,	/* window flags */
	NULL, NULL,		/* gadget, checkmark */
	(UBYTE *)&banner[0],	/* title of window */
	NULL, NULL,		/* screen, bitmap */
	200, 50, -1, -1,	/* sizing limits */
	WBENCHSCREEN,		/* on the workbench */
};
struct Window *win;
struct MsgPort *keyboard, *consinp, *consoutp, *tcpinp, *tcpoutp;
struct IOStdReq consin, consout;
char InputCharacter;
int deviceopened = 0;
struct IOINETReq tnreq, tninreq, tnoutreq;
char recv[512], snd[512];
struct telnet *tn;
#ifdef LATTICE
extern struct { short error; char *msg; } os_errlist[];
extern int _OSERR, os_nerr;
#endif
static
clean(why)
	char *why;
{
	int i;
	InputCharacter = ' ';
        while (InputCharacter != '<')
	  if (kbread() > 0)
	    amigaputchar(InputCharacter);

	if (win)
		CloseWindow(win);
	if (consinp)
		DeletePort(consinp);
	if (consoutp)
		DeletePort(consoutp);
	if (tcpinp)
		DeletePort(tcpinp);
	if (tcpoutp)
		DeletePort(tcpoutp);
	if (deviceopened)
		CloseDevice(&tnreq);
	mytask->pr_WindowPtr = oldwindowptr;
	if (why) {
           myoserr(why);
	}
	exit(0);
}
printlist(l)
struct List *l;
{
  printf("head %x tail %x tailpred %x\n", l->lh_Head, l->lh_Tail, 
		l->lh_TailPred);
}
myoserr(why)
char *why;
{
  int i;
		fprintf(stderr, "%s: ", why); 
#ifdef LATTICE
		fprintf(stderr, "%d: ", _OSERR);

		for(i = 0; os_errlist[i].error < os_nerr; i++)
		  if (os_errlist[i].error == _OSERR)
			fprintf(stderr, os_errlist[i].msg);
#endif
		fprintf(stderr, "\r\n");
}
/* Called at startup time to set up console I/O, memory heap */
ioinit()
{
	struct Screen *scr;

	mytask = (struct Process *) FindTask((char *) NULL);
	oldwindowptr = mytask->pr_WindowPtr;
	mytask->pr_WindowPtr = (APTR) -1;	/* disable DOS requestors */

	if ((IntuitionBase = (struct IntuitionBase *)
	   OpenLibrary("intuition.library", 33L)) == NULL)
		clean("No intuition: Version 1.2 of Amiga Systems Software required");
	/*
	 *  Try to determine the size of the workbench screen
	 */
	scr = malloc(sizeof(struct Screen));
	if (scr==NULL)
		clean("Can't alloc screen");

	if (GetScreenData(scr, (ULONG) sizeof(struct Screen),
			  WBENCHSCREEN, NULL) == TRUE) {
		nw.Width = scr->Width;
		nw.Height = scr->Height-20;
		nw.TopEdge = 19;
	} else
		fprintf(stderr, "Can't GetScreenData()\n");

	free((char *)scr);
	if ((win = OpenWindow(&nw)) == NULL)
		clean("Can't open window");
	if ((consinp = CreatePort("telnet:console in", 0L)) == NULL)
		clean("Can't create console port");
	if ((tcpinp = CreatePort("telnet:tcp in", 0L)) == NULL)
		clean("Can't create telnet tcp input port");
	if ((tcpoutp = CreatePort("telnet:tcp out", 0L)) == NULL)
		clean("Can't create telnet tcp output port");

	consin.io_Data = (APTR) win;
	consin.io_Length = sizeof(struct Window);

	_OSERR = OpenDevice("console.device", 0L, &consin, 0L);
	if (_OSERR != 0L){
		printf("opendevice returned %d\n", _OSERR);
		myoserr("could not get console");
		clean("Can't open console device");
	}
	consout = consin;

	consin.io_Message.mn_ReplyPort = consinp;
	consin.io_Length = 1;
	consin.io_Data = (APTR) &InputCharacter;
	consin.io_Command = CMD_READ;
	SendIO(&consin);
	consout.io_Message.mn_ReplyPort = consoutp;
	consout.io_Command = CMD_WRITE;


}
/* Read characters from the keyboard, translating them to "real" ASCII
 * If none are ready, return the -1 from kbraw()
 */
kbread()
{
	char c;

	if (CheckIO(&consin)) {
		WaitIO(&consin);
		c = InputCharacter;
		consin.io_Length = 1;
		consin.io_Data = (APTR) &InputCharacter;
		consin.io_Command = CMD_READ;
		SendIO(&consin);		/* start next read up */
		return (c & 0xff);
	}

	return -1;		/* nuthin here */
}
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
/* Telnet receiver upcall routine */
void
rcv_char()
{
/*printf("rcv_char: %d\n", tninreq.io_Actual);*/
	tel_input(tn,tninreq.io_Data, tninreq.io_Actual);

	fflush(stdout);
}
brk()
{
  clean("ok i iwll quit\n");
}
/* TCP connection states */
char *tcpstates[] = {
	"Closed",
	"Listen",
	"SYN sent",
	"SYN received",
	"Established",
	"FIN wait 1",
	"FIN wait 2",
	"Close wait",
	"Closing",
	"Last ACK",
	"Time wait"
};
/* TCP segment header flags */
char *tcpflags[] = {
	"FIN",	/* 0x01 */
	"SYN",	/* 0x02 */
	"RST",	/* 0x04 */
	"PSH",	/* 0x08 */
	"ACK",	/* 0x10 */
	"URG"	/* 0x20 */
};

/* TCP closing reasons */
char *reasons[] = {
	"Normal",
	"Reset",
	"Timeout",
	"ICMP"
};
char old = LISTEN;
int done = 0;
char *hostname="";
int hostport=0;
char *bannerfmt = "telnet %10s %4d %10s";
void
showstate(old, new)
	char old, new;
{


/*	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.
	 */

	sprintf(banner, bannerfmt, hostname, hostport, tcpstates[new]);
	SetWindowTitles(win, banner, -1);
	switch(new){
	case CLOSE_WAIT:
		done = 1;
		break;
	case CLOSED:	/* court adjourned */
/*			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");
*/
		done = 1;
		break;
	default:
		break;
	}
	fflush(stdout);

}

/* Execute user telnet command */
main(argc,argv)
int argc;
char *argv[];
{
	extern int _OSERR;
	struct InternetBase *InternetBase;
	int send_tel();
        int unix_send_tel();
	struct session *s;

/*	struct tcb *tcb = NULL;*/
	struct socket lsocket,fsocket;
	ioinit();
	hostname = argv[1];
	old = LISTEN;
	showstate(old, LISTEN);
	tnreq.io_fsocket.address = aton(argv[1]);
	if(argc < 3)
		tnreq.io_fsocket.port = TELNET_PORT;
	else
		tnreq.io_fsocket.port = atoi(argv[2]);
	tnreq.io_Device = NULL;
	tnreq.io_Unit = NULL;
	tnreq.io_Flags = 0;
	tnreq.io_Error = 0;
	InternetBase = (struct InternetBase *) OpenDevice("internet.device",
				(long) INET_UNIT_TCP, &tnreq, 0L);
	if (InternetBase != 0L){
	  printf("it did not open %d\n",InternetBase);
	  clean("i quit");
	}
/*	tcb = (struct tcb *) tnreq.io_Unit->iu_ccb;*/

	hostport = tnreq.io_lsocket.port;
	deviceopened = 1;
	tninreq = tnreq; /* possible lettuce bug  ?*/
	tnoutreq = tnreq;
	tninreq.io_Length = 512;
	tnoutreq.io_Length = 1;
	tninreq.io_Data = recv;
	tnoutreq.io_Data = &InputCharacter;
	tninreq.io_Command = CMD_READ;
	tnoutreq.io_Command = CMD_WRITE;
	tninreq.io_Message.mn_ReplyPort = tcpinp;
	tnoutreq.io_Message.mn_ReplyPort = tcpoutp;
	/* Create and initialize a Telnet protocol descriptor */
	if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
		myoserr("calloc faiuled\n");
		goto done;
	}
	tn->session = s;	/* Upward pointer */
	tn->state = TS_DATA;
        SendIO(&tninreq);
	onbreak(&brk);
	InputCharacter = ' ';
	while (! done)
	  {
	    if ((snd[0] = kbread()) >= 0){
	      
	      unix_send_tel(snd, (short) 1);}

	    if (CheckIO(&tninreq))
	      {
		chkabort();
		WaitIO(&tninreq);
	        rcv_char();
	        if (tninreq.io_State != old)
	          {
		    showstate(old, tninreq.io_State);
		    old = tninreq.io_State;
	          }
    	        SendIO(&tninreq);
	      }
	  }
done:
	clean("All done");	      

#ifdef NOTDEF
	/* 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;
#endif
}

/* Process typed characters */
int
unix_send_tel(buf,n)
char *buf;
int16 n;
{
	int i;
/*printf("unix_send_tel: buf[0] %d\n", buf[0]);*/
	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;
{
	int i;

	tnoutreq.io_Data = buf;
	tnoutreq.io_Length = n;

	SendIO(&tnoutreq);
/*	printf("now waitio insend_tel: ");*/
	i = WaitIO(&tnoutreq); 
/*printf("send_tel: WaitIo is %d\n", i);*/
	if (tnoutreq.io_State != old)
	  {
	    showstate(old, tnoutreq.io_State);
	    old = tnoutreq.io_State;
	  }

}

/* Process incoming TELNET characters */
int
tel_input(tn,bp, len)
register struct telnet *tn;
char *bp;
int len;
{
	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(len--){
		c = *bp++;
		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;
		}
	}
}

#ifdef NOTDEF
/* 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);
}
#endif
/* Delete telnet structure */
static
free_telnet(tn)
struct telnet *tn;
{

	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;
	tnoutreq.io_Data = s;
	tnoutreq.io_Length = 3;
	DoIO(&tnoutreq);
/*
	bp = qdata(s,(int16)3);
	send_tcp(tn->tcb,bp);
*/
}

#define	BUFMAXCNT	150
static char conbuf[BUFMAXCNT];
static int concnt = 0;

int
amigaputchar(c)
	char c;
{
	conbuf[concnt++] = c;
	if ((c == '\n') || (concnt == BUFMAXCNT))
		amigaflush();
	return c;
}

amigaflush()
{
	if (concnt == 0)
		return;
	consout.io_Data = (APTR) conbuf;
	consout.io_Length = concnt;
	consout.io_Command = CMD_WRITE;
	DoIO(&consout);
	concnt = 0;
}
	
/*
 *  Begin terrible, horrible hack.  All output should be printed upon (into?)
 *  the window we opened before.  Here goes nothing...
 */

printf(a, b, c, d, e, f, g, h, i, j, k)
	char *a;
	int b, c, d, e, f, g, h, i, j, k;
{
	if (concnt)
		amigaflush();

	sprintf(conbuf, a, b, c, d, e, f, g, h, i, j, k);
	consout.io_Data = (APTR) conbuf;
	consout.io_Length = strlen(conbuf);
	consout.io_Command = CMD_WRITE;
	DoIO(&consout);		/* no use in doing this async */
}
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.