From 32beacdca89585479cd2ef2e254bb6958478b6b4 Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Wed, 27 Jan 2016 17:24:51 -0800 Subject: [PATCH] Progress made; updated to work with Contralto, initial stubs for CopyDisk. Misc services work. --- PUP/BCPLString.cs | 42 +++- PUP/BSPManager.cs | 208 ++++++++++++---- PUP/Conf/hosts.txt | 23 ++ PUP/CopyDiskServer.cs | 94 ++++++- PUP/DirectoryServices.cs | 224 ++++++++++++++++- PUP/EchoProtocol.cs | 25 +- PUP/Entrypoint.cs | 40 ++- PUP/GatewayInformationProtocol.cs | 39 +++ PUP/IFS.csproj | 10 +- PUP/Logging/Log.cs | 6 +- PUP/MiscServicesProtocol.cs | 136 ++++++---- PUP/PUP.cs | 235 +++++++++++++----- PUP/PUPProtocolBase.cs | 3 +- ...Dispatcher.cs => PUPProtocolDispatcher.cs} | 31 ++- PUP/Serializer.cs | 160 ++++++++++++ PUP/Transport/Ethernet.cs | 125 ++++++++-- 16 files changed, 1179 insertions(+), 222 deletions(-) create mode 100644 PUP/Conf/hosts.txt create mode 100644 PUP/GatewayInformationProtocol.cs rename PUP/{Dispatcher.cs => PUPProtocolDispatcher.cs} (68%) create mode 100644 PUP/Serializer.cs 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 + +