Copyright (C) 1985 by Xerox Corporation. All rights reserved. The following program was created in 1985 but has not been published within the meaning of the copyright law, is furnished under license, and may not be used, copied and/or disclosed except in accordance with the terms of said license.
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.STREAMNIL;
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.