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

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

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

[Here is a freely redistributable TCP/IP package for the Amiga, also
known as the KA9Q package.  Executables and documentation are in
comp.binaries.amiga.  To my knowledge, this is the same package that
has been available for some time.  ..Bob]

#	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.h
#	arp.h
#	ax25.h
#	cmdparse.h
#	ether.h
#	ftp.h
#	functions.h
#	icmp.h
#	iface.h
#	inetdev.h
#	inetlib.h
#	internet.h
#	ip.h
#	machdep.h
#	mbuf.h
#	netrom.h
#	netuser.h
#	session.h
#	slip.h
#	smtp.h
#	tcp.h
#	telnet.h
#	timer.h
#	trace.h
#	udp.h
#	cmdparse.c
#	ether.c
#	ftp.c
#	hexload.c
#	makefile
#	misc.c
#	netrom.c
#	netuser.c
#	smisc.c
#	tcptimer.c
#	timer.c
#	tnserv.c
#	ttydriv.c
# This archive created: Fri Mar 17 17:56:19 1989
cat << \SHAR_EOF > amiga.h
#define	ASY_MAX	1	/* one serial port on an amiga */

struct asy {
	unsigned char  *input_buffer;	/* point to input buffer */
	unsigned	buflen;		/* length of recv buffer */
	unsigned char  *input_p;	/* pointer to current byte */
	unsigned	input_len;	/* bytes left */
	unsigned	speed;		/* line speed */
	unsigned	addr;		/* address: we'll use this as unit number */
	unsigned	vec;		/* vector: right. */
};

extern struct asy asy[ASY_MAX];
extern unsigned nasy;
SHAR_EOF
cat << \SHAR_EOF > arp.h
/* Size of ARP hash table */
#define	ARPSIZE	17

/* Lifetime of a valid ARP entry (seconds) */
#define	ARPLIFE		(15*60)	/* 15 minutes */
/* Lifetime of a pending ARP entry (seconds) */
#define	PENDTIME	15	/* 15 seconds */

/* ARP definitions (see RFC 826) */

/* Address size definitions */
#define	IPALEN	4		/* Length in bytes of an IP address */
#define	MAXHWALEN	255	/* Maximum length of a hardware address */

/* ARP opcodes */
#define	ARP_REQUEST	1
#define	ARP_REPLY	2

/* Hardware types */
#define	ARP_ETHER	1	/* Assigned to 10 megabit Ethernet */
#define	ARP_EETHER	2	/* Assigned to experimental Ethernet */
#define	ARP_AX25	3	/* Assigned to AX.25 Level 2 */
#define	ARP_PRONET	4	/* Assigned to PROnet token ring */
#define	ARP_CHAOS	5	/* Assigned to Chaosnet */
#define	ARP_ARCNET	7

/* Table of hardware types known to ARP */
struct arp_type {
	int hwalen;		/* Hardware length */
	int iptype;		/* Hardware type field for IP */
	int arptype;		/* Hardware type field for ARP */
	char *bdcst;		/* Hardware broadcast address */
	int (*format)();	/* Function that formats addresses */
	int (*scan)();		/* Reverse of format */
};
extern struct arp_type arp_type[];

/* Format of an ARP request or reply packet. From p. 3 */
struct arp {
	int16 hardware;			/* Hardware type */
	int16 protocol;			/* Protocol type */
	unsigned char hwalen;		/* Hardware address length, bytes */
	unsigned char pralen;		/* Length of protocol address */
	int16 opcode;			/* ARP opcode (request/reply) */
	char shwaddr[MAXHWALEN];	/* Sender hardware address field */
	int32 sprotaddr;		/* Sender Protocol address field */
	char thwaddr[MAXHWALEN];	/* Target hardware address field */
	int32 tprotaddr;		/* Target protocol address field */
};
		
/* Format of ARP table */
struct arp_tab {
	struct arp_tab *next;	/* Doubly-linked list pointers */
	struct arp_tab *prev;	
	int32 ip_addr;		/* IP Address, host order */
	int16 hardware;		/* Hardware type */
	char *hw_addr;		/* Hardware address */
	char state;		/* (In)complete */
#define	ARP_PENDING	0
#define	ARP_VALID	1
	struct timer timer;	/* Time until aging this entry */
	struct mbuf *pending;	/* Queue of datagrams awaiting resolution */
};
#define	NULLARP	(struct arp_tab *)NULL
extern struct arp_tab *arp_tab[];

struct arp_stat {
	int recv;	/* Total number of ARP packets received */
	int badtype;	/* Incoming requests for unsupported hardware */
	int badlen;	/* Incoming length field(s) didn't match types */
	int inreq;	/* Incoming requests for us */
	int replies;	/* Replies sent */
	int outreq;	/* Outoging requests sent */
};
SHAR_EOF
cat << \SHAR_EOF > ax25.h
/* Control field templates */
#define	I	0x00	/* Information frames */
#define	S	0x01	/* Supervisory frames */
#define	RR	0x01	/* Receiver ready */
#define	RNR	0x05	/* Receiver not ready */
#define	REJ	0x09	/* Reject */
#define	U	0x03	/* Unnumbered frames */
#define	SABM	0x2f	/* Set Asynchronous Balanced Mode */
#define	DISC	0x43	/* Disconnect */
#define	DM	0x0f	/* Disconnected mode */
#define	UA	0x63	/* Unnumbered acknowledge */
#define	FRMR	0x87	/* Frame reject */
#define	UI	0x03	/* Unnumbered information */
#define	PF	0x10	/* Poll/final bit */

/* FRMR reason bits */
#define	W	1	/* Invalid control field */
#define	X	2	/* Unallowed I-field */
#define	Y	4	/* Too-long I-field */
#define	Z	8	/* Invalid sequence number */

/* Format of an AX.25 address - left-shifted callsign plus sub station ID */
#define	ALEN	6	/* Number of chars in callsign field */
struct ax25_addr {
	char call[ALEN];	
	char ssid;
};
/*
 *  It seems that some compilers (on VAX and 68K hardware) round up 
 *  structure sizes to quad- or double-byte boundaries.  Hmm..
 */
/* AX.25 address length (7 bytes) */
#define	AXALEN		(ALEN+1)    /* (sizeof(struct ax25_addr)) */

/* SSID address byte definitions */
#define	SSID		0x1e	/* Sub station ID */
#define	REPEATED	0x80	/* Has-been-repeated bit in repeater field */
#define	E		0x01	/* Address extension bit */
#define	C		0x80	/* Command/response designation */

#define	UNKNOWN		0
#define	COMMAND		1
#define	RESPONSE	2

#define	PID_ARP		0xcd	/* AX.25 Level 3 PID for ARP */
#define	PID_IP		0xcc	/* AX.25 Level 3 PID for IP */
#define PID_NETROM	0xcf	/* AX.25 Level 3 PID for NET/ROM frames */

/* Our AX.25 address */
extern struct ax25_addr mycall;

/* AX.25 broadcast address: "QST   -0" in shifted ASCII */
extern struct ax25_addr ax25_bdcst;
SHAR_EOF
cat << \SHAR_EOF > cmdparse.h
#define	NARG		10	/* Max number of args to commands */

struct cmds {
	char *name;		/* Name of command */
	int (*func)();		/* Function to execute command */
        int  argcmin;		/* Minimum number of args */
        char *argc_errmsg;	/* Message to print if insufficient args */
        char *exec_errmsg;	/* Message to print if function fails */
};
#ifndef	NULLCHAR
#define	NULLCHAR	(char *)0
#endif
SHAR_EOF
cat << \SHAR_EOF > ether.h
/* Generic Ethernet constants and templates */

#define	EADDR_LEN	6
/* Format of an Ethernet header */
struct ether {
	char dest[EADDR_LEN];
	char source[EADDR_LEN];
	int16 type;
};
/* Ethernet broadcast address */
extern char ether_bdcst[];

/* Ethernet type fields */
#define	IP_TYPE		0x800	/* Type field for IP */
#define	ARP_TYPE	0x806	/* Type field for ARP */

#define	HDR		sizeof(struct ether)	/* Size of Ethernet header */

#define	RUNT		60	/* smallest legal size packet, no fcs */
#define	GIANT		1514	/* largest legal size packet, no fcs */
SHAR_EOF
cat << \SHAR_EOF > ftp.h
#define	FTP_PORT	21	/* Control port */
#define	FTPD_PORT	20	/* Data port */
#define CTLZ	26		/* EOF for CP/M systems */

/* Per-session control block */
struct ftp {
	struct ftp *prev;	/* Linked list pointers */
	struct ftp *next;
	struct tcb *control;	/* TCP control connection */
	char state;
#define	COMMAND_STATE	0	/* Awaiting user command */
#define	SENDING_STATE	1	/* Sending data to user */
#define	RECEIVING_STATE	2	/* Storing data from user */

	char type;		/* Transfer type */
#define	IMAGE_TYPE	0
#define	ASCII_TYPE	1

	FILE *fp;		/* File descriptor being transferred */
	struct socket port;	/* Remote port for data connection */
	struct tcb *data;	/* Data connection */

	/* The following are used only by the server */
	char *username;		/* User's name */
	char *buf;		/* Input command buffer */
	char cnt;		/* Length of input buffer */
#ifdef	AMIGA
	unsigned long cd;	/* lock on current directory *.
#else
	char *cd;		/* Current directory name */
#endif
	/* And this is used only by the client */
	struct session *session;
};

#define	NULLFTP	(struct ftp *)NULL
SHAR_EOF
cat << \SHAR_EOF > functions.h
#define LATTICE	1
SHAR_EOF
cat << \SHAR_EOF > icmp.h
/* Internet Control Message Protocol */

