-- File: EchoServerImpl.mesa - last edit:
-- AOF                  5-May-87 12:58:07
-- SMA                 22-May-86 11:37:27
-- Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. 

DIRECTORY
  CommFlags USING [doStats],
  CommSwitches USING [remoteSwat],
  EchoServer USING [],
  HostNumbers USING [IsMulticastID],
  NetworkStream USING [closeSST, closeReplySST],
  NSConstants USING [echoerSocket],
  NSBuffer USING [Body, Buffer, ReturnBuffer],
  NSTypes USING [bytesPerSppHeader, bytesPerIDPHeader, EchoType],
  PacketStream USING [
    ConnectionAlreadyThere, ConnectionFailed, ConnectionSuspended, Make,
    Handle, unknownConnID, WaitTime],
  Process USING [Detach],
  Protocol1 USING [EncapsulateAndTransmit],
  RouterInternal USING [SendErrorPacket],
  Runtime USING [CallDebugger],
  Socket USING [ChannelHandle, Delete, PutPacket, SwapSourceAndDestination],
  SocketInternal USING [ListenerProcType, CreateListen],
  Stats USING [StatIncr, StatBump],
  System USING [nullNetworkAddress, SocketNumber, switches];

EchoServerImpl: PROGRAM
  IMPORTS
    NSBuffer, HostNumbers, Process, PacketStream, Protocol1,
    RouterInternal, Runtime, Socket, SocketInternal, Stats, System
  EXPORTS EchoServer =
  BEGIN

  echoCH: Socket.ChannelHandle;
  echoPackets : LONG CARDINAL ← 0;
  echoBytes: LONG CARDINAL ← 0;

  swatRequest: WORD = 17; 
  swatCH: Socket.ChannelHandle;
  swatSocket: System.SocketNumber ← LOOPHOLE[27];  --Action Request 7176

  CreateServer: PUBLIC PROCEDURE [buffers: CARDINAL] =
    BEGIN
    echoCH ← SocketInternal.CreateListen[
      socket: NSConstants.echoerSocket, callback: EchoServer,
      receive: buffers, clientData: NIL];
    IF System.switches[CommSwitches.remoteSwat] = down THEN
      BEGIN
      swatCH ← SocketInternal.CreateListen[
        socket: swatSocket, receive: 1,
	callback: SwatWatcher, clientData: NIL];
      END;
    END; -- CreateServer


  DeleteServer: PUBLIC PROCEDURE =
    BEGIN
    Socket.Delete[echoCH];
    IF System.switches[CommSwitches.remoteSwat] = down THEN
      Socket.Delete[swatCH];
    END;  --DeleteServer


  EchoServer: SocketInternal.ListenerProcType =
    BEGIN
    nsb: NSBuffer.Body = b.ns;
    SELECT nsb.packetType FROM
      (echo) =>
	BEGIN
	IF nsb.echoType = echoRequest THEN
	  BEGIN
	  Socket.SwapSourceAndDestination[b];
	  nsb.echoType ← echoResponse;
	  echoPackets ← echoPackets + 1;
	  echoBytes ← echoBytes + nsb.pktLength;
	  IF CommFlags.doStats THEN Stats.StatIncr[packetsEchoed];
	  IF CommFlags.doStats THEN Stats.StatBump[bytesEchoed, nsb.pktLength];
	  SELECT TRUE FROM
	    (nsb.checksum # 177777B) =>
	      Socket.PutPacket[echoCH, b];  --he checksum'd => me too
	    (nsb.source.host = nsb.destination.host) =>
	      Socket.PutPacket[echoCH, b];  --it's gotta be ME => ME
	    (nsb.source.net # nsb.destination.net) =>
	      Socket.PutPacket[echoCH, b];  --need router's help router's help
	    ENDCASE =>
	      BEGIN
	      <<nsb.checksum ← 177777B;>>  --that field's already set
	      nsb.transportControl ← [FALSE, 0, 0];
	      Protocol1.EncapsulateAndTransmit[
	        LOOPHOLE[b], @nsb.destination.host];
	      END;
	  END
	ELSE RouterInternal.SendErrorPacket[b, protocolViolation];
	END;
      (sequencedPacket) => Process.Detach[FORK Sequenced[b]];
      ENDCASE => RouterInternal.SendErrorPacket[b, protocolViolation];
    END; -- EchoServer

  GetCounters: PUBLIC PROCEDURE RETURNS [packets, bytes: LONG CARDINAL] =
    {RETURN[echoPackets, echoBytes]};

  Sequenced: PROC[rb: NSBuffer.Buffer] =
    BEGIN
    echo: LONG POINTER TO NSTypes.EchoType;
    rbody: NSBuffer.Body ← rb.ns;  --make things closer
    dontcopy: CARDINAL = NSTypes.bytesPerSppHeader + NSTypes.bytesPerIDPHeader;
    SELECT TRUE FROM
      (rb = NIL) => NULL;  --rb is NIL if client aborted this process
      (rb.fo.status # goodCompletion) => NSBuffer.ReturnBuffer[rb];
      (rbody.destinationConnectionID # PacketStream.unknownConnID) =>
	RouterInternal.SendErrorPacket[rb, protocolViolation];
      (rbody.sourceConnectionID = PacketStream.unknownConnID) =>
	RouterInternal.SendErrorPacket[rb, protocolViolation];
      (rbody.sequenceNumber # 0) =>
	RouterInternal.SendErrorPacket[rb, protocolViolation];
      (~rbody.systemPacket) =>
	RouterInternal.SendErrorPacket[rb, protocolViolation];
      (~PacketStream.ConnectionAlreadyThere[
	rbody.source, rbody.sourceConnectionID]) =>
	BEGIN
	psH: PacketStream.Handle ← PacketStream.Make[
	  System.nullNetworkAddress, rbody.source,
	  PacketStream.unknownConnID, rbody.sourceConnectionID,
	  TRUE, LAST[PacketStream.WaitTime], bulk !
	  PacketStream.ConnectionFailed => GOTO failed];
	NSBuffer.ReturnBuffer[rb];  --we don't need buffer any longer
	--UNTIL stream ends-- DO
	  ENABLE PacketStream.ConnectionSuspended => EXIT;  --that's an out
	  IF (rb ← psH.get[]) = NIL THEN EXIT;  --Stream.TimeOut => quit
	  rbody ← rb.ns;  --pick up pointer to the body
	  echo ← LOOPHOLE[@rbody.sppWords[0]];
	  IF rbody.subtype = NetworkStream.closeReplySST THEN GOTO exit;
	  IF echo↑ = echoRequest THEN echo↑ ← echoResponse;  --maybe
	  echoPackets ← echoPackets + 1;
	  echoBytes ← echoBytes + rbody.pktLength;
	  IF CommFlags.doStats THEN Stats.StatIncr[packetsEchoed];
	  IF CommFlags.doStats THEN Stats.StatBump[bytesEchoed, rbody.pktLength];
	  rbody.subtype ← IF rbody.subtype = NetworkStream.closeSST
	    THEN NetworkStream.closeReplySST ELSE 0;
	  psH.put[rb];  --send the buffer back
	  REPEAT exit => psH.returnReceiveBuffer[rb];
	  ENDLOOP;
	psH.destroy[psH];  --get rid of stream
	EXITS failed => NSBuffer.ReturnBuffer[rb];
	END;
      ENDCASE => NSBuffer.ReturnBuffer[rb];
    END;  --Sequenced

  SwatWatcher: SocketInternal.ListenerProcType =
    BEGIN
    nsb: NSBuffer.Body = b.ns;
    ok: BOOLEAN ← (nsb.packetType = private)
      AND (nsb.nsWords[0] = swatRequest)
      AND (~HostNumbers.IsMulticastID[LOOPHOLE[@nsb.destination.host]]);
    NSBuffer.ReturnBuffer[b];
    IF ok THEN Runtime.CallDebugger["SwatRequest"L];
    END;  --SwatWatcher

  END....

LOG
16-May-84 20:29:43  AOF  Post Klamath.
 5-Apr-85 10:37:18  AOF  Remote call debug.
30-Dec-85 14:12:26  AOF  Restructure listeners.
22-May-86 11:34:56  SMA  Remove dependencies on Buffer. 
19-Aug-86 10:30:26  AOF  Caching pointer to buffer body. 
16-Sep-86 18:50:40  AOF  One last "rbody ← r.ns". 
18-Nov-86 16:06:56  AOF  PilotSwitchesExtra5 => CommSwitches
 4-May-87 11:32:11  AOF  Sequenced is real echo protocol and uses buffer swap