diff --git a/PUP/BCPLString.cs b/PUP/BCPLString.cs
index 0554131..8eca421 100644
--- a/PUP/BCPLString.cs
+++ b/PUP/BCPLString.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -19,7 +20,7 @@ namespace IFS
throw new InvalidOperationException("Max length for a BCPL string is 255 characters.");
}
- _string = new byte[_string.Length];
+ _string = new byte[s.Length];
// We simply take the low 8-bits of each Unicode character and stuff it into the
// byte array. This works fine for the ASCII subset of Unicode but obviously
@@ -53,6 +54,45 @@ namespace IFS
Array.Copy(rawData, 1, _string, 0, rawData.Length - 1);
}
+ ///
+ /// Build a new BCPL string from the raw representation at the given position in the array
+ ///
+ ///
+ public BCPLString(byte[] rawData, int offset)
+ {
+ int length = rawData[offset];
+
+ // Sanity check that BCPL length fits within specified array
+ if (length > rawData.Length - offset)
+ {
+ throw new InvalidOperationException("BCPL length data is invalid.");
+ }
+
+ _string = new byte[length];
+ Array.Copy(rawData, offset + 1, _string, 0, length);
+ }
+
+ public BCPLString(BSPChannel channel)
+ {
+ byte length = channel.ReadByte();
+ _string = new byte[length];
+
+ channel.Read(ref _string, length);
+ }
+
+ public BCPLString(Stream s)
+ {
+ byte length = (byte)s.ReadByte();
+ _string = new byte[length];
+
+ s.Read(_string, 0, length);
+ }
+
+ public int Length
+ {
+ get { return _string.Length; }
+ }
+
///
/// Returns a native representation of the BCPL string
///
diff --git a/PUP/BSPManager.cs b/PUP/BSPManager.cs
index 557fb9f..7190276 100644
--- a/PUP/BSPManager.cs
+++ b/PUP/BSPManager.cs
@@ -1,4 +1,5 @@
-using System;
+using IFS.Logging;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -9,6 +10,10 @@ using System.Threading.Tasks;
namespace IFS
{
+ public abstract class BSPProtocol : PUPProtocolBase
+ {
+ public abstract void InitializeServerForChannel(BSPChannel channel);
+ }
public enum BSPState
{
@@ -18,7 +23,7 @@ namespace IFS
public class BSPChannel
{
- public BSPChannel(PUP rfcPup, UInt32 socketID)
+ public BSPChannel(PUP rfcPup, UInt32 socketID, BSPProtocol protocolHandler)
{
_inputLock = new ReaderWriterLockSlim();
_outputLock = new ReaderWriterLockSlim();
@@ -27,6 +32,10 @@ namespace IFS
_inputQueue = new Queue(65536);
+ _outputAckEvent = new AutoResetEvent(false);
+
+ _protocolHandler = protocolHandler;
+
// TODO: init IDs, etc. based on RFC PUP
_start_pos = _recv_pos = _send_pos = rfcPup.ID;
@@ -35,10 +44,16 @@ namespace IFS
// in the RFC pup.
_clientConnectionPort = new PUPPort(rfcPup.Contents, 0);
+ //
+ if (_clientConnectionPort.Network == 0)
+ {
+ _clientConnectionPort.Network = DirectoryServices.Instance.LocalNetwork;
+ }
+
// We create our connection port using a unique socket address.
_serverConnectionPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, socketID);
}
-
+
public PUPPort ClientPort
{
get { return _clientConnectionPort; }
@@ -49,11 +64,25 @@ namespace IFS
get { return _serverConnectionPort; }
}
+ public void Destroy()
+ {
+
+ }
+
///
/// Reads data from the channel (i.e. from the client). Will block if not all the requested data is available.
///
///
public int Read(ref byte[] data, int count)
+ {
+ return Read(ref data, count, 0);
+ }
+
+ ///
+ /// Reads data from the channel (i.e. from the client). Will block if not all the requested data is available.
+ ///
+ ///
+ public int Read(ref byte[] data, int count, int offset)
{
// sanity check
if (count > data.Length)
@@ -75,7 +104,7 @@ namespace IFS
// TODO: this is a really inefficient thing.
for (int i = 0; i < count; i++)
{
- data[i] = _inputQueue.Dequeue();
+ data[i + offset] = _inputQueue.Dequeue();
}
_inputLock.ExitWriteLock();
@@ -96,8 +125,33 @@ namespace IFS
return read;
}
+ public byte ReadByte()
+ {
+ // TODO: optimize this
+ byte[] data = new byte[1];
+
+ Read(ref data, 1);
+
+ return data[0];
+ }
+
+ public ushort ReadUShort()
+ {
+ // TODO: optimize this
+ byte[] data = new byte[2];
+
+ Read(ref data, 2);
+
+ return Helpers.ReadUShort(data, 0);
+ }
+
+ public BCPLString ReadBCPLString()
+ {
+ return new BCPLString(this);
+ }
+
///
- /// Appends data into the input queue (called from BSPManager to place new PUP data into the BSP stream)
+ /// Appends incoming client data into the input queue (called from BSPManager to place new PUP data into the BSP stream)
///
public void WriteQueue(PUP dataPUP)
{
@@ -117,7 +171,8 @@ namespace IFS
// Current behavior is to simply drop all incoming PUPs (and not ACK them) until they are re-sent to us
// (in which case the above sanity check will pass). According to spec, AData requests that are not ACKed
// must eventually be resent. This is far simpler than accepting out-of-order data and keeping track
- // of where it goes in the queue.
+ // of where it goes in the queue, though less efficient.
+ _inputLock.ExitUpgradeableReadLock();
return;
}
@@ -129,6 +184,8 @@ namespace IFS
for (int i = 0; i < dataPUP.Contents.Length; i++)
{
_inputQueue.Enqueue(dataPUP.Contents[i]);
+
+ //Console.Write("{0:x} ({1}), ", dataPUP.Contents[i], (char)dataPUP.Contents[i]);
}
_recv_pos += (UInt32)dataPUP.Contents.Length;
@@ -139,6 +196,7 @@ namespace IFS
_inputWriteEvent.Set();
+ // If the client wants an ACK, send it now.
if ((PupType)dataPUP.Type == PupType.AData)
{
SendAck();
@@ -147,16 +205,23 @@ namespace IFS
}
///
- /// Sends data to the channel (i.e. to the client). Will block if an ACK is requested.
+ /// Sends data to the channel (i.e. to the client). Will block (waiting for an ACK) if an ACK is requested.
///
/// The data to be sent
public void Send(byte[] data)
{
- // Write data to the output stream
+ // Write data to the output stream.
+ // For now, we request ACKs for every pup sent.
+ // TODO: should buffer data until an entire PUP's worth is ready
+ // (and split data that's too large into multiple PUPs.)
+ PUP dataPup = new PUP(PupType.AData, _send_pos, _clientConnectionPort, _serverConnectionPort, data);
+
+ PUPProtocolDispatcher.Instance.SendPup(dataPup);
+
+ _send_pos += (uint)data.Length;
// Await an ack for the PUP we just sent
_outputAckEvent.WaitOne(); // TODO: timeout and fail
-
}
///
@@ -167,6 +232,15 @@ namespace IFS
{
// Update receiving end stats (max PUPs, etc.)
// Ensure client's position matches ours
+ if (ackPUP.ID != _send_pos)
+ {
+ Log.Write(LogLevel.BSPLostPacket,
+ String.Format("Client position != server position for BSP {0} ({1} != {2})",
+ _serverConnectionPort.Socket,
+ ackPUP.ID,
+ _send_pos));
+ }
+
// Let any waiting threads continue
_outputAckEvent.Set();
@@ -186,15 +260,18 @@ namespace IFS
// Events for:
// Abort, End, Mark, Interrupt (from client)
// Repositioning (due to lost packets) (perhaps not necessary)
+ // to allow protocols consuming BSP streams to be alerted when things happen.
//
private void SendAck()
{
+ PUP ackPup = new PUP(PupType.Ack, _recv_pos, _clientConnectionPort, _serverConnectionPort);
+ PUPProtocolDispatcher.Instance.SendPup(ackPup);
}
+ private BSPProtocol _protocolHandler;
- private BSPState _state;
private UInt32 _recv_pos;
private UInt32 _send_pos;
private UInt32 _start_pos;
@@ -235,8 +312,39 @@ namespace IFS
// a unique ID. (Well, until we wrap around...)
//
_nextSocketID = _startingSocketID;
+
+ _activeChannels = new Dictionary();
}
+ ///
+ /// Called when a PUP comes in on a known BSP socket
+ ///
+ ///
+ public static void EstablishRendezvous(PUP p, BSPProtocol protocolHandler)
+ {
+ if (p.Type != PupType.RFC)
+ {
+ Log.Write(LogLevel.Error, String.Format("Expected RFC pup, got {0}", p.Type));
+ return;
+ }
+
+ UInt32 socketID = GetNextSocketID();
+ BSPChannel newChannel = new BSPChannel(p, socketID, protocolHandler);
+ _activeChannels.Add(socketID, newChannel);
+
+ //
+ // Initialize the server for this protocol.
+ protocolHandler.InitializeServerForChannel(newChannel);
+
+ // Send RFC response to complete the rendezvous.
+
+ // Modify the destination port to specify our network
+ PUPPort sourcePort = p.DestinationPort;
+ sourcePort.Network = DirectoryServices.Instance.LocalNetwork;
+ PUP rfcResponse = new PUP(PupType.RFC, p.ID, newChannel.ClientPort, sourcePort, newChannel.ServerPort.ToArray());
+
+ PUPProtocolDispatcher.Instance.SendPup(rfcResponse);
+ }
///
/// Called when BSP-based protocols receive data.
@@ -246,82 +354,80 @@ namespace IFS
/// a new BSPChannel if one has been created based on the PUP (new RFC)
///
///
- public static BSPChannel RecvData(PUP p)
- {
- PupType type = (PupType)p.Type;
+ public static void RecvData(PUP p)
+ {
+ BSPChannel channel = FindChannelForPup(p);
- switch (type)
+ if (channel == null)
+ {
+ Log.Write(LogLevel.Error, "Received BSP PUP on an unconnected socket, ignoring.");
+ return;
+ }
+
+ switch (p.Type)
{
case PupType.RFC:
- {
- BSPChannel newChannel = new BSPChannel(p, GetNextSocketID());
- _activeChannels.Add(newChannel.ServerPort.Socket, newChannel);
-
- // Send RFC response to complete the rendezvous.
- PUP rfcResponse = new PUP(PupType.RFC, p.ID, newChannel.ClientPort, newChannel.ServerPort, newChannel.ServerPort.ToArray());
-
- Dispatcher.Instance.SendPup(rfcResponse);
-
- return newChannel;
- }
+ Log.Write(LogLevel.Error, "Received RFC on established channel, ignoring.");
break;
case PupType.Data:
case PupType.AData:
- {
- BSPChannel channel = FindChannelForPup(p);
-
- if (channel != null)
- {
- channel.WriteQueue(p);
- }
+ {
+ channel.WriteQueue(p);
}
break;
case PupType.Ack:
- {
- BSPChannel channel = FindChannelForPup(p);
-
- if (channel != null)
- {
- channel.Ack(p);
- }
+ {
+ channel.Ack(p);
}
break;
case PupType.End:
- {
- BSPChannel channel = FindChannelForPup(p);
-
- if (channel != null)
- {
- //channel.EndReply();
- }
+ {
+ //channel.EndReply();
}
break;
case PupType.Abort:
{
// TODO: tear down the channel
+ DestroyChannel(channel);
+
+ string abortMessage = Helpers.ArrayToString(p.Contents);
+
+ Log.Write(LogLevel.Warning, String.Format("BSP aborted, message: '{0}'", abortMessage));
}
break;
default:
- throw new NotImplementedException(String.Format("Unhandled BSP PUP type {0}.", type));
+ throw new NotImplementedException(String.Format("Unhandled BSP PUP type {0}.", p.Type));
- }
+ }
+ }
- return null;
+ public static bool ChannelExistsForSocket(PUP p)
+ {
+ return FindChannelForPup(p) != null;
}
public static void DestroyChannel(BSPChannel channel)
{
-
+ channel.Destroy();
+
+ _activeChannels.Remove(channel.ServerPort.Socket);
}
private static BSPChannel FindChannelForPup(PUP p)
{
- return null;
+ if (_activeChannels.ContainsKey(p.DestinationPort.Socket))
+ {
+ return _activeChannels[p.DestinationPort.Socket];
+ }
+ else
+ {
+ return null;
+ }
}
private static UInt32 GetNextSocketID()
diff --git a/PUP/Conf/hosts.txt b/PUP/Conf/hosts.txt
new file mode 100644
index 0000000..9bfc63f
--- /dev/null
+++ b/PUP/Conf/hosts.txt
@@ -0,0 +1,23 @@
+# hosts.txt:
+#
+# This is similar in nearly every way to a typical UNIX 'hosts' file except that
+# Xerox Inter-Network Name Expressions (i.e. network#host#) are used instead of IP addresses.
+#
+# Format for Inter-Network name expressions is either:
+# network#host# (to specify hosts on another network)
+# or
+# host# (to specify hosts on the same network as this IFS server)
+#
+# all numbers are in octal.
+#
+
+1# ifs
+42# Muffin
+43# Pumpkin
+
+43# Duplicate
+45# Pumpkin
+
+# to test
+5#177# NotHere
+
diff --git a/PUP/CopyDiskServer.cs b/PUP/CopyDiskServer.cs
index 0d4c810..97d5f20 100644
--- a/PUP/CopyDiskServer.cs
+++ b/PUP/CopyDiskServer.cs
@@ -2,11 +2,46 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
+using System.Threading;
namespace IFS
{
- public class CopyDiskServer : PUPProtocolBase
+ public enum CopyDiskBlock
+ {
+ Version = 1,
+ SendDiskParamsR = 2,
+ HereAreDiskParams = 3,
+ StoreDisk = 4,
+ RetrieveDisk = 5,
+ HereIsDiskPage = 6,
+ EndOfTransfer = 7,
+ SendErrors = 8,
+ HereAreErrors = 9,
+ No = 10,
+ Yes = 11,
+ Comment = 12,
+ Login = 13,
+ SendDiskParamsW = 14,
+ }
+
+ struct VersionBlock
+ {
+ public VersionBlock(ushort version, string herald)
+ {
+ Version = version;
+ Herald = new BCPLString(herald);
+
+ Length = (ushort)((6 + herald.Length + 2) / 2); // +2 for length of BCPL string and to round up to next word length
+ Command = (ushort)CopyDiskBlock.Version;
+ }
+
+ public ushort Length;
+ public ushort Command;
+ public ushort Version;
+ public BCPLString Herald;
+ }
+
+ public class CopyDiskServer : BSPProtocol
{
///
/// Called by dispatcher to send incoming data destined for this protocol.
@@ -14,11 +49,60 @@ namespace IFS
///
public override void RecvData(PUP p)
{
- BSPChannel newChannel = BSPManager.RecvData(p);
+ throw new NotImplementedException();
+ }
- if (newChannel != null)
+ public override void InitializeServerForChannel(BSPChannel channel)
+ {
+ // spwan new worker thread with new BSP channel
+ Thread newThread = new Thread(new ParameterizedThreadStart(CopyDiskServerThread));
+ newThread.Start(channel);
+ }
+
+ private void CopyDiskServerThread(object obj)
+ {
+ BSPChannel channel = (BSPChannel)obj;
+
+ while(true)
{
- // spwan new worker thread with new BSP channel
+ // Retrieve length of this block (in bytes):
+ int length = channel.ReadUShort() * 2;
+
+ // Sanity check that length is a reasonable value.
+ if (length > 2048)
+ {
+ // TODO: shut down channel
+ throw new InvalidOperationException(String.Format("Insane block length ({0})", length));
+ }
+
+ // Retrieve type
+ CopyDiskBlock blockType = (CopyDiskBlock)channel.ReadUShort();
+
+ // Read rest of block
+ byte[] data = new byte[length];
+
+ channel.Read(ref data, data.Length - 4, 4);
+
+ switch(blockType)
+ {
+ case CopyDiskBlock.Version:
+ VersionBlock vbIn = (VersionBlock)Serializer.Deserialize(data, typeof(VersionBlock));
+
+ Console.WriteLine("Copydisk client is version {0}, '{1}'", vbIn.Version, vbIn.Herald.ToString());
+
+ // Send the response:
+ VersionBlock vbOut = new VersionBlock(vbIn.Version, "IFS CopyDisk of 26-Jan-2016");
+ channel.Send(Serializer.Serialize(vbOut));
+ break;
+
+ case CopyDiskBlock.Login:
+
+ break;
+
+ default:
+ Console.WriteLine("Unhandled CopyDisk block {0}", blockType);
+ break;
+ }
}
}
}
diff --git a/PUP/DirectoryServices.cs b/PUP/DirectoryServices.cs
index 24b8edc..746009b 100644
--- a/PUP/DirectoryServices.cs
+++ b/PUP/DirectoryServices.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.IO;
using System.Text;
using System.Threading.Tasks;
@@ -17,6 +18,11 @@ namespace IFS
public byte Network;
public byte Host;
+
+ ///
+ /// Non-existent address
+ ///
+ public static HostAddress Empty = new HostAddress(0, 0);
}
///
@@ -30,19 +36,50 @@ namespace IFS
// Get our host address; for now just hardcode it.
// TODO: need to define config files, etc.
- _localHost = new HostAddress(1, 34);
+ _localHost = new HostAddress(1, 1);
+
+ // Load in hosts table from hosts file.
+ LoadHostTable();
+
+ Logging.Log.Write(Logging.LogLevel.Normal, "Directory services initialized.");
}
public string AddressLookup(HostAddress address)
{
- // TODO: actually look things up
- return "Alto";
+ //
+ // First look up the network. If specified network is Zero,
+ // we will assume our network number.
+ //
+ byte network = address.Network;
+
+ if (network == 0)
+ {
+ network = _localHost.Network;
+ }
+
+ if (_hostAddressTable.ContainsKey(network))
+ {
+ //
+ // We have entries for this network, see if the host is specified.
+ //
+ if (_hostAddressTable[network].ContainsKey(address.Host))
+ {
+ return _hostAddressTable[network][address.Host];
+ }
+ }
+
+ // Not found.
+ return null;
}
public HostAddress NameLookup(string hostName)
- {
- // TODO: actually look things up
- return new HostAddress(1, 0x80);
+ {
+ if (_hostNameTable.ContainsKey(hostName.ToLowerInvariant()))
+ {
+ return _hostNameTable[hostName.ToLowerInvariant()];
+ }
+
+ return null;
}
public static DirectoryServices Instance
@@ -55,8 +92,183 @@ namespace IFS
get { return _localHost; }
}
+ public byte LocalNetwork
+ {
+ get { return _localHost.Network; }
+ }
+
+ public byte LocalHost
+ {
+ get { return _localHost.Host; }
+ }
+
+ private void LoadHostTable()
+ {
+ _hostAddressTable = new Dictionary>();
+ _hostNameTable = new Dictionary();
+
+ // TODO: do not hardcode path like this.
+ using (StreamReader sr = new StreamReader("Conf\\hosts.txt"))
+ {
+ int lineNumber = 0;
+ while (!sr.EndOfStream)
+ {
+ lineNumber++;
+
+ //
+ // A line is either:
+ // '#' followed by comment to EOL
+ //
+ // Any whitespace is ignored
+ //
+ // Format for Inter-Network name expressions is either:
+ // network#host# (to specify hosts on another network)
+ // or
+ // host# (to specify hosts on our network)
+ //
+
+ string line = sr.ReadLine().Trim().ToLowerInvariant();
+
+ if (line.StartsWith("#") || String.IsNullOrWhiteSpace(line))
+ {
+ // Comment or empty, just ignore
+ continue;
+ }
+
+ // Tokenize on whitespace
+ string[] tokens = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+
+ // We need at least two tokens (inter-network name and one hostname)
+ if (tokens.Length < 2)
+ {
+ // Log warning and continue.
+ Logging.Log.Write(Logging.LogLevel.Warning,
+ String.Format("hosts.txt line {0}: Invalid syntax.", lineNumber));
+
+ continue;
+ }
+
+ // First token should be an inter-network name, which should end with '#'.
+ if (!tokens[0].EndsWith("#"))
+ {
+ // Log warning and continue.
+ Logging.Log.Write(Logging.LogLevel.Warning,
+ String.Format("hosts.txt line {0}: Improperly formed inter-network name '{1}'.", lineNumber, tokens[0]));
+
+ continue;
+ }
+
+ // tokenize on '#'
+ string[] networkTokens = tokens[0].Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
+
+ HostAddress host = new HostAddress(0, 0);
+ // 1 token means a local name, 2 means on other network, anything else is illegal
+ if (networkTokens.Length == 1)
+ {
+ try
+ {
+ host.Host = Convert.ToByte(networkTokens[0], 8);
+ host.Network = _localHost.Network;
+ }
+ catch
+ {
+ // Log warning and continue.
+ Logging.Log.Write(Logging.LogLevel.Warning,
+ String.Format("hosts.txt line {0}: Invalid host number in inter-network address '{1}'.", lineNumber, tokens[0]));
+
+ continue;
+ }
+ }
+ else if (networkTokens.Length == 2)
+ {
+ try
+ {
+ host.Network = Convert.ToByte(networkTokens[0], 8);
+ host.Host = Convert.ToByte(networkTokens[1], 8);
+ }
+ catch
+ {
+ // Log warning and continue.
+ Logging.Log.Write(Logging.LogLevel.Warning,
+ String.Format("hosts.txt line {0}: Invalid host or network number in inter-network address '{1}'.", lineNumber, tokens[0]));
+
+ continue;
+ }
+ }
+ else
+ {
+ // Log warning and continue.
+ Logging.Log.Write(Logging.LogLevel.Warning,
+ String.Format("hosts.txt line {0}: Improperly formed inter-network name '{1}'.", lineNumber, tokens[0]));
+
+ continue;
+ }
+
+ // Hash the host by one or more names
+ for (int i=1;i networkTable = null;
+
+ if (_hostAddressTable.ContainsKey(host.Network))
+ {
+ networkTable = _hostAddressTable[host.Network];
+ }
+ else
+ {
+ // No entry for this network yet, add it now
+ networkTable = new Dictionary();
+ _hostAddressTable.Add(host.Network, networkTable);
+ }
+
+ // Add to network table
+ if (networkTable.ContainsKey(host.Host))
+ {
+ // Duplicate host entry! Skip this line.
+ Logging.Log.Write(Logging.LogLevel.Warning,
+ String.Format("hosts.txt line {0}: Duplicate host ID '{1}'.", lineNumber, host.Host));
+ break;
+ }
+
+ networkTable.Add(host.Host, hostName);
+
+ }
+ }
+
+ }
+
+ }
+
+ ///
+ /// Points to us.
+ ///
private HostAddress _localHost;
+ ///
+ /// Hash table for address resolution; outer hash finds the dictionary
+ /// for a given network, inner hash finds names for hosts.
+ ///
+ private Dictionary> _hostAddressTable;
+
+ ///
+ /// Hash table for name resolution.
+ ///
+ private Dictionary _hostNameTable;
+
private static DirectoryServices _instance = new DirectoryServices();
}
}
diff --git a/PUP/EchoProtocol.cs b/PUP/EchoProtocol.cs
index 9c5b48d..c736f07 100644
--- a/PUP/EchoProtocol.cs
+++ b/PUP/EchoProtocol.cs
@@ -28,8 +28,29 @@ namespace IFS
// Just send it back with the source/destination swapped.
PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.SourcePort.Socket);
- PUP echoPup = new PUP(PupType.ImAnEcho, p.ID, p.SourcePort, localPort);
- Dispatcher.Instance.SendPup(p);
+ //
+ // An annoyance: The Alto "puptest" diagnostic actually expects us to echo *everything* back including
+ // the garbage byte on odd-length PUPs. (Even though the garbage byte is meant to be ignored.)
+ // So in these cases we need to do extra work and copy in the garbage byte. Grr.
+ //
+ byte[] contents;
+ bool garbageByte = (p.Contents.Length % 2) != 0;
+
+ if (!garbageByte)
+ {
+ // Even, no work needed
+ contents = p.Contents;
+ }
+ else
+ {
+ // No such luck, copy in the extra garbage byte to make diagnostics happy.
+ contents = new byte[p.Contents.Length + 1];
+ p.Contents.CopyTo(contents, 0);
+ contents[contents.Length - 1] = p.RawData[p.RawData.Length - 3];
+ }
+
+ PUP echoPup = new PUP(PupType.ImAnEcho, p.ID, p.SourcePort, localPort, contents, garbageByte);
+ PUPProtocolDispatcher.Instance.SendPup(echoPup);
}
}
diff --git a/PUP/Entrypoint.cs b/PUP/Entrypoint.cs
index 68dc68b..0f7199c 100644
--- a/PUP/Entrypoint.cs
+++ b/PUP/Entrypoint.cs
@@ -9,8 +9,31 @@ namespace IFS
{
public class Entrypoint
{
+ struct foo
+ {
+ public ushort Bar;
+ public short Baz;
+ public byte Thing;
+ public int Inty;
+ public uint Uinty;
+ public BCPLString Quux;
+ }
+
static void Main(string[] args)
{
+ foo newFoo = new foo();
+ newFoo.Bar = 0x1234;
+ newFoo.Baz = 0x5678;
+ newFoo.Thing = 0xcc;
+ newFoo.Inty = 0x01020304;
+ newFoo.Uinty = 0x05060708;
+ newFoo.Quux = new BCPLString("The quick brown fox jumped over the lazy dog's tail.");
+
+ byte[] data = Serializer.Serialize(newFoo);
+
+
+ foo oldFoo = (foo) Serializer.Deserialize(data, typeof(foo));
+
Logging.Log.Level = Logging.LogLevel.All;
@@ -19,20 +42,21 @@ namespace IFS
Console.WriteLine("available interfaces are:");
foreach(EthernetInterface i in ifaces)
{
- Console.WriteLine(String.Format("{0} - address {1}", i.Name, i.MacAddress));
- }
-
- // TODO: MAKE THIS CONFIGURABLE.
- Dispatcher.Instance.RegisterInterface(ifaces[1]);
+ Console.WriteLine(String.Format("{0} - address {1}, desc {2} ", i.Name, i.MacAddress, i.Description));
+ }
// Set up protocols:
// Connectionless
- Dispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Misc Services", 0x4, ConnectionType.Connectionless, new MiscServicesProtocol()));
- Dispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Echo", 0x5, ConnectionType.Connectionless, new EchoProtocol()));
+ PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Gateway Information", 2, ConnectionType.Connectionless, new GatewayInformationProtocol()));
+ PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Misc Services", 0x4, ConnectionType.Connectionless, new MiscServicesProtocol()));
+ PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Echo", 0x5, ConnectionType.Connectionless, new EchoProtocol()));
// RTP/BSP based:
- Dispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("CopyDisk", 0x15 /* 25B */, ConnectionType.BSP, new CopyDiskServer()));
+ PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("CopyDisk", 0x15 /* 25B */, ConnectionType.BSP, new CopyDiskServer()));
+
+ // TODO: MAKE THIS CONFIGURABLE.
+ PUPProtocolDispatcher.Instance.RegisterInterface(ifaces[2]);
while (true)
{
diff --git a/PUP/GatewayInformationProtocol.cs b/PUP/GatewayInformationProtocol.cs
new file mode 100644
index 0000000..e7996e2
--- /dev/null
+++ b/PUP/GatewayInformationProtocol.cs
@@ -0,0 +1,39 @@
+using IFS.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IFS
+{
+ ///
+ /// Gateway Information Protocol (see http://xeroxalto.computerhistory.org/_cd8_/pup/.gatewayinformation.press!1.pdf)
+ ///
+ public class GatewayInformationProtocol : PUPProtocolBase
+ {
+ public GatewayInformationProtocol()
+ {
+ // TODO:
+ // load host tables, etc.
+ }
+
+ ///
+ /// Called by dispatcher to send incoming data destined for this protocol
+ ///
+ ///
+ public override void RecvData(PUP p)
+ {
+ switch (p.Type)
+ {
+
+
+ default:
+ Log.Write(LogLevel.UnhandledProtocol, String.Format("Unhandled Gateway protocol {0}", p.Type));
+ break;
+ }
+ }
+
+
+ }
+}
diff --git a/PUP/IFS.csproj b/PUP/IFS.csproj
index 7e79323..5220e86 100644
--- a/PUP/IFS.csproj
+++ b/PUP/IFS.csproj
@@ -80,15 +80,21 @@
+
+
-
+
-
+
+
+ PreserveNewest
+
+