/* Message types */
#define	ECHO_REPLY	0	/* Echo Reply */
#define	DEST_UNREACH	3	/* Destination Unreachable */
#define	QUENCH		4	/* Source Quench */
#define	REDIRECT	5	/* Redirect */
#define	ECHO		8	/* Echo Request */
#define	TIME_EXCEED	11	/* Time-to-live Exceeded */
#define	PARAM_PROB	12	/* Parameter Problem */
#define	TIMESTAMP	13	/* Timestamp */
#define	TIME_REPLY	14	/* Timestamp Reply */
#define	INFO_RQST	15	/* Information Request */
#define	INFO_REPLY	16	/* Information Reply */

struct icmp {
	char type;
	char code;
	int16 checksum;
	union icmp_args {
		int32 unused;
		char pointer;
		int32 address;
		struct {
			int16 id;
			int16 seq;
		} echo;
	} args;
};
#define	NULLICMP	((union icmp_args *)NULL)
	
/* Destination Unreachable codes */
#define	NET_UNREACH	0	/* Net unreachable */
#define	HOST_UNREACH	1	/* Host unreachable */
#define	PROT_UNREACH	2	/* Protocol unreachable */
#define	PORT_UNREACH	3	/* Port unreachable */
#define	FRAG_NEEDED	4	/* Fragmentation needed and DF set */
#define	ROUTE_FAIL	5	/* Source route failed */

/* Time Exceeded codes */
#define	TTL_EXCEED	0	/* Time-to-live exceeded */
#define	FRAG_EXCEED	1	/* Fragment reassembly time exceeded */

/* Redirect message codes */
#define	REDR_NET	0	/* Redirect for the network */
#define	REDR_HOST	1	/* Redirect for the host */
#define	REDR_TOS	2	/* Redirect for Type of Service, or-ed with prev */

struct icmp_errors {
	unsigned checksum;		/* ICMP Checksum errors */
	unsigned nospace;		/* alloc_mbuf failed someplace */
	unsigned noloop;		/* No ICMP in response to an ICMP */
	unsigned bdcsts;		/* Ignore broadcast ICMPs */
};
#define	ICMP_TYPES	17
struct icmp_stats {
	unsigned input[ICMP_TYPES];	/* ICMP input stats by type */
	unsigned output[ICMP_TYPES];	/* ICMP output stats by type */
};

void icmp_dump();
SHAR_EOF
cat << \SHAR_EOF > iface.h
/* Interface control structure */
struct interface {
	struct interface *next;	/* Linked list pointer */
	char *name;		/* Ascii string with interface name */
	int16 mtu;		/* Maximum transmission unit size */
	int (*send)();		/* Routine to call to send IP datagram */
	int (*output)();	/* Routine to call to send raw packet */
	int (*recv)();		/* Routine to kick to process input */
	int (*stop)();		/* Routine to call before detaching */
	int16 dev;		/* Subdevice number to pass to send */
	int16 flags;		/* State of interface */
#define	IF_ACTIVE	0x01
#define	IF_BROADCAST	0x04	/* Interface is capable of broadcasting */
	char *hwaddr;		/* Device hardware address, if any */
};
#define	NULLIF	(struct interface *)NULL
extern struct interface *ifaces;	/* Head of interface list */
SHAR_EOF
cat << \SHAR_EOF > inetdev.h
/*
 *  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.
 */

#ifndef	EXEC_SEMAPHORES_H
#include <exec/semaphores.h>
#endif


struct InternetBase {
	struct Library lib;

	/* use AttemptSemaphore()/ObtainSemaphore()/ReleaseSemaphore() */
	struct SignalSemaphore ib_lock;

	/* add any user visible variables here */
	struct List	ib_Units;
} *InternetBase;


struct IOINETReq {
	struct	Message	io_Message;
	struct	Device	*io_Device;	/* device node pointer */
	struct INET_Unit	*io_Unit;		/* unit (driver private) */
	UWORD	io_Command;		/* device command */
	UBYTE	io_Flags;
	BYTE	io_Error;
	ULONG	io_Actual;
	ULONG	io_Length;
	APTR	io_Data;
	ULONG	io_Offset;
	union {
		struct TCP_state_u {
			UBYTE	io_u_OldState;	/* old TCP state */
			UBYTE	io_u_State;	/* new tcp state */
		} io_TCP_State_u;
		/* add more members of union here (protocol specific) */
	} io_INET_u;
	struct	socket io_lsocket;	/* local socket address */
	struct	socket io_fsocket;	/* remote socket address */

/* parameters used on OpenDevice() only */
#define	io_TCP_Window	io_Length
#define	io_INET_TOS	io_Actual

/* define easier to use names for members of protocol specific status union */
#define	io_OldState	io_INET_u.io_TCP_State_u.io_u_OldState
#define io_State	io_INET_u.io_TCP_State_u.io_u_State

};


struct INET_Unit {
	struct	Node iu_Unit;		/* list thread */
	struct	List iu_Input;		/* queue of read requests */
	struct	List iu_Output;		/* queue of write reqeusts */
	struct	IOINETReq *iu_Act_Input;/* current active input request */
	struct	IOINETReq *iu_Act_Output;/* current active output request */
	void	*iu_ccb;		/* generic control block 
					   (TCP/UDP protocol specific) */
	ULONG	iu_user;		/* user supplied "cookie" */
	ULONG	iu_type;		/* type of connection (UDP/TCP..) */
};

#define IN_VERSION	1
#define IN_REVISION	0

#define INET_UNIT_RAW	0L
#define	INET_UNIT_TCP	1L
#define	INET_UNIT_UDP	2L
SHAR_EOF
cat << \SHAR_EOF > inetlib.h
#pragma libcall  InternetBase DSOpen 1e 109e04
#pragma libcall  InternetBase DSClose 24 9e02
#pragma libcall  InternetBase DSExpunge 2a e01
#pragma libcall  InternetBase DSBeginIO 30 e902
#pragma libcall  InternetBase DSAbortIO 36 e902
SHAR_EOF
cat << \SHAR_EOF > internet.h
/* Global structures and constants pertaining to the interface between IP and
 * 	higher level protocols
 */

/* IP protocol field values */
#define	ICMP_PTCL	1	/* Internet Control Message Protocol */
#define	TCP_PTCL	6	/* Transmission Control Protocol */
#define	UDP_PTCL	17	/* User Datagram Protocol */

#define	MAXTTL		255	/* Maximum possible IP time-to-live value */

/* DoD-style precedences */
#define	ROUTINE		0x00
#define	PRIORITY	0x20
#define	IMMEDIATE	0x40
#define	FLASH		0x60
#define	FLASH_OVER	0x80
#define	CRITIC		0xa0
#define	INET_CTL	0xc0
#define	NET_CTL		0xe0

/* Amateur-style precedences */
#define	AM_ROUTINE	0x00
#define	AM_WELFARE	0x20
#define	AM_PRIORITY	0x40
#define	AM_EMERGENCY	0x60

/* Class-of-service bits */
#define	LOW_DELAY	0x10
#define	THROUGHPUT	0x08
#define	RELIABILITY	0x04

/* IP TOS fields */
#define	PREC(x)		((x)>>5 & 7)
#define	DELAY		0x10
#define	THRUPUT		0x8
#define	RELIABLITY	0x4

/* Pseudo-header for TCP and UDP checksumming */
struct pseudo_header {
	int32 source;		/* IP source */
	int32 dest;		/* IP destination */
	char zero;
	char protocol;		/* Protocol */
	int16 length;		/* Data field length */
};
#define	NULLHEADER	(struct pseudo_header *)NULL
void tcp_input(),udp_input(),ip_send(),dump_ip();
SHAR_EOF
cat << \SHAR_EOF > ip.h
#define	NROUTE	5	/* Number of hash chains in routing table */

extern int32 ip_addr;	/* Our IP address for ICMP and source routing */

extern char ip_ttl;	/* Default time-to-live for IP datagrams */

struct ip_header {
	char v_ihl;		/* Version + IP header length */
#define	IPVERSION	4
	char tos;		/* Type of service */
	int16 length;		/* Total length */
	int16 id;		/* Identification */
	int16 fl_offs;		/* Flags + fragment offset */

#define	F_OFFSET	0x1fff	/* Offset field */
#define	DF	0x4000		/* Don't fragment flag */
#define	MF	0x2000		/* More Fragments flag */	

	char ttl;		/* Time to live */
	char protocol;		/* Protocol */
	int16 checksum;		/* Header checksum */
	int32 source;		/* Source address */
	int32 dest;		/* Destination address */
};

/* Fields in option type byte */
#define	OPT_COPIED	0x80	/* Copied-on-fragmentation flag */
#define	OPT_CLASS	0x60	/* Option class */
#define	OPT_NUMBER	0x1f	/* Option number */

/* IP option numbers */
#define	IP_EOL		0	/* End of options list */
#define	IP_NOOP		1	/* No Operation */
#define	IP_SECURITY	2	/* Security parameters */
#define	IP_LSROUTE	3	/* Loose Source Routing */
#define	IP_TIMESTAMP	4	/* Internet Timestamp */
#define	IP_RROUTE	7	/* Record Route */
#define	IP_STREAMID	8	/* Stream ID */
#define	IP_SSROUTE	9	/* Strict Source Routing */

/* Timestamp option flags */
#define	TS_ONLY		0	/* Time stamps only */
#define	TS_ADDRESS	1	/* Addresses + Time stamps */
#define	TS_PRESPEC	3	/* Prespecified addresses only */

/* IP routing table entry */
struct route {
	struct route *prev;	/* Linked list pointers */
	struct route *next;
	int32 target;		/* Target IP address */
	int32 gateway;		/* IP address of local gateway for this target */
	int metric;		/* Hop count, whatever */
	struct interface *interface;	/* Device interface structure */
};
#define	NULLROUTE	(struct route *)NULL

