<> <> <> <> DIRECTORY Basics USING [BITNOT], BasicTime USING [GMT, Now, Period], Convert USING [RopeFromTime, TimeFromRope], PrincOps USING [zEXCH], UDP USING [BodyRec, default, date, echo, Handle, HandleRec, maxLength, minLength, Port, PseudoHeader, time], IO USING [STREAM], IPConfig USING [ourLocalAddress], IPDefs USING [Byte, CreateIPHandle, DataBuffer, Datagram, DByte, UDPProtocol, Address, InternetHandle, InternetHeader, nullAddress, PrintDatagram, Receive, RequestData, SendSpecific], IPOps USING [maxTTL, OnesComplementAddBlock], Process USING [Detach, DisableTimeout, MsecToTicks, SetTimeout], Rope USING [Fetch, Length, ROPE]; UDPImpl: CEDAR MONITOR IMPORTS Basics, BasicTime, Convert, IPConfig, IPDefs, IPOps, Process, Rope EXPORTS UDP = BEGIN logStream: IO.STREAM _ NIL; <> nextPort: UDP.Port _ 256; users: LIST OF InfoRef _ NIL; InfoRef: TYPE = REF InfoRec; InfoRec: TYPE = RECORD [ handle: UDP.Handle, port: UDP.Port, waiting: CONDITION, queue: LIST OF IPDefs.Datagram]; RequestedPortAlreadyInUse: ERROR = CODE; Create: PUBLIC ENTRY PROC [him: IPDefs.Address, remote: UDP.Port, local: UDP.Port _ UDP.default] RETURNS [handle: UDP.Handle] = { new: InfoRef; IF local = UDP.default THEN local _ (nextPort _ nextPort.SUCC); handle _ NEW[UDP.HandleRec _ [local: local, remote: remote, him: him]]; new _ NEW[InfoRec _ [handle: handle, port: 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.port = local THEN ERROR RequestedPortAlreadyInUse; IF user.rest = NIL THEN {user.rest _ CONS[new, NIL]; RETURN; }; ENDLOOP; }; Destroy: PUBLIC ENTRY PROC [handle: UDP.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: UDP.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: UDP.Handle, data: IPDefs.Datagram, length: INT] = TRUSTED { udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; IF length < UDP.minLength THEN ERROR; IF length > UDP.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.UDPProtocol; data.inHdr.source _ IPConfig.ourLocalAddress; data.inHdr.destination _ handle.him; udp.source _ handle.local; udp.dest _ handle.remote; udp.length _ length; udp.checksum _ Checksum[data]; IPDefs.SendSpecific[data]; }; SendReply: PUBLIC PROC [data: IPDefs.Datagram] = TRUSTED { <> udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; tempAddress: IPDefs.Address; tempPort: UDP.Port; tempAddress _ data.inHdr.source; data.inHdr.source _ data.inHdr.destination; data.inHdr.destination _ tempAddress; tempPort _ udp.source; udp.source _ udp.dest; udp.dest _ tempPort; udp.checksum _ Checksum[data]; IPDefs.SendSpecific[data]; }; Listener: PROC = { <> myHandle: IPDefs.InternetHandle _ IPDefs.CreateIPHandle[ IPDefs.RequestData[ matchProtocol: TRUE, protocol: IPDefs.UDPProtocol, matchAddr: FALSE, address: IPDefs.nullAddress, matchLocalAddr: TRUE, localAddress: IPDefs.nullAddress]]; DO data: IPDefs.Datagram _ NIL; data _ myHandle.Receive[]; IF data # NIL AND IsValid[data] THEN TRUSTED { udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; IF logStream # NIL THEN IPDefs.PrintDatagram[logStream, data]; IF ~TakeThisOne[data, udp.dest] THEN CheckForDefaults[data, udp.dest]; }; ENDLOOP; }; IsValid: PROC [data: IPDefs.Datagram] RETURNS [ok: BOOL] = TRUSTED BEGIN udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; IF data.dataLength < UDP.minLength THEN RETURN[FALSE]; IF udp.checksum = 0 THEN RETURN[TRUE]; -- Don't care IF udp.checksum # Checksum[data] THEN RETURN[FALSE]; RETURN[TRUE]; END; Checksum: PUBLIC UNSAFE PROC [data: IPDefs.Datagram] RETURNS [checksum: IPDefs.DByte] = UNCHECKED BEGIN <> pseudoHeader: UDP.PseudoHeader; udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; cs: CARDINAL _ Basics.BITNOT[udp.checksum]; <> pseudoHeader.source _ data.inHdr.source; pseudoHeader.dest _ data.inHdr.destination; pseudoHeader.zero _ 0; pseudoHeader.protocol _ data.inHdr.protocol; pseudoHeader.udpTotalBytes _ data.dataLength; IF data.dataLength MOD 2 # 0 THEN data.data[data.dataLength] _ 0; cs _ IPOps.OnesComplementAddBlock[ptr: @pseudoHeader, count: UDP.PseudoHeader.SIZE, initialSum: cs]; cs _ IPOps.OnesComplementAddBlock[ptr: udp, count: (data.dataLength+1)/2, initialSum: cs]; RETURN [Basics.BITNOT[cs]]; END; TakeThisOne: ENTRY PROC [data: IPDefs.Datagram, port: UDP.Port] RETURNS [yes: BOOL] = { <> ENABLE UNWIND => NULL; FOR user: LIST OF InfoRef _ users, user.rest UNTIL user = NIL DO this: InfoRef _ user.first; IF this.port # port 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; NOTIFY this.waiting; RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; CheckForDefaults: PROC [data: IPDefs.Datagram, port: UDP.Port] = { SELECT port FROM UDP.echo => SendReply[data]; UDP.date => TRUSTED { udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; date: Rope.ROPE _ Convert.RopeFromTime[BasicTime.Now[], years, seconds, TRUE]; bytes: CARDINAL _ Rope.Length[date]; FOR i: CARDINAL IN [0..bytes) DO udp.data[UDP.minLength+i] _ LOOPHOLE[Rope.Fetch[date, i]]; ENDLOOP; data.dataLength _ udp.length _ UDP.minLength+bytes; SendReply[data]; }; UDP.time => TRUSTED { udp: LONG POINTER TO UDP.BodyRec _ LOOPHOLE[@data.data]; now: BasicTime.GMT _ BasicTime.Now[]; Flop: PROC [LONG CARDINAL] RETURNS [Pair] = TRUSTED MACHINE CODE BEGIN PrincOps.zEXCH; END; Pair: TYPE = MACHINE DEPENDENT RECORD [a, b: CARDINAL]; -- used for word swap flopped: LONG POINTER TO Pair _ LOOPHOLE[@udp.data]; period: INT _ BasicTime.Period[from: baseGmt, to: now]; raw: LONG CARDINAL _ period+baseRaw; flopped^ _ Flop[raw]; data.dataLength _ udp.length _ UDP.minLength+4; SendReply[data]; }; ENDCASE => NULL; -- Drop on the floor RETURN; }; baseGmt: BasicTime.GMT _ Convert.TimeFromRope["1-Jan-70 00:00:00 GMT"]; baseRaw: LONG CARDINAL _ 2208988800; TRUSTED {Process.Detach[FORK Listener[]]}; END.