UDPImpl.mesa
Hal Murray June 26, 1985 10:41:36 pm PDT
John Larson, June 12, 1987 11:24:29 pm PDT
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;
If non-NIL (probably set from the debugger), then log incoming UDP packets.
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 {
NIL if timeout (ms), 0 => 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 {
Reverse all the fields and send it back.
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 = {
Handle incoming UDP messages.
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
Compute the header checksum for a datagram.
pseudoHeader: UDP.PseudoHeader;
udp: LONG POINTER TO UDP.BodyRec ← LOOPHOLE[@data.data];
cs: CARDINAL ← Basics.BITNOT[udp.checksum];
Start with negative of the checksum that's in the header so that we don't have to smash it to zero to compute the real 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] = {
Find a home for this datagram and queue it.
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.