/* Reassembly descriptor */
struct reasm {
	struct reasm *next;	/* Linked list pointers */
	struct reasm *prev;
	int32 source;		/* These four fields uniquely describe a datagram */
	int32 dest;
	int16 id;
	char protocol;
	int16 length;		/* Entire datagram length, if known */
	struct timer timer;	/* Reassembly timeout timer */
	struct frag *fraglist;	/* Head of data fragment chain */
};
#define	NULLREASM	(struct reasm *)NULL

/* Fragment descriptor in a reassembly list */
struct frag {
	struct frag *prev;	/* Previous fragment on list */
	struct frag *next;	/* Next fragment */
	struct mbuf *buf;	/* Actual fragment data */
	int16 offset;		/* Starting offset of fragment */
	int16 last;		/* Ending offset of fragment */
};
#define	NULLFRAG	(struct frag *)NULL

extern struct reasm *reasmq;	/* The list of reassembly descriptors */

/* IP error logging counters */
struct ip_stats {
	long total;		/* Total packets received */
	unsigned runt;		/* Smaller than minimum size */
	unsigned length;	/* IP header length field too small */
	unsigned version;	/* Wrong IP version */
	unsigned checksum;	/* IP header checksum errors */
	unsigned badproto;	/* Unsupported protocol */
};
extern struct ip_stats ip_stats;
void ip_dump();
SHAR_EOF
cat << \SHAR_EOF > machdep.h
/* This file defines certain low level operations and characteristics that
 * are likely to be machine dependent.
 */

#if	(MPU8086 || MPU8080 || vax)
#define	LITTLE_ENDIAN	/* Low order bytes are first in memory */
#endif

#ifdef	AMIGA

#ifdef	AMIGADEVDRV
#define	disable()	Forbid()	/* doesn't really return a value */
#define	restore(intst)	Permit()

#else

#define	disable()	(0)	/* got to return a value */
#define	restore(intst)		/* but we're not gonna do anything */
#endif

#ifdef WINDOWIO
#ifdef	putchar
#undef	putchar
#endif
#ifdef	fflush
#undef	fflush
#endif
#define	putchar(c)	amigaputchar(c)
#ifdef LATTICE
#define	fflush(fp)	((fp)==stdout ? amigaflush() : _flsbf(-1, fp))
#else
#define	fflush(fp)	((fp)==stdout ? amigaflush() : flsh_(fp, -1))
#endif
				/* this has to be the same as in stdio.h */
#ifdef LATTICE
#define index strchr
#endif
#endif
#ifdef LATTICE
#undef printf
#endif
#endif WINDOWIO
/* These two lines assume that your compiler's longs are 32 bits and
 * shorts are 16 bits. It is already assumed that chars are 8 bits,
 * but it doesn't matter if they're signed or unsigned.
 */
typedef long int32;		/* 32-bit signed integer */
typedef unsigned short int16;	/* 16-bit unsigned integer */

#define	bcopy(a,b,cnt)	movmem(a,b,cnt)

#ifdef	LITTLE_ENDIAN
int32 ntohl();
int16 ntohs();
#else	/* Degenerate case for Big Endian machines */
#define	ntohl(x)	(x)
#define ntohs(x)	(x)
#endif

#define	min(x,y)	((x)<(y)?(x):(y))
#define	max(x,y)	((x)>(y)?(x):(y))

int16 cksum();

/* Host-to-network and network-to-host are symmetrical */
#define htonl(x)	ntohl(x)
#define	htons(x)	ntohs(x)

#ifdef	MPU8080	/* Assembler routines are available */
int16 hinibble(),lonibble(),hibyte(),lobyte(),hiword(),loword();

#else

/* Extract a short from a long */
#define	hiword(x)	((int16)((x) >> 16))
#define	loword(x)	((int16)(x))

/* Extract a byte from a short */
#define	hibyte(x)	(((x) >> 8) & 0xff)
#define	lobyte(x)	((x) & 0xff)

/* Extract nibbles from a byte */
#define	hinibble(x)	(((x) >> 4) & 0xf)
#define	lonibble(x)	((x) & 0xf)

#endif

/* Define null object pointer in case stdio.h isn't included */
#ifndef	NULL
/* General purpose NULL pointer */
#define	NULL (void *)0
#endif
#define	NULLCHAR (char *)NULL	/* Null character pointer */
#define	NULLFP	 (int (*)())0	/* Null pointer to function returning int */
#define	NULLVFP	 (void (*)())0	/* Null pointer to function returning void */
#define	NULLFILE (FILE *)NULL	/* Null file pointer */
SHAR_EOF
cat << \SHAR_EOF > mbuf.h
/* Basic message buffer structure */
struct mbuf {
	struct mbuf *next;	/* Links mbufs belonging to single packets */
	struct mbuf *anext;	/* Links packets on queues */
	char *data;		/* Active working pointers */
	int16 cnt;
};
#define	NULLBUF	(struct mbuf *)NULL
void enqueue(),hexdump(),asciidump();
struct mbuf *alloc_mbuf(),*free_mbuf(),*dequeue(),*copy_p(),*free_p(),*qdata();
int16 pullup(),append(),dup_p(),len_mbuf(),dqdata(),len_q();

SHAR_EOF
cat << \SHAR_EOF > netrom.h
/*
 *  Define NET/ROM constants.
 */

#define NETROM_SIG	0xff	/* Signature byte in NET/ROM datagrams */

struct nr {
	struct interface *link;		/* pointer to companion interface */
	unsigned	upd_freq;	/* routing update frequency */
	char		ident[7];	/* my identifier 6 char + NUL */
};
SHAR_EOF
cat << \SHAR_EOF > netuser.h
/* Global structures and constants needed by an Internet user process */
#define	NCONN	20		/* Maximum number of open network connections */

extern int32 ip_addr;	/* Our IP address */

extern int net_error;	/* Error return code */
#define	NONE	0		/* No error */
#define	CON_EXISTS	1	/* Connection already exists */
#define	NO_CONN	2		/* Connection does not exist */
#define	CON_CLOS	3	/* Connection closing */
#define	NO_SPACE	4	/* No memory for TCB creation */
#define	WOULDBLK	5	/* Would block */
#define	NOPROTO		6	/* Protocol or mode not supported */
#define	INVALID		7	/* Invalid arguments */

/* Codes for the tcp_open call */
#define	TCP_PASSIVE	0
#define	TCP_ACTIVE	1

/* Socket structure */
struct socket {
	int32 address;		/* IP address */
	int16 port;			/* port number */
};

/* Connection structure (two sockets) */
struct connection {
	struct socket local;
	struct socket remote;
};
#define	NULLSOCK	(struct socket *)NULL
int32 aton();
char *inet_ntoa(),*psocket();
long htol();
SHAR_EOF
cat << \SHAR_EOF > session.h
extern int mode;
#define	CMD_MODE	1	/* Command mode */
#define	CONV_MODE	2	/* Converse mode */

/* Session control structure; only one entry is used at a time */
struct session {
	int type;
#define	FREE	0
#define	TELNET	1
#define	FTP	2
	union {
		struct ftp *ftp;
		struct telnet *telnet;
	} cb;
	int (*parse)();	/* Where to hand typed input when conversing */
};
#define	NULLSESSION	(struct session *)NULL
extern struct session sessions[];

extern struct session *current;
extern int16 lport;
SHAR_EOF
cat << \SHAR_EOF > slip.h
/* SLIP definitions */
#define	SLIP_ALLOC	40	/* Receiver allocation increment */
#define	SLIP_MTU	1024	/* Maximum receiver buffer size */

#define	FR_END		0300	/* Frame End */
#define	FR_ESC		0333	/* Frame Escape */
#define	T_FR_END	0334	/* Transposed frame end */
#define	T_FR_ESC	0335	/* Transposed frame escape */

/* Slip protocol control structure */
struct slip {
	struct mbuf *sndq;	/* Encapsulated packets awaiting transmission */
	int16 sndcnt;		/* Number of datagrams on queue */
	char escaped;		/* Receiver State control flag */
	struct mbuf *rbp;	/* Head of mbuf chain being filled */
	struct mbuf *rbp1;
	char *rcp;		/* Write pointer */
	int16 rcnt;		/* Length of mbuf chain */
	struct mbuf *tbp;	/* Transmit mbuf being sent */
	int16 errors;		/* Receiver input errors */
	int (*recv)();		/* Function to call with an incoming buffer */
};
extern struct slip slip[];
SHAR_EOF
cat << \SHAR_EOF > smtp.h
/* Turn off the next line if your system doesn't have a clock */
#define	DATE
/* #define USERNAME "bdale"		/* name of local user */
/* #define SMTPGATE "44.32.0.1"		/* address to punt mail to */
#define SMTP_PORT	25		/* well-known port for smtp */
#define SMTPCLITIME	900		/* 15 minutes between client starts */

/* currently, the below definitions are set up to put incoming mail activity
   in \spool\mail, and outgoing mail activity in \spool\mqueue.  Twiddle to
   suit, this is for pseudo-compatibility with 4bsd, since that's what I'm
   most familiar with...    Bdale  */

#ifndef	AMIGA
/* Mail box file name template - edit to taste */
#define	MAILSPOOL	"/spool/mail/%s.txt"
/* path for outgoin mail files */
#define MAILQDIR	"/spool/mqueue/"
#else	AMIGA
#define	MAILSPOOL	"INET:mail/%s.txt"
#define	MAILQDIR	"INET:mqueue/"
#endif

extern char hostname[];
#define	LINELEN		128
#define SLINELEN	32

/* Recipient address entry */
struct addr {
	struct addr *next;
	char *val;
};
#define	NULLADDR	(struct addr *)NULL

/* Per-session control block */
struct mail {
	struct tcb *tcb;	/* TCP control block pointer */
	char state;
#define	COMMAND_STATE	0
#define	DATA_STATE	1

