DIRECTORY BasicTime USING [earliestGMT, GMT, Now], Commander USING [CommandProc, Register], CommBuffer USING [Overhead], CommDriver USING [Buffer, BufferObject, CreateInterceptor, DestroyInterceptor, GetNetworkChain, Interceptor, Network, RecvInterceptor, SendInterceptor], Convert USING [RopeFromCard], Endian USING[BYTE, CardFromF, CardFromH, FFromCard, FWORD, HFromCard, HWORD], GenericTool USING [ButtonProc, CreateInstance, PutMsgRope, ToolHandle], IO USING [Error, PutChar, PutF, PutFR, PutRope, STREAM], PrincOpsUtils USING [LongMove], Process USING [ConditionPointer, EnableAborts, Pause, SecondsToTicks, SetTimeout], Pup USING [Host], Rope USING [Concat, FromChar, Length, ROPE], ViewerClasses USING [Viewer], XNS USING [Address, broadcastHost, GetThisHost, Host, Net, unknownHost], XNSAddressParsing USING [AddressFromRope, Error, Format, MyRope, RopeFromAddress], XNSBuf USING [Buffer, hdrBytes], XNSEchoBuf USING [Buffer], XNSErrorBuf USING [Buffer, minBodyBytes], XNSErrorTypes USING [badChecksumErr, ErrorType, invalidPacketTypeErr, listenerRejectErr, noSocketErr, protocolViolationErr, resourceLimitsErr], XNSExchangeBuf USING [Buffer, hdrBytes], XNSExchangeTypes USING [ExchangeType, clearinghouseServiceType, teledebugType, timeServiceType], XNSRoutingBuf USING [Buffer, NumTuplesFromUserBytes], XNSSocket USING [GetUserBytes], XNSSPPBuf USING [Buffer, hdrBytes], XNSSPPTypes USING []; XNSSpyTool: CEDAR MONITOR IMPORTS BasicTime, Commander, CommDriver, Convert, Endian, GenericTool, IO, PrincOpsUtils, Process, Rope, XNS, XNSAddressParsing, XNSRoutingBuf, XNSSocket ~ BEGIN ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; Viewer: TYPE ~ ViewerClasses.Viewer; BYTE: TYPE ~ Endian.BYTE; FWORD: TYPE ~ Endian.FWORD; HWORD: TYPE ~ Endian.HWORD; ToolHandle: TYPE ~ GenericTool.ToolHandle; ButtonProc: TYPE ~ GenericTool.ButtonProc; thisHost: XNS.Host _ XNS.GetThisHost[]; toolName: ROPE ~ "XNSSpyTool"; globalToolHandle: ToolHandle; RunningType: TYPE ~ { none, watch, stats }; ControlHandle: TYPE ~ REF ControlObject; ControlObject: TYPE ~ RECORD [ running: RunningType _ none, stop: ButtonProc _ Stop, watch: ButtonProc _ Watch, stats: ButtonProc _ Stats, putRouting: BOOL _ FALSE, putEcho: BOOL _ FALSE, putError: BOOL _ FALSE, putExchange: BOOL _ FALSE, putSPP: BOOL _ FALSE, putTranslation: BOOL _ FALSE, shortFormat: BOOL _ FALSE, maxLength: CARDINAL _ 576, 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: XNS.Host _ XNS.unknownHost, toHost: XNS.Host _ XNS.unknownHost, nTooLong: INT _ 0, nEcho: INT _ 0, nError: INT _ 0, nExchange: INT _ 0, nRouting: INT _ 0, nSPP: INT _ 0, nTranslation: INT _ 0 ]; Create: ENTRY PROC RETURNS [created: BOOL] ~ { cH: ControlHandle; dH: DataHandle; IF globalToolHandle # NIL THEN RETURN [FALSE]; cH _ NEW[ControlObject _ [thisMachine~XNSAddressParsing.MyRope[]]]; 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; { ENABLE XNSAddressParsing.Error => { GenericTool.PutMsgRope[tH, text, TRUE]; CONTINUE }; gotTo _ gotFrom _ FALSE; dH.toHost _ dH.fromHost _ thisHost; IF Rope.Length[cH.to] > 0 THEN { dH.toHost _ XNSAddressParsing.AddressFromRope[cH.to].host; gotTo _ TRUE }; IF Rope.Length[cH.from] > 0 THEN { dH.fromHost _ XNSAddressParsing.AddressFromRope[cH.from].host; gotFrom _ 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 echo\n%7g error\n%7g exchange\n%7g routing\n%7g spp\n", [integer[dH.nEcho]], [integer[dH.nError]], [integer[dH.nExchange]], [integer[dH.nRouting]], [integer[dH.nSPP]] ]; IO.PutF[tH.out, "\n%7g translation\n", [integer[dH.nTranslation]] ]; IO.PutF[tH.out, "\n%7g dropped (too long)\n", [integer[dH.nTooLong]] ]; IO.PutF[tH.out, "\n%7g total\n\n", [integer[ dH.nEcho+dH.nError+dH.nExchange+dH.nRouting+dH.nSPP+dH.nTranslation]] ]; }; Exit[tH]; }; HfC: PROC [c: CARDINAL] RETURNS [HWORD] ~ INLINE { RETURN [Endian.HFromCard[c]] }; FfC: PROC [c: LONG CARDINAL] RETURNS [FWORD] ~ INLINE { RETURN [Endian.FFromCard[c]] }; CfH: PROC [h: HWORD] RETURNS [CARDINAL] ~ INLINE { RETURN [Endian.CardFromH[h]] }; CfF: PROC [f: FWORD] RETURNS [LONG CARDINAL] ~ INLINE { RETURN [Endian.CardFromF[f]] }; RopeFromHost: PROC[h: XNS.Host] RETURNS[r: ROPE] ~ { temp: MACHINE DEPENDENT RECORD[host1: FWORD, host2: HWORD] ~ LOOPHOLE[h]; r _ IO.PutFR["%x%04xH", [cardinal[CfF[temp.host1]]], [cardinal[CfH[temp.host2]]] ]; }; PutAddress: PROC[s: STREAM, a: XNS.Address] ~ { temp: MACHINE DEPENDENT RECORD[ net: FWORD, host1: FWORD, host2: HWORD, socket: HWORD] ~ LOOPHOLE[a]; IO.PutF[s, "%xH.%x%04xH.%xH", [cardinal[CfF[temp.net]]], [cardinal[CfF[temp.host1]]], [cardinal[CfH[temp.host2]]], [cardinal[CfH[temp.socket]]] ]; }; DisplayXNSBuf: PROC [s: STREAM, b: XNSBuf.Buffer, heading: ROPE, when: BasicTime.GMT, short: BOOL] ~ { PutHdr: PROC [typeName: ROPE] ~ { IO.PutF[s, "XNS %g %g:\nlen %g, dst ", [rope[heading]], [time[when]], [cardinal[CfH[b.hdr1.length]]] ]; PutAddress[s, b.hdr1.dest]; IO.PutRope[s, ", src "]; PutAddress[s, b.hdr1.source]; IO.PutF[s, ", type %g\n", [rope[typeName]] ]; }; SELECT b.hdr1.type FROM routing => { PutHdr["routing"]; TRUSTED { PutRoutingBuf[s, LOOPHOLE[b], short] }}; echo => { PutHdr["echo"]; TRUSTED { PutEchoBuf[s, LOOPHOLE[b], short] }}; error => { PutHdr["error"]; TRUSTED { PutErrorBuf[s, LOOPHOLE[b], short] }}; exchange => { PutHdr["exchange"]; TRUSTED { PutExchangeBuf[s, LOOPHOLE[b], short] }}; spp => { PutHdr["spp"]; TRUSTED { PutSPPBuf[s, LOOPHOLE[b], short] }}; ENDCASE => { PutHdr[Convert.RopeFromCard[ORD[b.hdr1.type], 16]] }; }; PutRoutingBuf: PROC [s: STREAM, b: XNSRoutingBuf.Buffer, short: BOOL] ~ { numTuples: NAT ~ XNSRoutingBuf.NumTuplesFromUserBytes[b.hdr1.length - XNSBuf.hdrBytes]; numToPrint: NAT; IO.PutF[s, "routingType %g, %g tuples:\n", [rope[ SELECT b.hdr2.type FROM request => "request", response => "response", ENDCASE => "???"]], [cardinal[numTuples]] ]; numToPrint _ numTuples; IF short THEN numToPrint _ MIN[numToPrint, 5]; FOR i: CARDINAL IN [0 .. numToPrint) DO IO.PutF[s, "[%09x,%2g] ", [cardinal[CfF[LOOPHOLE[b.body.tuples[i].net]]]], [cardinal[CfH[b.body.tuples[i].delay]]] ]; ENDLOOP; IF numToPrint < numTuples THEN IO.PutRope[s, " ..."]; IO.PutChar[s, '\n]; }; PutEchoBuf: PROC [s: STREAM, b: XNSEchoBuf.Buffer, short: BOOL] ~ { IO.PutF[s, "echoType %g\n", [rope[SELECT b.hdr2.type FROM request => "request", reply => "reply", ENDCASE => Convert.RopeFromCard[ORD[b.hdr2.type]]]] ]; }; RopeFromErrorType: PROC [t: XNSErrorTypes.ErrorType] RETURNS [r: ROPE] ~ INLINE { SELECT TRUE FROM t = XNSErrorTypes.badChecksumErr => r _ "badChecksum"; t = XNSErrorTypes.noSocketErr => r _ "noSocket"; t = XNSErrorTypes.resourceLimitsErr => r _ "resourceLimit"; t = XNSErrorTypes.listenerRejectErr => r _ "listenerReject"; t = XNSErrorTypes.invalidPacketTypeErr => r _ "invalidPacketType"; t = XNSErrorTypes.protocolViolationErr => r _ "protocolViolation"; ENDCASE => r _ Convert.RopeFromCard[t.a*256+t.b, 16]; }; PutErrorBuf: PROC [s: STREAM, b: XNSErrorBuf.Buffer, short: BOOL] ~ { IO.PutF[s, "errorType %g, param %g\n", [rope[ RopeFromErrorType[b.hdr2.type] ]], [cardinal[CfH[b.hdr2.param]]] ]; IF NOT short THEN { nsb: XNSBuf.Buffer; TRUSTED { nsb _ LOOPHOLE[b] }; IF XNSSocket.GetUserBytes[nsb] >= XNSErrorBuf.minBodyBytes THEN TRUSTED { PrincOpsUtils.LongMove[ from: @b.body, nwords: (XNSErrorBuf.minBodyBytes+1)/2, to: @nsb.hdr1]; DisplayXNSBuf[s, nsb, "error body", BasicTime.earliestGMT, TRUE]; } ELSE { IO.PutF[s, "(short body)\n"]; }; }; }; RopeFromExchangeType: PROC [t: XNSExchangeTypes.ExchangeType] RETURNS [r: ROPE] ~ INLINE { SELECT TRUE FROM t = XNSExchangeTypes.timeServiceType => r _ "timeService"; t = XNSExchangeTypes.clearinghouseServiceType => r _ "clearinghouseService"; t = XNSExchangeTypes.teledebugType => r _ "teledebug"; ENDCASE => r _ Convert.RopeFromCard[t.a*256+t.b, 16]; }; PutExchangeBuf: PROC [s: STREAM, b: XNSExchangeBuf.Buffer, short: BOOL] ~ { IO.PutF[s, "exchangeID %g, exchangeType %g\n", [cardinal[ CfF[b.hdr2.id] ]], [rope[ RopeFromExchangeType[b.hdr2.type] ]] ]; { nBytes: INT _ CfH[b.hdr1.length]; nHWords, nToPrint: INT; nBytes _ nBytes - XNSBuf.hdrBytes - XNSExchangeBuf.hdrBytes; IO.PutF[s, "len %g, data", [integer[nBytes]]]; nToPrint _ nHWords _ (nBytes + 1) / 2; IF short THEN nToPrint _ MIN[nToPrint, 8]; FOR i: INT IN [0 .. nToPrint) DO IO.PutF[s, " %g", [cardinal[CfH[b.body.hWords[i]]]] ]; ENDLOOP; IF nToPrint < nHWords THEN IO.PutF[s, " ..."]; IO.PutChar[s, '\n]; }; }; PutSPPBuf: PROC [s: STREAM, b: XNSSPPBuf.Buffer, short: BOOL] ~ { IO.PutF[s, "[ "]; IF b.hdr2.connCtl.system THEN IO.PutF[s, "system "]; IF b.hdr2.connCtl.sendAck THEN IO.PutF[s, "sendAck "]; IF b.hdr2.connCtl.attn THEN IO.PutF[s, "attention "]; IF b.hdr2.connCtl.endOfMsg THEN IO.PutF[s, "endOfMsg "]; IO.PutF[s, "], "]; IO.PutF[s, "sst %g, srcID %g, destID %g, ", [cardinal[b.hdr2.sst]], [cardinal[CfH[b.hdr2.sourceConnID]]], [cardinal[CfH[b.hdr2.destConnID]]] ]; IO.PutF[s, "seq %g, ack %g, alloc %g\n", [cardinal[CfH[b.hdr2.seqNum]]], [cardinal[CfH[b.hdr2.ackNum]]], [cardinal[CfH[b.hdr2.allocNum]]] ]; { nBytes: INT _ CfH[b.hdr1.length]; nHWords, nToPrint: INT; nBytes _ nBytes - XNSBuf.hdrBytes - XNSSPPBuf.hdrBytes; IO.PutF[s, "len %g, data", [integer[nBytes]]]; nToPrint _ nHWords _ (nBytes + 1) / 2; IF short THEN nToPrint _ MIN[nToPrint, 8]; FOR i: INT IN [0 .. nToPrint) DO IO.PutF[s, " %g", [cardinal[CfH[b.body.hWords[i]]]] ]; ENDLOOP; IF nToPrint < nHWords THEN IO.PutF[s, " ..."]; IO.PutChar[s, '\n]; }; }; TranslationType: TYPE ~ MACHINE DEPENDENT RECORD [a, b: BYTE]; requestType: TranslationType ~ [010H, 041H]; replyType: TranslationType ~ [00eH, 038H]; HostPair: TYPE ~ MACHINE DEPENDENT RECORD [ xnsHost: XNS.Host, pupHost: Pup.Host, filler: BYTE]; TranslationBuf: TYPE ~ REF TranslationBufObject; TranslationBufObject: TYPE ~ MACHINE DEPENDENT RECORD [ ovh: CommBuffer.Overhead, translationType: TranslationType, replier: HostPair, requestor: HostPair]; putTranslation: BOOL _ FALSE; PutHostPair: PROC [s: STREAM, hosts: HostPair] ~ INLINE { IO.PutF[s, "%g -> %g", [rope[RopeFromHost[hosts.xnsHost]]], [cardinal[hosts.pupHost]]]; }; DisplayTranslationBuf: PROC [s: STREAM, b: TranslationBuf, heading: ROPE, when: BasicTime.GMT, short: BOOL] ~ { t: TranslationType ~ b.translationType; IO.PutF[s, "Translation %g %g:\ntype %g, ", [rope[heading]], [time[when]], [rope[SELECT TRUE FROM t = requestType => "request", t = replyType => "reply", ENDCASE => "???"]] ]; IO.PutRope[s, "replier "]; PutHostPair[s, b.replier]; IO.PutRope[s, ", "]; IO.PutRope[s, "requestor "]; PutHostPair[s, b.requestor]; IO.PutChar[s, '\n]; }; 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 xns => { b: XNSBuf.Buffer; TRUSTED { b _ LOOPHOLE[buffer] }; IF dH.promiscuous THEN { SELECT TRUE FROM (b.hdr1.source.host = dH.fromHost) => { PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~send] }; (b.hdr1.dest.host = dH.toHost) => { PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~recv] }; (b.hdr1.dest.host = XNS.broadcastHost) => { PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~recv] }; ENDCASE; } ELSE { PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~recv]; }; }; xnsTranslate => { PostPrintRequest[tH~tH, kind~translation, buffer~buffer, direction~recv]; }; 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 xns, xnsReturn => { b: XNSBuf.Buffer; TRUSTED { b _ LOOPHOLE[buffer] }; PostPrintRequest[tH~tH, kind~ns, buffer~buffer, direction~send]; }; xnsTranslate => { PostPrintRequest[tH~tH, kind~translation, buffer~buffer, direction~send]; }; 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~FALSE, arpaReturn~FALSE, arpaTranslate~FALSE, xns~TRUE, xnsReturn~TRUE, xnsTranslate~TRUE, pup~FALSE, pupReturn~FALSE, pupTranslate~FALSE, other~FALSE, otherReturn~FALSE, otherTranslate~FALSE, raw~FALSE], sendProc~Send, recvMask~[arpa~FALSE, arpaTranslate~FALSE, xns~TRUE, xnsTranslate~TRUE, 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 ~ { ns, translation }; BufDirection: TYPE ~ { send, recv }; PrintBuf: TYPE ~ REF PrintBufObject; PrintBufObject: TYPE ~ RECORD [ bufObject: CommDriver.BufferObject, time: BasicTime.GMT, kind: BufKind, direction: BufDirection, next: PrintBuf]; FlushPrintQueue: ENTRY PROC [dH: DataHandle] ~ { IF dH.printQTail # NIL THEN { dH.printQTail.next _ dH.printQFree; dH.printQFree _ dH.printQHead; dH.printQHead _ dH.printQTail _ NIL }; }; EnqueuePrintBuf: ENTRY PROC [dH: DataHandle, b: PrintBuf] ~ INLINE { IF dH.printQHead = NIL THEN { dH.printQHead _ dH.printQTail _ b; NOTIFY dH.printQNonempty } ELSE { dH.printQTail.next _ b; dH.printQTail _ b }; b.next _ NIL }; DequeuePrintBuf: ENTRY PROC [dH: DataHandle] RETURNS [b: PrintBuf] ~ INLINE { ENABLE UNWIND => NULL; WHILE (b _ dH.printQHead) = NIL DO IF dH.pleaseStop THEN RETURN; WAIT dH.printQNonempty ENDLOOP; dH.printQHead _ b.next; 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 _ NEW[PrintBufObject _ [bufObject~, time~, kind~, direction~, next~NIL]] }; }; FreePrintBuf: ENTRY PROC [dH: DataHandle, b: PrintBuf] ~ INLINE { b.next _ dH.printQFree; dH.printQFree _ b; }; PostPrintRequest: PROC [tH: ToolHandle, kind: BufKind, buffer: CommDriver.Buffer, direction: BufDirection] ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle _ NARROW[tH.data]; b: PrintBuf; SELECT kind FROM ns => { nsb: XNSBuf.Buffer; TRUSTED { nsb _ LOOPHOLE[buffer] }; SELECT nsb.hdr1.type FROM routing => { dH.nRouting _ dH.nRouting + 1; IF NOT cH.putRouting THEN RETURN }; echo => { dH.nEcho _ dH.nEcho + 1; IF NOT cH.putEcho THEN RETURN }; error => { dH.nError _ dH.nError + 1; IF NOT cH.putError THEN RETURN }; exchange => { dH.nExchange _ dH.nExchange + 1; IF NOT cH.putExchange THEN RETURN }; spp => { dH.nSPP _ dH.nSPP + 1; IF NOT cH.putSPP THEN RETURN }; ENDCASE => { NULL }; }; translation => { IF NOT cH.putTranslation THEN RETURN }; ENDCASE => ERROR; b _ AllocPrintBuf[dH]; b.bufObject _ buffer^; b.bufObject.ovh.next _ NIL; b.bufObject.ovh.network _ NIL; b.time _ BasicTime.Now[]; b.kind _ kind; b.direction _ direction; EnqueuePrintBuf[dH, b]; }; Watch: PROC [tH: ToolHandle] ~ { cH: ControlHandle ~ NARROW[tH.control]; dH: DataHandle ~ NARROW[tH.data]; b: PrintBuf; IF NOT Enter[tH, watch] THEN RETURN; FlushPrintQueue[dH]; Install[tH]; IO.PutF[tH.out, "\nStarted %g\n\n", [time[BasicTime.Now[]]] ]; DO ENABLE IO.Error, ABORTED => EXIT; b _ DequeuePrintBuf[dH]; IF dH.pleaseStop THEN EXIT; IF b = NIL THEN LOOP; SELECT b.direction FROM send => SELECT b.kind FROM ns => TRUSTED { DisplayXNSBuf[tH.out, LOOPHOLE[b], "Send", b.time, cH.shortFormat] }; translation => TRUSTED { DisplayTranslationBuf[tH.out, LOOPHOLE[b], "Send", b.time, cH.shortFormat] }; ENDCASE => ERROR; recv => SELECT b.kind FROM ns => TRUSTED { DisplayXNSBuf[tH.out, LOOPHOLE[b], "Recv", b.time, cH.shortFormat] }; translation => TRUSTED { DisplayTranslationBuf[tH.out, LOOPHOLE[b], "Recv", b.time, cH.shortFormat] }; ENDCASE => ERROR; ENDCASE; IO.PutChar[tH.out, '\n]; FreePrintBuf[dH, b]; ENDLOOP; IO.PutF[tH.out, "\n\nStopped %g\n", [time[BasicTime.Now[]]] ]; UnInstall[tH]; 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; }; CvtAddrFromCardinals: PROC [args: LIST OF CARDINAL, format: XNSAddressParsing.Format _ productSoftware] RETURNS [result: ROPE _ NIL] ~ { buf: ARRAY [0..6) OF CARDINAL; FOR i: CARDINAL IN [0..6) DO IF args = NIL THEN RETURN ["args too short"]; buf[i] _ args.first; args _ args.rest; ENDLOOP; TRUSTED { result _ XNSAddressParsing.RopeFromAddress[LOOPHOLE[buf], format] }; }; Go: Commander.CommandProc ~ { IF Create[] THEN RETURN [result~$Done] ELSE RETURN [result~$Failure] }; Commander.Register[key~toolName, proc~Go]; END. nXNSSpyTool.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Demers, December 9, 1986 10:12:19 am PST Hal Murray, May 5, 1986 11:42:59 pm PDT Tool to watch all XNS packets received. Tool Creation Print Utilities XNS Buffers Print an XNS buffer on s. No assumptions about encapsulation. Print contents of a routing information packet. Put generic part of echo packet. Print generic part of an error packet. Note this clobbers the content of the buffer! Print generic part of an exchange packet. Print generic part of an SPP packet. Translation Buffers Print an XNSEthernetOneTranslation buffer on s. No assumptions about encapsulation. 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] Κv˜codešœ™Kšœ Οmœ1™˜>Kšœ žœ˜—K˜—Kšœžœ ˜$Kšœ˜K˜—š œ˜Kšœžœ ˜!Kšœžœ ˜'Jšžœžœžœžœ˜$š œžœžœžœžœ˜'KšžœΎ˜ΐKšžœB˜DKšžœE˜Gšžœ*˜,KšœH˜H—K˜—Kšœ ˜ K˜——™š  œžœžœžœžœžœ˜2Kšžœ˜—š œžœžœžœžœžœžœ˜7Kšžœ˜—š  œžœžœžœžœžœ˜2Kšžœ˜—š œžœžœžœžœžœžœ˜7Kšžœ˜—K˜š   œžœžœžœžœ˜4Kš œžœž œžœžœ žœžœ˜IKšœžœM˜SK˜K˜—š  œžœžœžœ ˜/šœžœž œžœ˜Kš œžœ žœ žœ žœžœ˜E—Kšžœ˜’K˜K˜——šœ ™ š   œžœžœžœžœ žœ˜fK™>K˜š œžœ žœ˜!Kšžœe˜gKšœ˜Kšžœ˜Kšœ˜Kšžœ+˜-K˜K˜—šžœ ž˜˜ Kšœ˜Kšžœžœ˜2—˜ Kšœ˜Kšžœžœ˜/—˜ Kšœ˜Kšžœžœ˜0—˜ Kšœ˜Kšžœžœ˜3—˜Kšœ˜Kšžœžœ˜.—šžœ˜ Kšœžœ˜5——K˜K™—š  œžœžœ"žœ˜IK™/Kšœ žœI˜WKšœ žœ˜šžœ(˜*Kšœžœ žœ/žœ ˜`Kšœ˜Kšœ˜—K˜Kšžœžœžœ˜/šžœžœžœž˜'Kšžœ&žœE˜uKšžœ˜—Kšžœžœžœ˜5Kšžœ˜K˜K™—š  œžœžœžœ˜CK™ šžœ˜Kš œžœ žœ)žœžœ˜yKšœ˜—K˜K™—š  œžœžœžœžœ˜Qšžœžœž˜Kšœ6˜6Kšœ0˜0Kšœ;˜;Kšœ<˜K˜Kšœ,˜,Kšœ*˜*K˜š œ žœžœž œžœ˜+Kšœ žœ˜K˜Kšœžœ˜K˜—Kšœžœžœ˜0š œžœžœž œžœ˜7K˜Kšœ!˜!K˜K˜—K˜Kšœžœžœ˜K˜š  œžœžœžœ˜9KšžœU˜WJ˜K˜—š  œžœžœžœžœ žœ˜oK™TKšœ'˜'šžœI˜Kšœžœžœž˜Kšœ˜Kšœ˜Kšžœ ˜—K˜—Kšžœ6žœ˜LKšžœ8˜:Kšžœ˜K˜—Icoode˜—™"š œ˜ Kš œžœžœ+žœžœžœžœ™jKšœ˜Kšœ˜K˜K˜K˜Kšœžœ˜Kšœžœ ˜Kšœžœ ˜šžœžœ˜Kšœžœ˜Kšžœ˜ —šžœž˜šœ˜K˜Kšžœžœ ˜!šžœ˜šžœ˜šžœžœž˜šœ'˜'KšœB˜B—šœ#˜#KšœB˜B—šœ+˜+KšœB˜B—Kšžœ˜—K˜—šžœ˜Kšœ@˜@Kšœ˜——K˜—šœ˜KšœI˜IK˜—Kšžœžœ˜—Kšœ˜K˜—š œ˜ Kšœžœžœžœžœžœžœžœžœžœžœ™kK˜Kšœ˜K˜K˜K˜Kšœžœ˜Kšœžœ ˜Kšœžœ ˜Kšžœžœžœ˜šžœžœ˜Kšœžœ˜Kšžœ˜ —šžœž˜šœ˜K˜Kšžœžœ ˜!Kšœ@˜@K˜—šœ˜KšœI˜IK˜—Kšžœžœ˜—K˜—K˜š œž œ˜(Kšœžœ ˜!Kšœžœž˜;Kšžœžœžœžœ˜$Kšžœ žœžœžœ˜˜.K˜Kšœžœ žœžœžœ žœžœžœ žœžœžœžœžœžœ˜ΫKšœ˜Kšœžœžœžœžœžœžœžœžœžœ˜—K˜K˜Kšœ˜—K˜K˜—š  œž œ˜*Kšœžœ ˜!Kšžœžœžœžœ˜$Kšœ.˜.Kšœžœ˜——™ K™UK˜Kšœ žœ˜$Kšœžœ˜$K˜Kšœ žœžœ˜$šœžœžœ˜K˜#Kšœžœ˜Kšœ˜Kšœ˜Kšœ˜K˜—š œžœžœ˜0šžœžœžœ˜KšœD˜DKšœ žœ˜&—K˜K˜—š œžœžœ!žœ˜Dšžœž˜šžœ˜Kšœ"˜"Kšžœ˜—šžœ˜Kšœ˜Kšœ˜——Kšœ ž˜ K˜—K˜š  œžœžœžœžœ˜MKšžœžœžœ˜šžœžœž˜"Kšžœžœžœ˜Kšžœ˜Kšžœ˜—Kšœ˜Kšœ žœ˜ K˜—K˜š   œžœžœžœžœ˜Kšžœž˜šžœ˜Kšœ˜Kšœ žœ˜—šžœ˜Kšœžœ>žœ˜M——K˜—K˜š  œžœžœ!žœ˜AKšœ+˜+K˜—K˜š œžœX˜nKšœžœ ˜'Kšœžœ ˜!Kšœ ˜ šžœž˜˜K˜Kšžœ žœ ˜#šžœž˜˜ K˜Kšžœžœžœžœ˜#—˜ K˜Kšžœžœ žœžœ˜ —˜ K˜Kšžœžœ žœžœ˜!—˜ K˜ Kšžœžœžœžœ˜$—˜K˜Kšžœžœ žœžœ˜—šžœ˜ Kšžœ˜——K˜—˜Kšžœžœžœžœ˜'—Kšžœžœ˜—Kšœ˜K˜Kšœžœ˜Kšœžœ˜K˜K˜K˜Kšœ˜K˜—K˜š œžœ˜!Kšœžœ ˜'Kšœžœ ˜!K˜ Kšžœžœžœžœ˜$Kšœ˜K˜ Kšžœ<˜>šž˜Kšžœžœžœžœ˜!Kšœ˜Kšžœžœžœ˜Kšžœžœžœžœ˜šžœ ž˜šœžœž˜šœžœ˜Kšœžœ'˜E—šœžœ˜Kšœžœ'˜M—Kšžœžœ˜—šœžœž˜šœžœ˜Kšœžœ'˜E—šœžœ˜Kšœžœ'˜M—Kšžœžœ˜—Kšžœ˜—Kšžœ˜K˜Kšžœ˜—Kšžœ<˜>J˜Jšœ ˜ K˜—K˜—™+š œžœžœžœžœžœ žœžœ˜Tšžœžœž˜Kšœžœ˜Kšœ+žœ ˜8Kšœ+žœžœ˜