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.