	char *system;		/* Name of remote system */
	struct addr *to;	/* Linked list of recipients */
	char buf[LINELEN];	/* Input buffer */
	char cnt;		/* Length of input buffer */
	FILE *data;		/* Temporary input file pointer */
};
#define	NULLMAIL	(struct mail *)NULL

struct smtp_msg {
	struct tcb *tcb;	/* tcp task control buffer */
	char cts;		/* used as boolean, true if space avail in
				   tcp buffer */
	char state;		/* state machine placeholder */
#define CLI_OPEN_STATE	0
#define CLI_MAIL_STATE	1
#define CLI_RCPT_STATE	2
#define CLI_DATA_STATE	3
#define	CLI_SEND_STATE	4
#define	CLI_UNLK_STATE	5
#define CLI_QUIT_STATE	6
	char	*filename;	/* name of workfile */
	char	toaddr[LINELEN],
		fromaddr[LINELEN];
	char buf[LINELEN];	/* Input buffer */
	char cnt;		/* Length of input buffer */
	FILE *wfile, *tfile;
};
SHAR_EOF
cat << \SHAR_EOF > tcp.h
/* TCP implementation. Follows RFC 793 as closely as possible */

#define	DEF_WND	2048	/* Default receiver window */
#define	NTCB	19	/* # TCB hash table headers */
#define	RETRY	10	/* Retry limit */
#define	BACKOFF	1024	/* Truncation point for backoff algorithm */
#define	DEF_MSS	512	/* Default maximum segment size */
#define	DEF_RTT	5	/* Initial guess at round trip time (5 sec) */
#define	MSL2	30	/* Guess at two maximum-segment lifetimes */
/* Round trip timing parameters */
#define	ALPHA1	7	/* 7/8 when delay is increasing */
#define	ALPHA2	15	/* 15/16 when delay is decreasing */
#define	BETA	2	/* Allow two round trip times before retransmitting */
/* TCP segment header */
struct tcp_header {
	int16 source;	/* Source port */
	int16 dest;	/* Destination port */
	int32 seq;	/* Sequence number */
	int32 ack;	/* Acknowledgment number */
	char offset;	/* Data offset */
	char flags;	/* Flags, data offset */
#define	DSHIFT	4	/* Data offset field */
#define	DMASK	0x0f	/* Mask for normalized data offset field */
#define	URG	0x20	/* URGent flag */
#define	ACK	0x10	/* ACKnowledgment flag */
#define	PSH	0x08	/* PuSH flag */
#define	RST	0x04	/* ReSeT flag */
#define	SYN	0x02	/* SYNchronize flag */
#define	FIN	0x01	/* FINal flag */
	int16 wnd;	/* Receiver flow control window */
	int16 checksum;	/* Header + data checksum */
	int16 up;	/* Urgent pointer */
};

/* Format of the Maximum Segment Size option (the only one currently
 * defined in TCP)
 */
struct mss {
	char kind;	/* Must be 2 */
#define	MSS_KIND	2
	char length;	/* Must be 4 */
#define	MSS_LENGTH	4
	int16 mss;	/* The actual value */
};

/* Resequencing queue entry */
struct reseq {
	struct reseq *next;	/* Linked-list pointer */
	char tos;		/* Type of service */
	struct tcp_header seg;	/* TCP header */
	struct mbuf *bp;	/* data */
	int16 length;		/* data length */
};
#define	NULLRESEQ	(struct reseq *)NULL

/* TCP connection control block */
struct tcb {
	struct tcb *prev;	/* Linked list pointers for hash table */
	struct tcb *next;

	struct connection conn;

	char state;	/* Connection state */
#define	CLOSED		0
#define	LISTEN		1
#define	SYN_SENT	2
#define	SYN_RECEIVED	3
#define	ESTABLISHED	4
#define	FINWAIT1	5
#define	FINWAIT2	6
#define	CLOSE_WAIT	7
#define	CLOSING		8
#define	LAST_ACK	9
#define	TIME_WAIT	10

	char reason;		/* Reason for closing */
#define	NORMAL		0	/* Normal close */
#define	RESET		1	/* Reset by other end */
#define	TIMEOUT		2	/* Excessive retransmissions */
#define	NETWORK		3	/* Network problem (ICMP message) */

/* If reason == NETWORK, the ICMP type and code values are stored here */
	char type;
	char code;

	/* Send sequence variables */
	struct {
		int32 una;	/* First unacknowledged sequence number */
		int32 nxt;	/* Next sequence num to be sent for the first time */
		int32 ptr;	/* Working transmission pointer */
		int16 wnd;	/* Other end's offered receive window */
		int16 up;	/* Send urgent pointer */
		int32 wl1;	/* Sequence number used for last window update */
		int32 wl2;	/* Ack number used for last window update */
	} snd;
	int32 iss;		/* Initial send sequence number */

	/* Receive sequence variables */
	struct {
		int32 nxt;	/* Incoming sequence number expected next */
		int16 wnd;	/* Our offered receive window */
		int16 up;	/* Receive urgent pointer */
	} rcv;
	int32 irs;		/* Initial receive sequence number */
	int16 mss;		/* Maximum segment size */

	char retry;		/* Retransmission retry count */
	void (*r_upcall)();	/* Call when "significant" amount of data arrives */
	void (*t_upcall)();	/* Call when ok to send more data */
	void (*s_upcall)();	/* Call when connection state changes */
	char force;		/* We owe the other end an ACK or window update */
	char tos;		/* Type of service (for IP) */

	int16 window;		/* Receiver window and send queue limit */
	struct mbuf *rcvq;	/* Receive queue */
	int16 rcvcnt;

	struct mbuf *sndq;	/* Send queue */
	int16 sndcnt;		/* Number of unacknowledged sequence numbers on
				 * send queue. NB: includes SYN and FIN, which don't
				 * actually appear on sndq!
				 */
	struct reseq *reseq;	/* Out-of-order segment queue */

	struct timer timer;	/* Retransmission timer */
	int32 rttseq;		/* Sequence number being timed */
	int32 srtt;		/* Smoothed round trip time, milliseconds */

	int *user;		/* User parameter (e.g., for mapping to an
				 * application control block
				 */
};
#define	NULLTCB	(struct tcb *)NULL
/* TCP statistics counters */
struct tcp_stat {
	int16 runt;		/* Smaller than minimum size */
	int16 checksum;		/* TCP header checksum errors */
	int16 conout;		/* Outgoing connection attempts */
	int16 conin;		/* Incoming connection attempts */
	int16 resets;		/* Resets generated */
	int16 bdcsts;		/* Bogus broadcast packets */
};
extern struct tcp_stat tcp_stat;
#ifndef LATTICE
#define	min(x,y)	((x)<(y)?(x):(y))
#define max(x,y)	((x)>(y)?(x):(y))
#endif
extern struct tcb *tcbs[];
extern int32 iss();
struct tcb *lookup_tcb();
struct tcb *create_tcb();
void rehash_tcb(),tcp_output(),tcp_input(),close_self(),dump_seg(),
	setstate();

/* TCP primitives */
struct tcb *open_tcp();
int send_tcp(),recv_tcp(),close_tcp(),del_tcp();
void state_tcp(),tcp_dump();

extern int16 tcp_mss;
extern int16 tcp_window;
SHAR_EOF
cat << \SHAR_EOF > telnet.h
#define	LINESIZE	256	/* Length of local editing buffer */
#define TELNET_PORT	23	/* TCP port for telnet service */

/* Telnet command characters */
#define	IAC		255	/* Interpret as command */
#define	WILL		251
#define	WONT		252
#define	DO		253
#define	DONT		254

/* Telnet options */
#define	TN_TRANSMIT_BINARY	0
#define	TN_ECHO			1
#define	TN_SUPPRESS_GA		3
#define	TN_STATUS		5
#define	TN_TIMING_MARK		6
#define	NOPTIONS		6

/* Telnet protocol control block */
struct telnet {
	struct tcb *tcb;
	char state;

#define	TS_DATA	0	/* Normal data state */
#define	TS_IAC	1	/* Received IAC */
#define	TS_WILL	2	/* Received IAC-WILL */
#define	TS_WONT	3	/* Received IAC-WONT */
#define	TS_DO	4	/* Received IAC-DO */
#define	TS_DONT	5	/* Received IAC-DONT */

	char local[NOPTIONS];	/* Local option settings */
	char remote[NOPTIONS];	/* Remote option settings */

	struct session *session;	/* Pointer to session structure */
};
#define	NULLTN	(struct telnet *)NULL
extern int refuse_echo;
struct telnet *open_telnet();
int send_tel(),tel_input();
SHAR_EOF
cat << \SHAR_EOF > timer.h
/* Software timers
 * There is one of these structures for each simulated timer.
 * Whenever the timer is running, it is on a doubly-linked list
 * pointed to by "timers" so that the (hardware) timer interrupt
 * can quickly run through the list and change counts and states.
 * Stopping a timer or letting it expire causes it to be removed
 * from the list; starting a timer puts it on the list.
 */
