<> <> <> <> <> <> <> DIRECTORY Basics USING [BITNOT], ICMP, IO USING [STREAM], IPDefs USING [Byte, CreateIPHandle, DataBuffer, Datagram, DByte, ICMPProtocol, Address, InternetHandle, InternetHeader, nullAddress, PrintDatagram, Receive, RequestData, SendSpecific], IPOps USING [maxTTL, OnesComplementAddBlock], IPRouter USING [Redirect], IPConfig USING [ourLocalAddress], Process USING [Detach, DisableTimeout, MsecToTicks, SetTimeout]; ICMPImpl: CEDAR MONITOR IMPORTS Basics, IPConfig, IPDefs, IPOps, IPRouter, Process EXPORTS ICMP = BEGIN OPEN ICMP; logStream: IO.STREAM _ NIL; <> nextID: ICMP.ID _ 256; users: LIST OF InfoRef _ NIL; InfoRef: TYPE = REF InfoRec; InfoRec: TYPE = RECORD [ handle: ICMP.Handle, id: ICMP.ID, waiting: CONDITION, queue: LIST OF IPDefs.Datagram]; RequestedPortAlreadyInUse: ERROR = CODE; Create: PUBLIC ENTRY PROC [him: IPDefs.Address, local: ICMP.ID _ ICMP.default] RETURNS [handle: ICMP.Handle] = { new: InfoRef; IF local = ICMP.default THEN local _ (nextID _ nextID.SUCC); handle _ NEW[ICMP.HandleRec _ [local: local, him: him]]; new _ NEW[InfoRec _ [handle: handle, id: local, waiting: , queue: NIL]]; IF users = NIL THEN { users _ CONS[new, NIL]; RETURN; }; FOR user: LIST OF InfoRef _ users, user.rest DO this: InfoRef _ user.first; IF this.id = local THEN ERROR RequestedPortAlreadyInUse; IF user.rest = NIL THEN {user.rest _ CONS[new, NIL]; RETURN; }; ENDLOOP; }; Destroy: PUBLIC ENTRY PROC [handle: ICMP.Handle] = { IF users = NIL THEN ERROR; IF users.first.handle = handle THEN {users _ users.rest; RETURN; }; FOR user: LIST OF InfoRef _ users, user.rest DO IF users.rest = NIL THEN ERROR; IF user.rest.first.handle = handle THEN {user.rest _ user.rest.rest; RETURN; }; ENDLOOP; }; Receive: PUBLIC ENTRY PROC [handle: ICMP.Handle, timeout: INT] RETURNS [data: IPDefs.Datagram] = TRUSTED { < Forever>> FOR user: LIST OF InfoRef _ users, user.rest UNTIL user = NIL DO this: InfoRef _ user.first; IF this.handle # handle THEN LOOP; UNTIL this.queue # NIL DO IF timeout = 0 THEN Process.DisableTimeout[@this.waiting] ELSE Process.SetTimeout[@this.waiting, Process.MsecToTicks[timeout]]; WAIT this.waiting; IF timeout # 0 AND this.queue = NIL THEN RETURN[NIL]; ENDLOOP; data _ this.queue.first; this.queue _ this.queue.rest; RETURN; REPEAT FINISHED => ERROR; ENDLOOP; }; Send: PUBLIC PROC [handle: ICMP.Handle, data: IPDefs.Datagram, length: INT] = TRUSTED { icmp: LONG POINTER TO ICMP.BodyRec _ LOOPHOLE[@data.data]; IF length < ICMP.minLength THEN ERROR; IF length > ICMP.maxLength THEN ERROR; data.dataLength _ length; data.inHdr.precedence _ 0; data.inHdr.delay _ 0; data.inHdr.throughput _ 0; data.inHdr.reliability _ 0; data.inHdr.dontFragment _ FALSE; data.inHdr.timeToLive _ IPOps.maxTTL; data.inHdr.protocol _ IPDefs.ICMPProtocol; data.inHdr.source _ IPConfig.ourLocalAddress; data.inHdr.destination _ handle.him; icmp.id _ handle.local; icmp.checksum _ Checksum[icmp, (data.dataLength+1)/2]; IPDefs.SendSpecific[data]; }; SendReply: PUBLIC PROC [data: IPDefs.Datagram] = TRUSTED { <> icmp: LONG POINTER TO ICMP.BodyRec _ LOOPHOLE[@data.data]; tempAddress: IPDefs.Address; tempAddress _ data.inHdr.source; data.inHdr.source _ data.inHdr.destination; data.inHdr.destination _ tempAddress; icmp.checksum _ Checksum[icmp, (data.dataLength+1)/2]; IPDefs.SendSpecific[data]; }; Checksum: UNSAFE PROC [icmp: LONG POINTER TO ICMP.BodyRec, words: INT] RETURNS [checksum: IPDefs.DByte] = UNCHECKED { <> p: LONG POINTER TO CARDINAL _ LOOPHOLE[icmp]; cs: CARDINAL _ Basics.BITNOT[icmp.checksum]; <> cs _ IPOps.OnesComplementAddBlock[ptr: p, count: words, initialSum: cs]; RETURN[Basics.BITNOT[cs]]; }; IsValidICMPDatagram: PROC [data: IPDefs.Datagram] RETURNS [ok: BOOL] = TRUSTED { icmp: LONG POINTER TO ICMP.BodyRec _ LOOPHOLE[@data.data]; IF data.dataLength < minLength THEN RETURN[FALSE]; IF icmp.checksum # Checksum[icmp, (data.dataLength+1)/2] THEN RETURN[FALSE]; RETURN[TRUE]; }; Listener: PROC = { myHandle: IPDefs.InternetHandle _ IPDefs.CreateIPHandle[IPDefs.RequestData[matchProtocol: TRUE, protocol: IPDefs.ICMPProtocol, matchAddr: FALSE, address: IPDefs.nullAddress, matchLocalAddr: TRUE, localAddress: IPDefs.nullAddress]]; DO data: IPDefs.Datagram = myHandle.Receive[]; IF data # NIL AND IsValidICMPDatagram[data] THEN TRUSTED { icmp: LONG POINTER TO ICMP.BodyRec _ LOOPHOLE[@data.data]; IF logStream # NIL THEN IPDefs.PrintDatagram[logStream, data]; SELECT icmp.type FROM -- Yucky packet format Echo => { icmp.type _ EchoReply; SendReply[data]; }; Timestamp => { icmp.type _ TimestampReply; icmp.data[minLength+4] _ 200b; icmp.data[minLength+8] _ 200b; SendReply[data]; }; InformationRequest => { icmp.type _ InformationReply; <> SendReply[data]; }; Redirect => { redirect: LONG POINTER TO ICMP.RedirectBody _ LOOPHOLE[@data.data]; IF redirect.code = redirectForNet THEN IPRouter.Redirect[redirect.header.destination, redirect.gateway, data.inHdr.source]; }; DestinationUnreachable => LOOP; TimeExceeded => LOOP; ParameterProblem => LOOP; SourceQuench => LOOP; Redirect => LOOP; EchoReply, TimestampReply, InformationReply => { FOR user: LIST OF InfoRef _ users, user.rest UNTIL user = NIL DO this: InfoRef _ user.first; IF this.id # icmp.id THEN LOOP; IF this.queue = NIL THEN this.queue _ CONS[data, NIL] ELSE FOR finger: LIST OF IPDefs.Datagram _ this.queue, finger.rest DO IF finger.rest # NIL THEN LOOP; finger.rest _ CONS[data, NIL]; EXIT; ENDLOOP; PokeUser[this]; EXIT; ENDLOOP; }; ENDCASE; }; ENDLOOP; }; PokeUser: ENTRY PROC [user: InfoRef] = { NOTIFY user.waiting; }; TRUSTED {Process.Detach[FORK Listener[]]; }; END.