DIRECTORY Arpa USING [Address, nullAddress], ArpaBuf USING [Buffer, FragmentOffset, maxBodyHWords, maxBytes, minIHL], ArpaExtras USING [IsBroadcast, MyAddress], ArpaICMPBuf USING [Body, Buffer, hdrBytes, maxEchoDataBytes], ArpaIP USING [GetUserBytes], ArpaName USING [ReplyStatus, NameToAddress], ArpaUDP USING [GetUserBytes], ArpaUDPBuf USING [Buffer, maxBodyHWords], Basics USING [Card32FromF, Card16FromH, FFromCard32, FWORD, HFromCard16, HWORD], BasicTime USING [GMT, Now], Commander USING [CommandProc, Register], CommBuffer USING [Overhead], CommDriver USING [Buffer, BufferObject, CreateInterceptor, DestroyInterceptor, GetNetworkChain, Interceptor, Network, RecvInterceptor, SendInterceptor], Convert USING [RopeFromCard], ConvertExtras USING [RopeFromArpaAddress], GenericTool USING [ButtonProc, CreateInstance, PutMsgRope, ToolHandle], IO USING [Error, PutChar, PutF, PutFR, STREAM], Process USING [ConditionPointer, EnableAborts, Pause, SecondsToTicks, SetTimeout], Rope USING [Concat, FromChar, Length, ROPE], ViewerClasses USING [Viewer]; OfflineArpaSpyTool: CEDAR MONITOR IMPORTS ArpaBuf, ArpaExtras, ArpaIP, ArpaName, ArpaUDP, Basics, BasicTime, Commander, CommDriver, Convert, ConvertExtras, GenericTool, IO, Process, Rope ~ BEGIN ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; Viewer: TYPE ~ ViewerClasses.Viewer; FWORD: TYPE ~ Basics.FWORD; HWORD: TYPE ~ Basics.HWORD; ToolHandle: TYPE ~ GenericTool.ToolHandle; ButtonProc: TYPE ~ GenericTool.ButtonProc; thisHost: Arpa.Address _ ArpaExtras.MyAddress[]; RopeFromNetTime: PROC [netTime: FWORD] RETURNS [rope: ROPE] = { seconds, fraction, ms: CARD; ms _ CfF[netTime]; IF ms < 0 THEN RETURN["Unknown"]; seconds _ ms/1000; fraction _ ms MOD 1000; RETURN[IO.PutFR["%R.%03G", [integer[seconds]], [integer[fraction]] ]]; }; toolName: ROPE ~ "OfflineArpaSpyTool"; globalToolHandle: ToolHandle; RunningType: TYPE ~ { none, catch, show, stats }; ControlHandle: TYPE ~ REF ControlObject; ControlObject: TYPE ~ RECORD [ running: RunningType _ none, stop: ButtonProc _ Stop, catch: ButtonProc _ Catch, show: ButtonProc _ Show, stats: ButtonProc _ Stats, putICMP: BOOL _ FALSE, putUDP: BOOL _ FALSE, putTCP: BOOL _ FALSE, putBcst: BOOL _ FALSE, -- packets sent to IP broadcast address putOther: BOOL _ FALSE, shortFormat: BOOL _ FALSE, maxLength: CARDINAL _ ArpaBuf.maxBytes, buffers: CARDINAL _ 32, thisMachine: ROPE, to: ROPE, from: ROPE]; DataHandle: TYPE ~ REF DataObject; DataObject: TYPE ~ RECORD [ interceptor: CommDriver.Interceptor _ NIL, printQHead, printQTail, printQFree: PrintBuf, printQNonempty: CONDITION, pleaseStop: BOOL _ FALSE, promiscuous: BOOL _ FALSE, fromHost: Arpa.Address _ Arpa.nullAddress, toHost: Arpa.Address _ Arpa.nullAddress, nTooLong: INT _ 0, nICMP: INT _ 0, nUDP: INT _ 0, nTCP: INT _ 0, nBcst: INT _ 0, nOther: INT _ 0 ]; Create: ENTRY PROC RETURNS [created: BOOL] ~ { cH: ControlHandle; dH: DataHandle; IF globalToolHandle # NIL THEN RETURN [FALSE]; cH _ NEW[ControlObject _ [thisMachine~ConvertExtras.RopeFromArpaAddress[thisHost]]]; dH _ NEW[DataObject _ []]; TRUSTED { InitCond[@dH.printQNonempty] }; globalToolHandle _ GenericTool.CreateInstance[ toolName~toolName, control~cH, options~LIST[ ["thisMachine", readonly[]], ["to", notify[GetFilterAddresses]], ["from", notify[GetFilterAddresses]] ], data~dH, preDestroy~PreDestroy ]; RETURN [TRUE]; }; InitCond: UNSAFE PROC [cP: Process.ConditionPointer] ~ UNCHECKED { Process.EnableAborts[cP]; Process.SetTimeout[cP, Process.SecondsToTicks[1]]; }; Enter: ENTRY PROC [tH: ToolHandle, me: RunningType] RETURNS [entered: BOOL] ~ { cH: ControlHandle ~ NARROW[tH.control]; IF cH.running # none THEN { GenericTool.PutMsgRope[tH, "Tool is busy", TRUE]; RETURN [FALSE] }; cH.running _ me; RETURN [TRUE]; }; Exit: PROC [tH: ToolHandle] ~ { cH: ControlHandle ~ NARROW[tH.control]; cH.running _ none }; Stop: ButtonProc ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle ~ NARROW[tH.data]; WHILE cH.running # none DO dH.pleaseStop _ TRUE; Process.Pause[ Process.SecondsToTicks[1] ]; ENDLOOP; dH.pleaseStop _ FALSE; }; PreDestroy: ButtonProc ~ { Stop[tH]; globalToolHandle _ NIL }; GetFilterAddresses: ButtonProc ~ { dH: DataHandle ~ NARROW[tH.data]; cH: ControlHandle ~ NARROW[tH.control]; gotTo, gotFrom: BOOL; status: ArpaName.ReplyStatus; source: Arpa.Address; gotTo _ gotFrom _ FALSE; dH.toHost _ dH.fromHost _ thisHost; IF Rope.Length[cH.to] > 0 THEN { [dH.toHost, status, source] _ ArpaName.NameToAddress[cH.to]; SELECT status FROM bogus => GenericTool.PutMsgRope[tH,"Invalid name in To field.\n", TRUE]; down => GenericTool.PutMsgRope[tH,"Name servers not responding.\n", TRUE]; other => GenericTool.PutMsgRope[tH,"No address for To field name.\n", TRUE]; ENDCASE; IF dH.toHost # Arpa.nullAddress THEN gotTo _ TRUE ELSE GenericTool.PutMsgRope[tH, "Unable to load address for To field name.\n", TRUE]; }; IF Rope.Length[cH.from] > 0 THEN { [dH.fromHost, status, source] _ ArpaName.NameToAddress[cH.from]; SELECT status FROM bogus => GenericTool.PutMsgRope[tH, "Invalid name in From field.\n", TRUE]; down => GenericTool.PutMsgRope[tH, "Name servers not responding.\n", TRUE]; other => GenericTool.PutMsgRope[tH, "No address for From field name.\n", TRUE]; ENDCASE; IF dH.fromHost # Arpa.nullAddress THEN gotFrom _ TRUE ELSE GenericTool.PutMsgRope[tH, "Unable to load address for From field name.\n", TRUE]}; dH.promiscuous _ (gotTo OR gotFrom); }; Stats: ButtonProc ~ { dH: DataHandle ~ NARROW[tH.data]; cH: ControlHandle ~ NARROW[tH.control]; IF NOT Enter[tH, stats] THEN RETURN; { ENABLE IO.Error, ABORTED => CONTINUE; IO.PutF[tH.out, "\n%7g ICMP\n%7g UDP\n%7g TCP\n%7g Other\n", [integer[dH.nICMP]], [integer[dH.nUDP]], [integer[dH.nTCP]], [integer[dH.nOther]] ]; IO.PutF[tH.out, "\n%7g dropped (too long)\n", [integer[dH.nTooLong]] ]; IO.PutF[tH.out, "\n%7g total\n\n", [integer[ dH.nICMP+dH.nUDP+dH.nTCP+dH.nOther]] ]; }; Exit[tH]; }; HfC: PROC [c: CARDINAL] RETURNS [HWORD] ~ INLINE { RETURN [Basics.HFromCard16[c]] }; FfC: PROC [c: LONG CARDINAL] RETURNS [FWORD] ~ INLINE { RETURN [Basics.FFromCard32[c]] }; CfH: PROC [h: HWORD] RETURNS [CARDINAL] ~ INLINE { RETURN [Basics.Card16FromH[h]] }; CfF: PROC [f: FWORD] RETURNS [LONG CARDINAL] ~ INLINE { RETURN [Basics.Card32FromF[f]] }; DisplayArpaBuf: PROC [s: STREAM, b: ArpaBuf.Buffer, heading: ROPE, bufBytes: NAT, when: BasicTime.GMT, short: BOOL] ~ { PutHdr: PROC [typeName: ROPE] ~ { IO.PutF[s, "Arpa %g (%g) %g:\n", [rope[heading]], [cardinal[bufBytes]], [time[when]] ]; IO.PutF[s, "%g->%g, ihl %g, len %g ttl %g", [rope[ConvertExtras.RopeFromArpaAddress[b.hdr1.source]]], [rope[ConvertExtras.RopeFromArpaAddress[b.hdr1.dest]]], [cardinal[b.hdr1.ihl]], [cardinal[CfH[b.hdr1.length]]], [cardinal[b.hdr1.timeToLive]] ]; IO.PutF[s, ", id %x, frag %x\n", [cardinal[CfH[b.hdr1.fragmentId]]], [cardinal[CfH[b.hdr1.fragmentCtl]]] ]; }; bodyBytes, optionsBytes: CARDINAL; firstFragment, optionsPresent, simple: BOOL; [bodyBytes, optionsBytes] _ ArpaIP.GetUserBytes[b]; firstFragment _ (ArpaBuf.FragmentOffset[b] = 0); optionsPresent _ (optionsBytes # 0); simple _ firstFragment AND (NOT optionsPresent); SELECT b.hdr1.protocol FROM icmp => { PutHdr["ICMP"]; IF simple THEN TRUSTED { PutICMPBuf[s, LOOPHOLE[b], bodyBytes, short] } ELSE PutOtherBuf[s, b, bodyBytes, short]; }; udp => { PutHdr["UDP"]; IF simple THEN TRUSTED { PutUDPBuf[s, LOOPHOLE[b], bodyBytes, short] } ELSE PutOtherBuf[s, b, bodyBytes, short]; }; tcp => { PutHdr["TCP"]; IF simple THEN TRUSTED { PutTCPBuf[s, LOOPHOLE[b], bodyBytes, short] } ELSE PutOtherBuf[s, b, bodyBytes, short]; }; ENDCASE => { PutHdr[Convert.RopeFromCard[ORD[b.hdr1.protocol], 16]]; PutOtherBuf[s, b, bodyBytes, short]; }; }; PutICMPBuf: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL] ~ { SELECT b.hdr2.icmpType FROM echoReply => PutEchoRequestOrReply[s, b, bytes, short, "echoReply"]; destUnreachable => PutReturnedData[s, b, bytes, short, "destUnreachable"]; sourceQuench => PutReturnedData[s, b, bytes, short, "sourceQuench"]; redirect => PutReturnedDataAndAddress[s, b, bytes, short, "redirect"]; echo => PutEchoRequestOrReply[s, b, bytes, short, "echo"]; timeExceeded => PutReturnedData[s, b, bytes, short, "timeExceeded"]; parameterProblem => PutReturnedDataAndPointer[s, b, bytes, short, "parameterProblem"]; timestamp => PutTimes[s, b, bytes, short, "timestamp"]; timestampReply => PutTimes[s, b, bytes, short, "timestampReply"]; infoRequest => PutInfoRequestOrReply[s, b, bytes, short, "infoRequest"]; infoReply => PutInfoRequestOrReply[s, b, bytes, short, "infoReply"]; ENDCASE => { IO.PutF[s, "type %g code %g\n", [cardinal[b.hdr2.icmpType.ORD]], [cardinal[b.hdr2.icmpCode]] ]; }; }; PutEchoRequestOrReply: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL, title: ROPE] ~ { i: CARDINAL; IO.PutF[s, "%g code %g id %g seq %g\n", [rope[title]], [cardinal[b.hdr2.icmpCode]], [cardinal[CfH[b.body.echo.identifier]]], [cardinal[CfH[b.body.echo.sequenceNum]]] ]; bytes _ bytes - ArpaICMPBuf.hdrBytes - (BYTES[ArpaICMPBuf.Body.echo] - ArpaICMPBuf.maxEchoDataBytes); i _ 0; WHILE i < bytes DO THROUGH [1..10] DO IF i < ArpaICMPBuf.maxEchoDataBytes THEN IO.PutF[s, "%4x ", [cardinal[b.body.echo.data[i]]] ]; i _ i + 1; ENDLOOP; IO.PutChar[s, '\n]; IF short THEN EXIT; ENDLOOP; }; PutOrigHdr: PROC [s: STREAM, b: ArpaICMPBuf.Buffer] ~ { IO.PutF[s, "%g->%g, ihl %g, len %g ttl %g", [rope[ConvertExtras.RopeFromArpaAddress[b.body.destUnreachable.origHdr.source]]], [rope[ConvertExtras.RopeFromArpaAddress[b.body.destUnreachable.origHdr.dest]]], [cardinal[b.body.destUnreachable.origHdr.ihl]], [cardinal[CfH[b.body.destUnreachable.origHdr.length]]], [cardinal[b.body.destUnreachable.origHdr.timeToLive]] ]; IO.PutF[s, ", id %x, frag %x\n", [cardinal[CfH[b.body.destUnreachable.origHdr.fragmentId]]], [cardinal[CfH[b.body.destUnreachable.origHdr.fragmentCtl]]] ]; }; PutReturnedData: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL, title: ROPE] ~ { IO.PutF[s, "%g code %g\n", [rope[title]], [cardinal[b.hdr2.icmpCode]] ]; PutOrigHdr[s, b]; }; PutReturnedDataAndAddress: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL, title: ROPE] ~ { IO.PutF[s, "%g code %g adr %g\n", [rope[title]], [cardinal[b.hdr2.icmpCode]], [rope[ConvertExtras.RopeFromArpaAddress[b.body.redirect.address]]] ]; PutOrigHdr[s, b]; }; PutReturnedDataAndPointer: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL, title: ROPE] ~ { IO.PutF[s, "%g code %g ptr %g\n", [rope[title]], [cardinal[b.hdr2.icmpCode]], [cardinal[b.body.parameterProblem.pointer]] ]; PutOrigHdr[s, b]; }; PutTimes: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL, title: ROPE] ~ { IO.PutF[s, "%g code %g id %g seq %g\n", [rope[title]], [cardinal[b.hdr2.icmpCode]], [cardinal[CfH[b.body.timestamp.identifier]]], [cardinal[CfH[b.body.timestamp.sequenceNum]]] ]; IO.PutF[s, "orig %g revc %g send %g\n", [rope[RopeFromNetTime[b.body.timestamp.originateTimestamp]]], [rope[RopeFromNetTime[b.body.timestamp.receiveTimestamp]]], [rope[RopeFromNetTime[b.body.timestamp.transmitTimestamp]]] ]; }; PutInfoRequestOrReply: PROC [s: STREAM, b: ArpaICMPBuf.Buffer, bytes: CARDINAL, short: BOOL, title: ROPE] ~ { IO.PutF[s, "%g code %g id %g seq %g\n", [rope[title]], [cardinal[b.hdr2.icmpCode]], [cardinal[CfH[b.body.infoRequest.identifier]]], [cardinal[CfH[b.body.infoRequest.sequenceNum]]] ]; }; PutUDPBuf: PROC [s: STREAM, b: ArpaUDPBuf.Buffer, bytes: CARDINAL, short: BOOL] ~ { i, words: CARDINAL; words _ MIN[((ArpaUDP.GetUserBytes[b]+BYTES[HWORD]-1) / BYTES[HWORD]), ArpaUDPBuf.maxBodyHWords]; IO.PutF[s, "ports %g->%g len %g\n", [cardinal[CfH[b.hdr2.sourcePort]]], [cardinal[CfH[b.hdr2.destPort]]], [cardinal[CfH[b.hdr2.length]]] ]; i _ 0; WHILE i < words DO THROUGH [1..10] WHILE i < words DO IO.PutF[s, "%4x ", [cardinal[CfH[b.body.hWords[i]]]] ]; i _ i + 1; ENDLOOP; IO.PutChar[s, '\n]; IF short THEN EXIT; ENDLOOP; }; PutTCPBuf: PROC [s: STREAM, b: ArpaBuf.Buffer, bytes: CARDINAL, short: BOOL] ~ { PutOtherBuf[s, b, bytes, short]; }; PutOtherBuf: PROC [s: STREAM, b: ArpaBuf.Buffer, bytes: CARDINAL, short: BOOL] ~ { i: CARDINAL _ 0; words: CARDINAL _ MIN[((bytes+BYTES[HWORD]-1) / BYTES[HWORD]), ArpaBuf.maxBodyHWords]; WHILE i < words DO THROUGH [1..10] WHILE i < words DO IO.PutF[s, "%4x ", [cardinal[CfH[b.body.hWords[i]]]] ]; i _ i + 1; ENDLOOP; IO.PutChar[s, '\n]; IF short THEN EXIT; ENDLOOP; }; Recv: CommDriver.RecvInterceptor ~ { tH: ToolHandle; dH: DataHandle; cH: ControlHandle; tH _ NARROW[data]; dH _ NARROW[tH.data]; cH _ NARROW[tH.control]; IF bytes > cH.maxLength THEN { dH.nTooLong _ dH.nTooLong.SUCC; RETURN }; SELECT recv FROM arpa, arpaTranslate => { b: ArpaBuf.Buffer; TRUSTED { b _ LOOPHOLE[buffer] }; IF dH.promiscuous THEN { SELECT TRUE FROM (b.hdr1.source = dH.fromHost) => { PostPrintRequest[tH~tH, kind~arpa, buffer~buffer, direction~send, bytes~bytes] }; (b.hdr1.dest = dH.toHost) => { PostPrintRequest[tH~tH, kind~arpa, buffer~buffer, direction~recv, bytes~bytes] }; (ArpaExtras.IsBroadcast[b.hdr1.dest]) => { PostPrintRequest[tH~tH, kind~arpa, buffer~buffer, direction~recv, bytes~bytes] }; ENDCASE; } ELSE { PostPrintRequest[tH~tH, kind~arpa, buffer~buffer, direction~recv, bytes~bytes]; }; }; ENDCASE => NULL; }; Send: CommDriver.SendInterceptor ~ { tH: ToolHandle; dH: DataHandle; cH: ControlHandle; tH _ NARROW[data]; dH _ NARROW[tH.data]; cH _ NARROW[tH.control]; IF dH.promiscuous THEN RETURN; IF bytes > cH.maxLength THEN { dH.nTooLong _ dH.nTooLong.SUCC; RETURN }; SELECT send FROM arpa, arpaReturn => { b: ArpaBuf.Buffer; TRUSTED { b _ LOOPHOLE[buffer] }; PostPrintRequest[tH~tH, kind~arpa, buffer~buffer, direction~send, bytes~bytes]; }; ENDCASE => NULL; }; Install: ENTRY PROC [tH: ToolHandle] ~ { dH: DataHandle ~ NARROW[tH.data]; network: CommDriver.Network ~ CommDriver.GetNetworkChain[]; IF dH.interceptor # NIL THEN RETURN; IF network = NIL THEN RETURN; dH.interceptor _ CommDriver.CreateInterceptor[ network~network, sendMask~[arpa~TRUE, arpaReturn~TRUE, arpaTranslate~FALSE, xns~FALSE, xnsReturn~FALSE, xnsTranslate~FALSE, pup~FALSE, pupReturn~FALSE, pupTranslate~FALSE, other~FALSE, otherReturn~FALSE, otherTranslate~FALSE, raw~FALSE], sendProc~Send, recvMask~[arpa~TRUE, arpaTranslate~FALSE, xns~FALSE, xnsTranslate~FALSE, pup~FALSE, pupTranslate~FALSE, other~FALSE, otherTranslate~FALSE, error~FALSE], recvProc~Recv, data~tH, promiscuous~dH.promiscuous]; }; UnInstall: ENTRY PROC [tH: ToolHandle] ~ { dH: DataHandle ~ NARROW[tH.data]; IF dH.interceptor = NIL THEN RETURN; CommDriver.DestroyInterceptor[dH.interceptor]; dH.interceptor _ NIL }; BufKind: TYPE ~ { arpa, unknown }; BufDirection: TYPE ~ { send, recv }; PrintBuf: TYPE ~ REF PrintBufObject; PrintBufObject: TYPE ~ RECORD [ bufObject: CommDriver.BufferObject, bytes: NAT, time: BasicTime.GMT, kind: BufKind, direction: BufDirection, next: PrintBuf]; FlushPrintQueue: ENTRY PROC [cH: ControlHandle, dH: DataHandle] ~ { dH.printQHead _ dH.printQTail _ NIL; THROUGH [1 .. cH.buffers] DO temp: PrintBuf _ NEW[PrintBufObject]; temp.next _ dH.printQFree; dH.printQFree _ temp; ENDLOOP; }; EnqueuePrintBuf: ENTRY PROC [dH: DataHandle, b: PrintBuf] ~ INLINE { IF dH.printQHead = NIL THEN { dH.printQHead _ dH.printQTail _ b; } ELSE { dH.printQTail.next _ b; dH.printQTail _ b }; b.next _ NIL }; AllocPrintBuf: ENTRY PROC [dH: DataHandle] RETURNS [b: PrintBuf] ~ INLINE { IF (b _ dH.printQFree) # NIL THEN { dH.printQFree _ b.next; b.next _ NIL } ELSE { b _ dH.printQHead; IF b = NIL THEN ERROR; dH.printQHead _ b.next; b.next _ NIL; }; }; PostPrintRequest: PROC [tH: ToolHandle, kind: BufKind, buffer: CommDriver.Buffer, direction: BufDirection, bytes: NAT] ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle _ NARROW[tH.data]; b: PrintBuf; SELECT kind FROM arpa => { ab: ArpaBuf.Buffer; TRUSTED { ab _ LOOPHOLE[buffer] }; IF ArpaExtras.IsBroadcast[ab.hdr1.dest] THEN { dH.nBcst _ dH.nBcst + 1; IF NOT cH.putBcst THEN RETURN }; SELECT ab.hdr1.protocol FROM icmp => { dH.nICMP _ dH.nICMP + 1; IF NOT cH.putICMP THEN RETURN }; udp => { dH.nUDP _ dH.nUDP + 1; IF NOT cH.putUDP THEN RETURN }; tcp => { dH.nTCP _ dH.nTCP + 1; IF NOT cH.putTCP THEN RETURN }; ENDCASE => { dH.nOther _ dH.nOther + 1; IF NOT cH.putOther THEN RETURN }; }; ENDCASE => ERROR; b _ AllocPrintBuf[dH]; b.bufObject _ buffer^; b.bytes _ bytes; b.bufObject.ovh.next _ NIL; b.bufObject.ovh.network _ NIL; b.time _ BasicTime.Now[]; b.kind _ kind; b.direction _ direction; EnqueuePrintBuf[dH, b]; }; Catch: PROC [tH: ToolHandle] ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle ~ NARROW[tH.data]; IF NOT Enter[tH, catch] THEN RETURN; FlushPrintQueue[cH, dH]; Install[tH]; IO.PutF[tH.out, "\nCatching at %g\n\n", [time[BasicTime.Now[]]] ]; DO ENABLE ABORTED => EXIT; IF dH.pleaseStop THEN EXIT; Process.Pause[ Process.SecondsToTicks[1] ]; -- ugh! ENDLOOP; IO.PutF[tH.out, "\n\nStopped %g\n", [time[BasicTime.Now[]]] ]; UnInstall[tH]; Exit[tH]; }; Show: PROC [tH: ToolHandle] ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle ~ NARROW[tH.data]; IF NOT Enter[tH, show] THEN RETURN; IO.PutF[tH.out, "\nShowing at %g\n\n", [time[BasicTime.Now[]]] ]; FOR b: PrintBuf _ dH.printQHead, b.next WHILE b # NIL DO ENABLE IO.Error, ABORTED => EXIT; IF dH.pleaseStop THEN EXIT; SELECT b.direction FROM send => SELECT b.kind FROM arpa => TRUSTED { DisplayArpaBuf[tH.out, LOOPHOLE[b], "Send", b.bytes, b.time, cH.shortFormat] }; ENDCASE => ERROR; recv => SELECT b.kind FROM arpa => TRUSTED { DisplayArpaBuf[tH.out, LOOPHOLE[b], "Recv", b.bytes, b.time, cH.shortFormat] }; ENDCASE => ERROR; ENDCASE; IO.PutChar[tH.out, '\n]; ENDLOOP; IO.PutF[tH.out, "\n\nDone showing at %g\n", [time[BasicTime.Now[]]] ]; Exit[tH]; }; CvtRopeFromCardinals: PROC [args: LIST OF CARDINAL] RETURNS [result: ROPE _ NIL] ~ { WHILE args # NIL DO n: CARDINAL ~ args.first; result _ Rope.Concat[result, Rope.FromChar[VAL[n/256]]]; result _ Rope.Concat[result, Rope.FromChar[VAL[n MOD 256]]]; args _ args.rest; ENDLOOP; }; Go: Commander.CommandProc ~ { IF Create[] THEN RETURN [result~$Done] ELSE RETURN [result~$Failure] }; Commander.Register[key~toolName, proc~Go]; END. ¬OfflineArpaSpyTool.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Demers, March 8, 1988 3:15:08 pm PST John Larson, November 9, 1987 8:54:49 pm PST Tool to watch all Arpa packets received, using ring buffer, printing and formatting them off line. Utilities that belong in convert Tool Creation Print Utilities Arpa Buffers Print an Arpa buffer on s. No assumptions about encapsulation. Print generic part of a TCP packet. NOT YET IMPLEMENTED. Send and Receive Interceptor Procs [recv: RecvType, data: REF ANY, network: Network, buffer: Buffer, bytes: NAT] RETURNS [kill: BOOL _ FALSE] [send: SendType, data: REF ANY, network: Network, buffer: Buffer, bytes: NAT] RETURNS [kill: BOOL _ FALSE]] Print Daemon The Daemon actually prints buffers that have been queued by Recv and Translate procs. Conversion Procs (for use from interpreter) Initialization [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] Κ˜codešœ™Kšœ Οmœ1™J˜Jšœ ˜ K˜K˜—š œžœ˜ Kšœžœ ˜'Kšœžœ ˜!Kšžœžœžœžœ˜#Kšžœ?˜Ašžœ%žœžœž˜8Kšžœžœžœžœ˜!Kšžœžœžœ˜šžœ ž˜šœžœž˜šœžœ˜Kšœžœ0˜O—Kšžœžœ˜—šœžœž˜šœžœ˜Kšœžœ0˜O—Kšžœžœ˜—Kšžœ˜—Kšžœ˜Kšžœ˜—KšžœD˜FJšœ ˜ K˜—K˜—™+š œžœžœžœžœžœ žœžœ˜Tšžœžœž˜Kšœžœ˜Kšœ+žœ ˜8Kšœ+žœžœ˜