struct timer {
	struct timer *next;	/* Doubly-linked-list pointers */
	struct timer *prev;
	int16 start;		/* Period of counter (load value) */
	int16 count;		/* Ticks to go until expiration */
	void (*func)();		/* Function to call at expiration */
	int *arg;		/* Arg to pass function */
	char state;		/* Timer state */
#define	TIMER_STOP	0
#define	TIMER_RUN	1
#define	TIMER_EXPIRE	2
};
#define	NULLTIMER	(struct timer *)NULL
#define	MAX_TIME	(int16)65535	/* Max short integer */
#ifndef	MSPTICK
#define	MSPTICK		1000		/* Milliseconds per tick */
#endif
/* Useful user macros that hide the timer structure internals */
#define	set_timer(t,x)	(((t)->start) = (x)/MSPTICK)
#define	dur_timer(t)	((t)->start)
#define	read_timer(t)	((t)->count)
#define	run_timer(t)	(((t)->state == TIMER_RUN) ? 1 : 0)
SHAR_EOF
cat << \SHAR_EOF > trace.h
/* Dump an IP datagram header. If it's the first fragment, also dump
 * the next layer header (if known). Dumping is controlled by the low-order
 * 4 bits of the external variable "trace":
 * Level 0: no dump
 * Level 1-2: link level dumps only
 * Level 3: IP and ARP dumps only
 * Level 4: IP header + UDP or TCP header
 * Level 5: All headers + hex dump
 */
extern int32 trace;

#define TRACE_AX25      0x8000l
#define TRACE_EC        0x4000l
#define TRACE_PC100     0x2000l
#define TRACE_SLIP      0x1000l
#define TRACE_SDLC      0x0800l
#define TRACE_SELF	0x0400l

#define TRACE_HDR       0x0fl
#define TRACE_DUMP      0x10l
#define TRACE_ASCII	0x20l
#define TRACE_CMDPARSE  0x80000000l
#define TRACE_DEVICE	0x40000000l
SHAR_EOF
cat << \SHAR_EOF > udp.h
/* User Datagram Protocol definitions */

#define	NUDP	20

/* Structure of a UDP protocol header */
struct udp_header {
	int16 source;	/* Source port */
	int16 dest;	/* Destination port */
	int16 length;	/* Length of header and data */
	int16 checksum;	/* Checksum over pseudo-header, header and data */
};

/* User Datagram Protocol control block
 * Each entry on the receive queue consists of the
 * remote socket structure, followed by any data
 */
struct udp_cb {
	struct udp_cb *prev;	/* Linked list pointers */
	struct udp_cb *next;
	struct socket socket;	/* Local port accepting datagrams */
	void (*r_upcall)();	/* Function to call when one arrives */
	struct mbuf *rcvq;	/* Queue of pending datagrams */
	int rcvcnt;		/* Count of pending datagrams */
};
extern struct udp_cb *udps[];	/* Hash table for UDP structures */
#define	NULLUDP	(struct udp_cb *)NULL

/* UDP statistics counters */
struct udp_stat {
	int16 rcvd;		/* Packets received */
	int16 sent;		/* Packets sent */
	int16 cksum;		/* Checksum errors */
	int16 unknown;		/* Unknown socket */
	int16 bdcsts;		/* Incoming broadcasts */
};

/* UDP primitives */
int open_udp(),recv_udp(),send_udp(),del_udp();
void udp_dump();
SHAR_EOF
cat << \SHAR_EOF > cmdparse.c
/* Parse command line, set up command arguments Unix-style, and call function.
 * Note: argument is modified (delimiters are overwritten with nulls)
 * Improved error handling by Brian Boesch of Stanford University
 */
#ifdef TRACE
#include <stdio.h>
#include "machdep.h"
#include "trace.h"
#endif

#include "cmdparse.h"

int
cmdparse(cmds,line)
struct cmds cmds[];
register char *line;
{
	struct cmds *cmdp;
	char *argv[NARG],*cp,*index();
	int argc,qflag;
	int i, rslt;

	/* Remove cr/lf */
	if((cp = index(line,'\r')) != NULLCHAR)
		*cp = '\0';
	if((cp = index(line,'\n')) != NULLCHAR)
		*cp = '\0';	/* shouldn't be necessary */

	for(argc = 0;argc < NARG;argc++)
		argv[argc] = NULLCHAR;

	for(argc = 0;argc < NARG;){
		qflag = 0;
		/* Skip leading white space */
		while(*line == ' ' || *line == '\t')
			line++;
		if(*line == '\0')
			break;
		/* Check for quoted token */
		if(*line == '"'){
			line++;	/* Suppress quote */
			qflag = 1;
		}
		argv[argc++] = line;	/* Beginning of token */
		/* Find terminating delimiter */
		if(qflag){
			/* Find quote, it must be present */
			if((line = index(line,'"')) == NULLCHAR){
				return -1;
			}
		} else {
			/* Find space or tab. If not present,
			 * then we've already found the last
			 * token.
			 */
			if((cp = index(line,' ')) == NULLCHAR
			 && (cp = index(line,'\t')) == NULLCHAR){
				break;
			}
			*cp++ = '\0';
			line = cp;
		}
	}
#ifdef TRACE
	if(trace & TRACE_CMDPARSE) {
		printf("Number of tokens = %d\r\n",argc);
		for (i=0; i< argc; i++) {
			printf("Argument=%d  '%s'\r\n",i,argv[i]);
		}
	}
#endif
	if (argc < 1) {		/* empty command line */
		argc = 1;
		argv[0] = "";
	}
	/* Lines beginning with "#" are comments */
	if(argv[0][0] == '#')
		return 0;

	/* Look up command in table; prefix matches are OK */
	for(cmdp = cmds;cmdp->name != NULLCHAR;cmdp++){
		if(strncmp(argv[0],cmdp->name,strlen(argv[0])) == 0)
			break;
	}
	if(cmdp->name == NULLCHAR) {
		if(cmdp->argc_errmsg != NULLCHAR) 
			printf("%s\r\n",cmdp->argc_errmsg);
		return -1;
	} else {
		if(argc < cmdp->argcmin) {
			/* Insufficient arguments */
			printf("Usage: %s\r\n",cmdp->argc_errmsg);
			return -1;
		} else {
			rslt = (*cmdp->func)(argc,argv);
			if ((rslt < 0) && (cmdp->exec_errmsg != NULLCHAR))
				printf("%s\r\n",cmdp->exec_errmsg);
			return(rslt);
		}
	}
}

/* Call a subcommand based on the first token in an already-parsed line */
int
subcmd(tab,argc,argv)
struct cmds tab[];
int argc;
char *argv[];
{
	int rslt;
	register struct cmds *cmdp;

	/* Strip off first token and pass rest of line to subcommand */
	if (argc < 2) {
		if (argc < 1)
			printf("SUBCMD - Don't know what to do?\r\n");
		else
			printf("\"%s\" - takes at least one argument\r\n",argv[0]);
		return -1;
	}
	argc--;
	argv++;
	for(cmdp = tab;cmdp->name != NULLCHAR;cmdp++){
		if(strncmp(argv[0],cmdp->name,strlen(argv[0])) == 0){
			if(argc < cmdp->argcmin) {
				if (cmdp->argc_errmsg != NULLCHAR)
					printf("Usage: %s\r\n",cmdp->argc_errmsg);
				return -1;
			} else {
				rslt = (*cmdp->func)(argc,argv);
				if ((rslt < 0) && (cmdp->exec_errmsg != NULLCHAR))
					printf("%s\r\n",cmdp->exec_errmsg);
				return(rslt);
			}
		}
	}
	if (cmdp->argc_errmsg != NULLCHAR) 
		printf("%s\r\n",cmdp->argc_errmsg);
	return -1;
}
SHAR_EOF
cat << \SHAR_EOF > ether.c
/* Stuff generic to all Ethernet controllers */
#include "machdep.h"

char ether_bdcst[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

/* Format an Ethernet address into a printable ascii string */
pether(out,addr)
char *out,*addr;
{
	sprintf(out,"%02x:%02x:%02x:%02x:%02x:%02x",
		addr[0] & 0xff,
		addr[1] & 0xff,
		addr[2] & 0xff,
		addr[3] & 0xff,
		addr[4] & 0xff,
		addr[5] & 0xff,
		addr[6] & 0xff);
}

/* Convert an Ethernet address from Hex/ASCII to binary */
gether(out,cp)
register char *out;
register char *cp;
{
	register int i;
	char *index();

	for(i=6; i!=0; i--){
		*out++ = htoi(cp);
		if((cp = index(cp,':')) == NULLCHAR)	/* Find delimiter */
			break;
		cp++;			/* and skip over it */
	}
}


SHAR_EOF
cat << \SHAR_EOF > ftp.c
/* Stuff common to both the FTP server and client */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"
#include "session.h"

#ifdef	AMIGA
#define	UNIX	1	/* UNIX semantics work for Amiga in this module */
#endif

/* FTP data channel receive upcall handler */
void
r_ftpd(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct ftp *ftp;
	struct mbuf *bp;
#ifdef	UNIX
	char c;
#endif

	ftp = (struct ftp *)tcb->user;
	if(ftp->state != RECEIVING_STATE){
		close_tcp(tcb);
		return;
	}
	/* This will likely also generate an ACK with window rotation */
	recv_tcp(tcb,&bp,cnt);

#ifdef	UNIX
	while(pullup(&bp,&c,1) == 1){
		if(ftp->type == IMAGE_TYPE || c != '\r')
			putc(c,ftp->fp);
	}
#else
	while(bp != NULLBUF){
		if(bp->cnt != 0)
			fwrite(bp->data,1,(unsigned)bp->cnt,ftp->fp);
		bp = free_mbuf(bp);
	}
#endif
}
/* FTP data channel transmit upcall handler */
void
t_ftpd(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	struct ftp *ftp;
	struct mbuf *bp;
	char *cp;
	int c;
#ifndef	CPM
#ifndef	AMIGA
	char *cdsave,*pwd();
#endif
#endif

	ftp = (struct ftp *)tcb->user;
	if(ftp->state != SENDING_STATE){
		close_tcp(tcb);
		return;
	}
	if((bp = alloc_mbuf(cnt)) == NULLBUF){
		/* Hard to know what to do here */
		return;
	}
	cp = bp->data;
	while(cnt > 1 && (c = getc(ftp->fp)) != EOF){
#ifdef	CPM
		if(ftp->type == ASCII_TYPE && c == CTLZ)
			break;	/* CTLZ is CP/M's text EOF marker */
#endif
#ifdef	UNIX
		if(ftp->type == ASCII_TYPE && c == '\n'){
			*cp++ = '\r';
			bp->cnt++;
			cnt--;
		}
#endif
		*cp++ = c;
		bp->cnt++;
		cnt--;
	}
	if(bp->cnt != 0)
		send_tcp(tcb,bp);
	else
		free_p(bp);

	if(cnt > 1){	/* EOF seen */
#ifndef	CPM
#ifndef	AMIGA
		cdsave = pwd();		/* Save current directory */
		chdir(ftp->cd);		/* Switch to user's directory*/
#endif
#endif
		fclose(ftp->fp);
#ifndef	CPM
#ifndef	AMIGA
		chdir(cdsave);		/* And back */
		free(cdsave);
#endif
#endif
		ftp->fp = NULLFILE;
		close_tcp(tcb);
	}
}
/* Allocate an FTP control block */
struct ftp *
ftp_create(bufsize)
unsigned bufsize;
{
	void ftp_delete();
	char *calloc(),*malloc();
	register struct ftp *ftp;

	if((ftp = (struct ftp *)calloc(1,sizeof (struct ftp))) == NULLFTP)
		return NULLFTP;
	if(bufsize != 0 && (ftp->buf = malloc(bufsize)) == NULLCHAR){
		ftp_delete(ftp);
		return NULLFTP;
	}
	ftp->state = COMMAND_STATE;
	ftp->type = ASCII_TYPE;	/* Default transfer type */
	return ftp;
}
/* Free resources, delete control block */
void
ftp_delete(ftp)
register struct ftp *ftp;
{
	if(ftp->fp != NULLFILE)
		fclose(ftp->fp);
	if(ftp->data != NULLTCB)
		del_tcp(ftp->data);
	if(ftp->username != NULLCHAR)
		free(ftp->username);
	if(ftp->buf != NULLCHAR)
		free(ftp->buf);
#ifndef	AMIGA
	if(ftp->cd != NULLCHAR)
		free(ftp->cd);
#endif
	if(ftp->session != NULLSESSION)
		ftp->session->type = FREE;
	free((char *)ftp);
}

#ifndef	AMIGA
/* Call getcwd(), but stick a backslash on the front (!) */
#define CWDLEN	256		/* max path len */

char *
pwd()
{
	char *buf,*malloc(),*getcwd();

	if((buf = malloc(CWDLEN+1)) == NULLCHAR)
		return NULLCHAR;
	buf[0] = '\\';
	if(getcwd(buf+1,CWDLEN) == NULLCHAR){
		free(buf);
		return NULLCHAR;
	}
	return buf;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > hexload.c
/*
 *  Hex loader program.  Sends an ASCII file out the serial port to the
 *  loader running in the TNC-2 KISS loader ROM.  Use this rather than
 *  just "COPY TNC2KISS.HEX to SER:" so we don't have to depend on what
 *  the preferences baud rate for the serial port is set to.
 *
 *  Copyright (C) 1987
 *  Louis A. Mamakos
 *
 *  For non-commercial use only.  May not be sold, or included in any other
 *  product or collection of software that is sold for profit.
 */

#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/serial.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>

#ifndef	DEFFILE
#define	DEFFILE	"tnc2kiss.hex"
#endif

#ifndef	DEFBAUD
#define	DEFBAUD	4800
#endif

struct IOExtSer serout;
struct MsgPort *seroutp;
struct	FileHandle	*hexfile;
char	buffer[500];
extern	int Enable_Abort;
int	baud = DEFBAUD;

main(argc, argv)
	int argc;
	char **argv;
{
	register long totlen = 0;
	register long len;
	
	strcpy(buffer, DEFFILE);
	if ((argc > 1) && ((baud = atoi(argv[1])) > 0)) {
		serout.io_Baud = baud;
		argc--; argv++;
		printf("Loading at %d baud\n", baud);
	}
	if (argc > 1)
		strcpy(buffer, argv[1]);

	hexfile = Open(buffer, MODE_OLDFILE);

	Enable_Abort = 0;

	if (hexfile == 0L) {
		printf("Can't open file '%s'\n", buffer);
		exit(1);
	}

	if ((seroutp = CreatePort(0L, 0L)) == NULL) {
		printf("Can't create serial input port");
		Close(hexfile);
		exit(2);
	}
	/*
	 * Open serial device.
	 */
	serout.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;  /* ? */
	serout.io_Status = 0;
	serout.io_Baud = baud;
	if (OpenDevice("serial.device", 0L, &serout, 0L) != 0) {
		printf("Can't open serial device");
		DeletePort(seroutp);
		Close(hexfile);
		exit(2);
	}
	serout.IOSer.io_Message.mn_ReplyPort = seroutp;
	serout.IOSer.io_Data = (APTR) buffer;
	serout.IOSer.io_Length = 0;
	serout.IOSer.io_Command = CMD_WRITE;
	serout.IOSer.io_Flags = 0;
	serout.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE;
	serout.io_Baud = baud;
	serout.IOSer.io_Command = SDCMD_SETPARAMS;

	if (DoIO(&serout))
		printf("SETPARMS failed\n");

	serout.IOSer.io_Command = CMD_WRITE;
	serout.IOSer.io_Data = (APTR) buffer;
	while ((len = Read(hexfile, buffer, (ULONG) sizeof(buffer))) > 0) {
		serout.IOSer.io_Length = len;
		if (DoIO(&serout))
			printf("Serial write failed %ld\n",
				serout.IOSer.io_Error);
		totlen += len;
		if (Chk_Abort()) {
			printf("Hex load aborted!\n");
			break;
		}
	}
	printf("Wrote %ld bytes\n", totlen);
	Close(hexfile);
	CloseDevice(&serout);
	DeletePort(seroutp);
}

SHAR_EOF
cat << \SHAR_EOF > makefile
.c.o:
	lc -ilettuce:h -c $(CFLAGS) $<
#
#	Makefile for KA9Q TCP/IP package for PC clones with Aztec C
#
# switches:
#	define the ones you want in the CFLAGS definition...
#
#	TRACE		- turn on tracing/debugging code
#	SERVERS		- include code for application servers
#	AMIGA		- include Amiga specific code
#	MSDOS		- include Messy-Dos specific code
#	ETHER		- include ethernet specific code
#	UNIX		- Use UNIX file format conventions
#	CPM		- Use CP/M file format conventions
#	WINDOWIO	- use AMIGA windowio instead of standard IO
#
# Hardware driver flags:
#
#	SLIP		- include serial line IP stuff
#	PC_EC		- include 3Com Ethernet board driver for IBM-PC
#	AX25		- include AX.25 stuff
#	HAPN		- include HAPN stuff
#	PC100		- include PC-100 board driver (incomplete ??)
#	NETROM		- pseudo driver, needs AX.25
#	AMIGADEVDRV	- include Amiga internet.device device driver support

#
# CFLAGS for typical Amiga installation
#
CFLAGS= -dAMIGA -dSERVERS -dTRACE -dSLIP -dAX25 -dETHER -dMSPTICK=100 \
-dNETROM -dLATTICE -dAMIGADEVDRV -dWINDOWIO
APPLFLAGS= -dAMIGA  -dTRACE -dLATTICE -dAMIGADEVDRV 

NETOBJS= ftpserv.o ftpcli.o ftp.o smtpserv.o smtpcli.o \
	telnet.o tnserv.o smisc.o \
	tcpuser.o tcptimer.o tcpout.o tcpin.o tcpsubr.o udp.o \
	ip.o iproute.o icmp.o \
	ax25.o arp.o slip.o ether.o netrom.o \
	timer.o ttydriv.o cmdparse.o  mbuf.o netuser.o misc.o


net: makefile amiga.o version.o main.o devstub.o amigadev.o  $(NETOBJS)
	-blink lib:c.o version.o,main.o,amiga.o,devstub.o amigadev.o\
$(NETOBJS) to ram:net lib lib:lc.lib,lib:amiga.lib
	copy ram:net net
	delete ram:net

telnetp: telnetp.o netuser.o 
	-blink lib:c.o telnetp.o netuser.o to ram:telnetp \
	lib lib:lc.lib,lib:amiga.lib
	copy ram:telnetp telnetp
telnetp.o:telnetp.c machdep.h
	lc -ilettuce:h -c $(APPLFLAGS)  -dWINDOWIO telnetp.c
newtelnetp: newtelnetp.o netuser.o 
	-blink lib:c.o newtelnetp.o netuser.o to ram:newtelnetp \
	lib lib:lc.lib,lib:amiga.lib
	copy ram:newtelnetp newtelnetp
	
newtelnetp.o:newtelnetp.c machdep.h
	lc -ilettuce:h -c $(APPLFLAGS)  -dWINDOWIO newtelnetp.c
clean:	
	delete #?.o
	delete net

arp.o: arp.c machdep.h mbuf.h timer.h iface.h ether.h ax25.h arp.h
amiga.o: amiga.c machdep.h 
amigadev.o: amigadev.c inetdev.h machdep.h timer.h mbuf.h netuser.h internet.h ip.h tcp.h
devstub.o: devstub.asm
ax25.o: ax25.c machdep.h mbuf.h iface.h timer.h arp.h slip.h ax25.h trace.h
netrom.o: netrom.c machdep.h mbuf.h iface.h timer.h netrom.h ax25.h trace.h
cmdparse.o: cmdparse.c machdep.h trace.h cmdparse.h
ether.o: ether.c machdep.h
ftp.o: ftp.c machdep.h mbuf.h netuser.h timer.h tcp.h ftp.h session.h
ftpcli.o: ftpcli.c machdep.h mbuf.h netuser.h icmp.h timer.h tcp.h ftp.h session.h cmdparse.h
ftpserv.o: ftpserv.c machdep.h mbuf.h netuser.h timer.h tcp.h ftp.h
icmp.o: icmp.c internet.h timer.h ip.h icmp.h mbuf.h
ip.o: ip.c machdep.h mbuf.h timer.h internet.h ip.h icmp.h iface.h
iproute.o: iproute.c machdep.h mbuf.h internet.h timer.h netuser.h ip.h icmp.h iface.h trace.h
main.o: main.c machdep.h mbuf.h netuser.h timer.h icmp.h iface.h ip.h tcp.h ftp.h telnet.h session.h cmdparse.h amiga.h trace.h
mbuf.o: mbuf.c machdep.h mbuf.h
netuser.o: netuser.c machdep.h netuser.h
slip.o: slip.c machdep.h mbuf.h iface.h slip.h amiga.h trace.h
smisc.o: smisc.c machdep.h mbuf.h netuser.h timer.h tcp.h
smtpcli.o: smtpcli.c machdep.h netuser.h mbuf.h timer.h tcp.h smtp.h
smtpserv.o: smtpserv.c machdep.h mbuf.h netuser.h timer.h tcp.h smtp.h
tcpin.o: tcpin.c machdep.h timer.h mbuf.h netuser.h internet.h tcp.h icmp.h
tcpout.o: tcpout.c machdep.h timer.h mbuf.h netuser.h internet.h tcp.h
tcpsubr.o: tcpsubr.c machdep.h timer.h mbuf.h netuser.h internet.h tcp.h
tcptimer.o: tcptimer.c machdep.h timer.h mbuf.h netuser.h internet.h ip.h tcp.h
tcpuser.o: tcpuser.c machdep.h timer.h mbuf.h netuser.h internet.h ip.h tcp.h
telnet.o: telnet.c machdep.h mbuf.h timer.h internet.h icmp.h netuser.h tcp.h telnet.h session.h
tnserv.o: tnserv.c machdep.h mbuf.h timer.h internet.h icmp.h netuser.h tcp.h telnet.h session.h
timer.o: timer.c machdep.h timer.h
ttydriv.o: ttydriv.c machdep.h
udp.o: udp.c machdep.h mbuf.h netuser.h udp.h internet.h
SHAR_EOF
cat << \SHAR_EOF > misc.c
/* Miscellaneous machine independent utilities */

#include "machdep.h"

bcmp(a,b,n)
register char *a,*b;
register int16 n;
{
	while(n-- != 0){
		if(*a++ != *b++)
			return 1;
	}
	return 0;
}

bzero(buf,cnt)
register char *buf;
register int16 cnt;
{
	while(cnt-- != 0)
		*buf++ = '\0';
}
 
/* Convert hex-ascii to integer */
int
htoi(s)
char *s;
{
	int i = 0;
	char c;

	while((c = *s++) != '\0'){
		if(c == 'x')
			continue;	/* allow 0x notation */
		if('0' <= c && c <= '9')
			i = (i * 16) + (c - '0');
		else if('a' <= c && c <= 'f')
			i = (i * 16) + (c - 'a' + 10);
		else if('A' <= c && c <= 'F')
			i = (i * 16) + (c - 'A' + 10);
		else
			break;
	}
	return i;
}
SHAR_EOF
cat << \SHAR_EOF > netrom.c
/*
 * Tunnel IP datagrams thru NET/ROM nonsense.  Requires (right now) use
 * of AX.25 module.  No reason that we can't also speak their async serial
 * line protocol, expect that you'd have to have your own NET/ROM node.
 *
 * Louis A. Mamakos  WA3YMH
 */
#ifdef	NETROM
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "iface.h"
#include "ax25.h"
#include "netrom.h"

#ifdef	TRACE
#include "trace.h"
#endif

static int nnr;

void
netrom_input(interface, bp)
	struct interface *interface;
	struct mbuf *bp;
{
	netrom_dump(bp);	/** DEBUG **/
	free_p(bp);
}

#ifdef	TRACE
netrom_dump(bp)
	struct mbuf *bp;
{
	struct ax25_addr addr;
	char qual, ident[20];

	if (bp == NULLBUF)
		return;

	/* make a copy of the frame */
	if ((bp = copy_p(bp, len_mbuf(bp))) == NULL)
		return;

	printf("NET/ROM: ");

	/* first: examine the first byte in the NET/ROM packet.  If it is
	 * equal to NETROM_SIG, then this frame is a routing update.
	 * Otherwise, it is an inter-node frame.
	 */
	if ((unsigned char) *bp->data == NETROM_SIG) {
		/* routing update */
		if (pullup(&bp, NULLCHAR, 1) != 1)	/* trash signature */
			return;

		/* get ident of sending node */
		ident[6] = '\0';
		if (pullup(&bp, ident, 6) != 6)
			return;
		printf("RT from %s:\r\n", ident);
		/* now, loop through and display each of the routing entries */
		for(;;) {
			if (pullup(&bp, (char *)&addr, AXALEN) != AXALEN) {
				break;
			}
			pax25(ident, &addr);
			printf(" [%s/", ident);
			if (pullup(&bp, ident, 6) != 6) {
				printf("\r\nMissing ident!\r\n");
				break;
			}
			ident[6] = '\0';
			printf("%s via ", ident);
			if (pullup(&bp, (char *)&addr, AXALEN) != AXALEN) {
				printf("Missing neighbor node\r\n");
				break;
			}
			pax25(ident, &addr);
			if (pullup(&bp, &qual, 1) != 1) {
				printf("missing qual\r\n");
				break;
			}
			printf("%s qual %u]", ident, qual & 0xff);
		}
		printf("\r\n");
	} else {
		/* inter node frame */
		printf("inter-node frame\r\n");
	}	
	free_p(bp);
}
#endif	TRACE

/*
 *  Attach a NET/ROM virual interface to the system.  This interface needs the
 *  presence of a companion AX.25 interface to actually communicate.
 *
 *  argv[0]: hardware type: "netrom"
 *  argv[1]: name of companion ax.25 interface (like ax0)
 *  argv[2]: must be "ax25" to indicate using AX.25 as link level protocol
 *  argv[3]: label, name of interface like "nr0"
 *  argv[4]: optional: MTU
 *  argv[5]: optional: update frequency in seconds
 */
netrom_attach(argc, argv)
	int argc;
	char *argv[];
{
#ifdef	foobarbaz
	int dev;
	register struct interface *if_nr;

	if (nnr >= NR_MAX) {
		printf("Too many NET/ROM devices\r\n");
		return -1;
	}

	dev = nnr++;
	if_nr = calloc(1, sizeof(struct interface));
	
#endif	NETROM
}
#endif
SHAR_EOF
cat << \SHAR_EOF > netuser.c
/* Miscellaneous format conversion subroutines */

#include "machdep.h"
#include "netuser.h"
int net_error;

/* Convert Internet address in dotted-decimal format (44.0.0.1) to binary */
int32
aton(s)
char *s;
{
	int32 n;
	int atoi(),i;
	char *index();

	n = 0;
	for(i=24;i>=0;i -= 8){
		n |= (int32)atoi(s) << i;
		if((s = index(s,'.')) == NULLCHAR)
			break;
		s++;
	}
	return n;
}
/* Convert an internet address (in host byte order) to a dotted decimal ascii
 * string, e.g., 255.255.255.255\0
 */
char *
inet_ntoa(a)
int32 a;
{
	static char buf[16];

	sprintf(buf,"%u.%u.%u.%u",
		hibyte(hiword(a)),
		lobyte(hiword(a)),
		hibyte(loword(a)),
		lobyte(loword(a)) );
	return buf;
}
/* Convert a socket (address + port) to an ascii string of the form
 * aaa.aaa.aaa.aaa:ppppp
 */
char *
psocket(s)
struct socket *s;
{
	static char buf[30];

	sprintf(buf,"%s:%u",inet_ntoa(s->address),s->port);
	return buf;
}
/* Convert hex-ascii string to long integer */
long
htol(s)
char *s;
{
	long ret;
	char c;

	ret = 0;
	while((c = *s++) != '\0'){
		c &= 0x7f;
		if(c >= '0' && c <= '9')
			ret = ret*16 + (c - '0');
		else if(c >= 'a' && c <= 'f')
			ret = ret*16 + (10 + c - 'a');
		else if(c >= 'A' && c <= 'F')
			ret = ret*16 + (10 + c - 'A');
		else
			break;
	}
	return ret;
}
SHAR_EOF
cat << \SHAR_EOF > smisc.c
/* Miscellaneous servers */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"

static struct tcb *disc_tcb,*echo_tcb;
/* Start up discard server */
discard_start(argc,argv)
int argc;
char *argv[];
{
	struct socket lsocket;
	void r_discard(),t_state(),t_state();

	lsocket.address = ip_addr;
	if(argc < 2)
		lsocket.port = 9;
	else
		lsocket.port = atoi(argv[1]);
	disc_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_discard,NULLVFP,t_state,0);
}
/* Start echo server */
echo_start(argc,argv)
int argc;
char *argv[];
{
	void r_echo(),t_echo(),t_state();
	struct socket lsocket;

	lsocket.address = ip_addr;
	if(argc < 2)
		lsocket.port = 7;
	else
		lsocket.port = atoi(argv[1]);
	echo_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_echo,t_echo,t_state,0);

}

/* Shut down miscellaneous servers */
discard_stop()
{
	if(disc_tcb != NULLTCB)
		close_tcp(disc_tcb);
}
echo_stop()
{
	if(echo_tcb != NULLTCB)
		close_tcp(echo_tcb);
}

/* Discard server receiver upcall */
static
void
r_discard(tcb,cnt)
struct tcb *tcb;
int cnt;
{
	struct mbuf *bp;

	if(recv_tcp(tcb,&bp,cnt) > 0)
		free_p(bp);			/* Discard */
}

/* Echo server receive
 * Copies only as much will fit on the transmit queue
 */
static
void
r_echo(tcb,cnt)
struct tcb *tcb;
int cnt;
{
	struct mbuf *bp;
	int acnt;

	if(cnt == 0){
		close_tcp(tcb);
		return;
	}
	acnt = min(cnt,tcb->snd.wnd);
	if(acnt > 0){
		/* Get only as much will fit in the send window */
		recv_tcp(tcb,&bp,tcb->snd.wnd);
		send_tcp(tcb,bp);
	}
}
/* Echo server transmit
 * Copies anything that might have been left in the receiver queue
 */
static
void
t_echo(tcb,cnt)
struct tcb *tcb;
int cnt;
{
	struct mbuf *bp;

	if(tcb->rcvcnt > 0){
		/* Get only as much will fit in the send window */
		recv_tcp(tcb,&bp,cnt);
		send_tcp(tcb,bp);
	}
}

/* Log connection state changes; also respond to remote closes */
static
void
t_state(tcb,old,new)
register struct tcb *tcb;
char old,new;
{
	switch(new){
	case ESTABLISHED:
		log(tcb,"open %d",tcb->conn.local.port);
		break;
	case CLOSE_WAIT:
		close_tcp(tcb);
		break;
	case CLOSED:
		log(tcb,"close %d",tcb->conn.local.port);
		del_tcp(tcb);
		/* Clean up if server is being shut down */
		if(tcb == disc_tcb)
			disc_tcb = NULLTCB;
		else if(tcb == echo_tcb)
			echo_tcb = NULLTCB;
		break;
	}
}
SHAR_EOF
cat << \SHAR_EOF > tcptimer.c
/* TCP timeout routines */
#include <stdio.h>
#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "ip.h"
#include "tcp.h"

/* Timer timeout */
void
tcp_timeout(arg)
int *arg;
{
	register struct tcb *tcb;

	tcb = (struct tcb *)arg;
	switch(tcb->state){
	case TIME_WAIT:		/* 2MSL timer has expired */
		close_self(tcb,NORMAL);
		break;
	default:		/* Retransmission timer has expired */
		if(tcb->retry < RETRY){
			tcb->retry++;
			tcb->snd.ptr = tcb->snd.una;
			/* Back off on retransmission timer;
			 * on closed window probes, limit it to
			 * BACKOFF x the current round trip estimate
			 */
			tcb->timer.start <<= 1;
			if(tcb->snd.wnd == 0)
				tcb->timer.start =
				 min(tcb->timer.start,BACKOFF*tcb->srtt/MSPTICK);
			tcp_output(tcb);
		} else {
			/* Give up */
			close_self(tcb,TIMEOUT);
		}
	}
}
SHAR_EOF
cat << \SHAR_EOF > timer.c
#include "machdep.h"
#include "timer.h"

/* Head of running timer chain */
struct timer *timers;

tick()
{
	register struct timer *t,*tp;
	register struct timer *expired = NULLTIMER;

	/* Run through the list of running timers, decrementing each one.
	 * If one has expired, take it off the running list and put it
	 * on a singly linked list of expired timers
	 */
	for(t = timers;t != NULLTIMER; t = tp){
		tp = t->next;
		if(t->state == TIMER_RUN && --(t->count) == 0){
			stop_timer(t);
			t->state = TIMER_EXPIRE;
			/* Put on head of expired timer list */
			t->next = expired;
			expired = t;
		}
	}
	/* Now go through the list of expired timers, removing each
	 * one and kicking the notify function, if there is one
	 */
	while((t = expired) != NULLTIMER){
		expired = t->next;
		if(t->func){
			(*t->func)(t->arg);
		}
	}
}
/* Start a timer */
start_timer(t)
register struct timer *t;
{
	char i_state;

	if(t == NULLTIMER || t->start == 0)
		return;
	i_state = disable();
	t->count = t->start;
	if(t->state != TIMER_RUN){
		t->state = TIMER_RUN;
		/* Put on head of active timer list */
		t->prev = NULLTIMER;
		t->next = timers;
		if(t->next != NULLTIMER)
			t->next->prev = t;
		timers = t;
	}
	restore(i_state);
}
/* Stop a timer */
stop_timer(t)
register struct timer *t;
{
	char i_state;

	if(t == NULLTIMER)
		return;
	i_state = disable();
	if(t->state == TIMER_RUN){
		/* Delete from active timer list */
		if(timers == t)
			timers = t->next;
		if(t->next != NULLTIMER)
			t->next->prev = t->prev;
		if(t->prev != NULLTIMER)
			t->prev->next = t->next;
	}
	t->state = TIMER_STOP;
	restore(i_state);
}
SHAR_EOF
cat << \SHAR_EOF > tnserv.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"

struct tcb *tnet_tcb;
telnet_start(argc,argv)
char *argv[];
{
	struct socket lsocket;
	extern int32 ip_addr;
	void tnet_state();
	void t_state(),rcv_char();

	/* Incoming Telnet */
	lsocket.address = ip_addr;
	if(argc < 2)
		lsocket.port = TELNET_PORT;
	else
		lsocket.port = atoi(argv[1]);
	tnet_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,rcv_char,NULLVFP,tnet_state,0,(int *)NULL);
}
/* Handle incoming Telnet connect requests by creating a Telnet session,
 * then change upcall vector so it behaves like an ordinary Telnet session.
 * 
 */
static void
tnet_state(tcb,old,new)
struct tcb *tcb;
char old,new;
{
	struct telnet *tn;
	struct session *s,*newsession();
	void t_state();
	char *calloc();

	switch(new){
	case ESTABLISHED:
		log(tcb,"open Telnet");
		/* Allocate a session descriptor */
		if((s = newsession()) == NULLSESSION){
			printf("\007Incoming Telnet call from %s refused; too many sessions\r\n",
			 psocket(&tcb->conn.remote));
			fflush(stdout);
			sndmsg(tcb,"Call rejected; too many sessions on remote system\r\n");
			close_tcp(tcb);
			return;
		}
		s->type = TELNET;
		s->parse = send_tel;
		/* Create and initialize a Telnet protocol descriptor */
		if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
			printf("\007Incoming Telnet call refused; no space\r\n");
			fflush(stdout);
			sndmsg(tcb,"Call rejected; no space on remote system\r\n");
			close_tcp(tcb);
			s->type = FREE;
			return;
		}
		tn->session = s;	/* Upward pointer */
		tn->state = TS_DATA;
		s->cb.telnet = tn;	/* Downward pointer */

		tcb->user = (int *)tn;	/* Upward pointer */
		tn->tcb = tcb;		/* Downward pointer */
		printf("\007Incoming Telnet session %u from %s\r\n",s - sessions,
			psocket(&tcb->conn.remote));
		fflush(stdout);
		tcb->s_upcall = t_state;
		return;
	case CLOSED:
		/* This will only happen if the connection closed before
		 * the session was set up, e.g., if we refused it because
		 * there were too many sessions, or if the server is being
		 * shut down.
		 */
		if(tcb == tnet_tcb)
			tnet_tcb = NULLTCB;
		del_tcp(tcb);
		break;
	}
}
telnet_stop()
{
	if(tnet_tcb != NULLTCB)
		close_tcp(tnet_tcb);
}
static
sndmsg(tcb,msg)
struct tcb *tcb;
char *msg;
{
	struct mbuf *bp;

	bp = qdata(msg,(int16)strlen(msg));
	send_tcp(tcb,bp);
}
SHAR_EOF
cat << \SHAR_EOF > ttydriv.c
#include <stdio.h>
#include "machdep.h"

/* TTY input driver */
#define	NULLCHAR	(char *)NULL

int ttymode;
#define TTY_COOKED	0
#define	TTY_RAW	1

#define	LINESIZE	256

#ifdef	AMIGA
#define	CTLX	24
#endif

#define	CTLU	21

raw()
{
	ttymode = TTY_RAW;
}

cooked()
{
	ttymode = TTY_COOKED;
}

/* Accept characters from the incoming tty buffer and process them
 * (if in cooked mode) or just pass them directly (if in raw mode).
 * Returns the number of characters available for use; if non-zero,
 * also stashes a pointer to the character(s) in the "buf" argument.
 */
int
ttydriv(c,buf)
char c;
char **buf;
{
	static char linebuf[LINESIZE];
	static char *cp = linebuf;
	int cnt;

	if(buf == (char **)NULL)
		return 0;	/* paranoia check */

	cnt = 0;
	switch(ttymode){
	case TTY_RAW:
		*cp++ = c;
		cnt = cp - linebuf;
		cp = linebuf;
		break;
	case TTY_COOKED:
		/* Perform cooked-mode line editing */
		switch(c & 0x7f){
		case '\r':	/* CR and LF are equivalent */
		case '\n':
			*cp++ = '\r';
			*cp++ = '\n';
			printf("\r\n");
			cnt = cp - linebuf;
			cp = linebuf;
			break;
		case '\b':		/* Backspace */
			if(cp != linebuf){
				cp--;
				printf("\b \b");
			}
			break;
		case CTLU:	/* Line kill */
#ifdef	AMIGA
		case CTLX:
#endif
			while(cp != linebuf){
				cp--;
				printf("\b \b");
			}
			break;
		default:	/* Ordinary character */
			*cp++ = c;
#ifdef	AMIGA
			printf("%c", c);
#else
			putchar(c);
#endif
			if(cp >= &linebuf[LINESIZE]){
				cnt = cp - linebuf;
				cp = linebuf;
			}
			break;
		}
	}
	if(cnt != 0)
		*buf = linebuf;
	else
		*buf = NULLCHAR;
	fflush(stdout);
	return cnt;
}
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.