コード例 #1
0
        private void SendMailCheckResponse(PUP p)
        {
            //
            // "Pup Contents: A string specifying the mailbox name."
            //

            //
            // See if there is any mail for the specified mailbox.
            //
            string mailboxName = Helpers.ArrayToString(p.Contents);

            //
            // If mailbox name has a host/registry appended, we will strip it off.
            // TODO: probably should validate host...
            //
            mailboxName = Authentication.GetUserNameFromFullName(mailboxName);

            IEnumerable <string> mailList = MailManager.EnumerateMail(mailboxName);

            if (mailList == null || mailList.Count() == 0)
            {
                PUPPort localPort   = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     noMailReply = new PUP(PupType.NoNewMailExistsReply, p.ID, p.SourcePort, localPort, new byte[] { });

                Router.Instance.SendPup(noMailReply);
            }
            else
            {
                PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     mailReply = new PUP(PupType.NewMailExistsReply, p.ID, p.SourcePort, localPort, Helpers.StringToArray("You've got mail!"));

                Router.Instance.SendPup(mailReply);
            }
        }
コード例 #2
0
ファイル: EchoProtocol.cs プロジェクト: shirriff/IFS
        /// <summary>
        /// Called by dispatcher to send incoming data destined for this protocol
        /// </summary>
        /// <param name="p"></param>
        public override void RecvData(PUP p)
        {
            // If this is an EchoMe packet, we will send back an "ImAnEcho" packet.
            if (p.Type == PupType.EchoMe)
            {
                // Just send it back with the source/destination swapped.
                PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.SourcePort.Socket);

                //
                // 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);
                Router.Instance.SendPup(echoPup);
            }
        }
コード例 #3
0
        /// <summary>
        /// Construct a new packet from the supplied data.
        /// </summary>
        /// <param name="contentsContainsGarbageByte">
        /// True if the "contents" array contains a garbage (padding) byte that should NOT
        /// be factored into the Length field of the PUP.  This is necessary so we can properly
        /// support the Echo protocol; since some PUP tests that check validation require that the
        /// PUP be echoed in its entirety (including the supposedly-ignorable "garbage" byte on
        /// odd-length PUPs) we need to be able to craft a PUP with one extra byte of content that's
        /// otherwise ignored...
        ///
        /// TODO: Update to use Serialization code rather than packing bytes by hand.
        /// </param>
        ///
        public PUP(PupType type, byte transportControl, UInt32 id, PUPPort destination, PUPPort source, byte[] contents, bool contentsContainsGarbageByte)
        {
            _rawData = null;

            // Ensure content length is <= 532 bytes.  (Technically larger PUPs are allowed,
            // but conventionally they are not used and I want to keep things safe.)
            if (contents.Length > MAX_PUP_SIZE)
            {
                throw new InvalidOperationException("PUP size must not exceed 532 bytes.");
            }

            //
            // Sanity check:
            // "contentsContainGarbageByte" can ONLY be true if "contents" is of even length
            //
            if (contentsContainsGarbageByte && (contents.Length % 2) != 0)
            {
                throw new InvalidOperationException("Odd content length with garbage byte specified.");
            }

            TransportControl = transportControl;
            Type             = type;
            ID = id;
            DestinationPort = destination;
            SourcePort      = source;

            // Ensure contents are an even number of bytes.
            int contentLength = (contents.Length % 2) == 0 ? contents.Length : contents.Length + 1;

            Contents = new byte[contents.Length];
            contents.CopyTo(Contents, 0);

            // Length is always the real length of the data (not padded to an even number)
            Length = (ushort)(PUP_HEADER_SIZE + PUP_CHECKSUM_SIZE + contents.Length);

            // Stuff data into raw array
            _rawData = new byte[PUP_HEADER_SIZE + PUP_CHECKSUM_SIZE + contentLength];

            //
            // Subtract off one byte from the Length value if the contents contain a garbage byte.
            // (See header comments for function)
            if (contentsContainsGarbageByte)
            {
                Length--;
            }

            Helpers.WriteUShort(ref _rawData, Length, 0);
            _rawData[2] = TransportControl;
            _rawData[3] = (byte)Type;
            Helpers.WriteUInt(ref _rawData, ID, 4);
            DestinationPort.WriteToArray(ref _rawData, 8);
            SourcePort.WriteToArray(ref _rawData, 14);
            Array.Copy(Contents, 0, _rawData, 20, Contents.Length);

            // Calculate the checksum and stow it
            Checksum = CalculateChecksum();
            Helpers.WriteUShort(ref _rawData, Checksum, _rawData.Length - 2);
        }
コード例 #4
0
        private void SendMicrocodeFile(PUPPort sourcePort, Stream microcodeFile)
        {
            //
            // "For version 1 of the protocol, a server willing to supply the data simply sends a sequence of packets
            // of type MicrocodeReply as fast as it can.  The high half of its pupID contains the version number(1)
            // and the low half of the pupID contains the packet sequence number. After all the data packets
            // have been sent, the server sends an empty (0 data bytes) packet for an end marker.  There are no
            // acknowledgments. This protocol is used by Dolphins and Dorados.
            // Currently, the version 1 servers send packets containing 3 * n words of data.  This constraint is imposed by the
            // Rev L Dolphin EPROM microcode.  I’d like to remove this restriction if I get a chance, so please don’t take
            // advantage of it unless you need to.The Rev L Dolphin EPROM also requires the second word of the source
            // socket to be 4. / HGM May - 80."
            //

            // TODO: this should happen in a worker thread.
            //

            //
            // We send 192 words of data per PUP (3 * 64) in an attempt to make the Dolphin happy.
            // We space these out a bit to give the D-machine time to keep up, we're much much faster than they are.
            //
            PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, 4);

            byte[] buffer = new byte[384];
            bool   done   = false;
            uint   id     = 0;

            while (!done)
            {
                int read = microcodeFile.Read(buffer, 0, buffer.Length);

                if (read < buffer.Length)
                {
                    done = true;
                }

                if (read > 0)
                {
                    PUP microcodeReply = new PUP(PupType.MicrocodeReply, (id | 0x00010000), sourcePort, localPort, buffer);
                    Router.Instance.SendPup(microcodeReply);
                }

                // Pause a bit to give the D0 time to breathe.
                System.Threading.Thread.Sleep(5);

                id++;
            }

            //
            // Send an empty packet to conclude the transfer.
            //
            PUP endReply = new PUP(PupType.MicrocodeReply, (id | 0x00010000), sourcePort, localPort, new byte[] { });

            Router.Instance.SendPup(endReply);

            Log.Write(LogType.Warning, LogComponent.MiscServices, "Microcode file sent.");
        }
コード例 #5
0
ファイル: MiscServicesProtocol.cs プロジェクト: shirriff/IFS
        //string request = "GET /\r\n";
        //Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
        //Byte[] bytesReceived = new Byte[1000];

        //s.Send(bytesSent, bytesSent.Length, 0);
        //int bytes = 0;
        //bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
        //string data = Encoding.ASCII.GetString(bytesReceived, 0, bytes);
        //Log.Write(LogType.Verbose, LogComponent.Exp, "Received {0}", data);


        private void SendNameLookupReply(PUP p)
        {
            //
            // For the request PUP:
            //  A string consisting of an inter-network name expression.
            //  NOTE: This is *not* a BCPL string, just the raw characters.
            //
            // Response:
            //  One or more 6-byte blocks containing the address(es) corresponding to the
            //  name expression.  Each block is a Pup Port structure, with the network and host numbers in
            //  the first two bytes and the socket number in the last four bytes.
            //

            //
            // For now, the assumption is that each name maps to at most one address.
            //
            string lookupName = Helpers.ArrayToString(p.Contents);

            Log.Write(LogType.Verbose, LogComponent.MiscServices, "Name lookup is for '{0}'", lookupName);

            HostAddress address = DirectoryServices.Instance.NameLookup(lookupName);

            if (address == null)
            {
                address = ExternalHost.LookupExternalHost(lookupName);
            }

            if (address != null)
            {
                // We found an address, pack the port into the response.
                PUPPort lookupPort  = new PUPPort(address, 0);
                PUPPort localPort   = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     lookupReply = new PUP(PupType.NameLookupResponse, p.ID, p.SourcePort, localPort, lookupPort.ToArray());

                Router.Instance.SendPup(lookupReply);

                Log.Write(LogType.Verbose, LogComponent.MiscServices, "Address is '{0}'", address);
            }
            else
            {
                // Unknown host, send an error reply
                string  errorString = "Unknown host.";
                PUPPort localPort   = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     errorReply  = new PUP(PupType.DirectoryLookupErrorReply, p.ID, p.SourcePort, localPort, Helpers.StringToArray(errorString));

                Router.Instance.SendPup(errorReply);

                Log.Write(LogType.Verbose, LogComponent.MiscServices, "Host is unknown.");
            }
        }
コード例 #6
0
        private void SendBootDirectory(PUP p)
        {
            //
            // From etherboot.bravo
            // "Pup ID: if it is in reply to a BootDirRequest, the ID should match the request.
            // Pup Contents: 1 or more blocks of the following format: A boot file number (the number that goes in the low 16 bits of a
            // BootFileRequest Pup), an Alto format date (2 words), a boot file name in BCPL string format."
            //
            MemoryStream ms = new MemoryStream(PUP.MAX_PUP_SIZE);

            List <BootFileEntry> bootFiles = BootServer.EnumerateBootFiles();

            foreach (BootFileEntry entry in bootFiles)
            {
                BootDirectoryBlock block;
                block.FileNumber = entry.BootNumber;
                block.FileDate   = 0;
                block.FileName   = new BCPLString(entry.Filename);

                byte[] serialized = Serializer.Serialize(block);

                //
                // If this block fits into the current PUP, add it to the stream, otherwise send off the current PUP
                // and start a new one.
                //
                if (serialized.Length + ms.Length <= PUP.MAX_PUP_SIZE)
                {
                    ms.Write(serialized, 0, serialized.Length);
                }
                else
                {
                    PUPPort localPort    = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                    PUP     bootDirReply = new PUP(PupType.BootDirectoryReply, p.ID, p.SourcePort, localPort, ms.ToArray());
                    Router.Instance.SendPup(bootDirReply);

                    ms.Seek(0, SeekOrigin.Begin);
                    ms.SetLength(0);
                }
            }

            // Shuffle out any remaining data.
            if (ms.Length > 0)
            {
                PUPPort localPort    = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     bootDirReply = new PUP(PupType.BootDirectoryReply, p.ID, p.SourcePort, localPort, ms.ToArray());
                Router.Instance.SendPup(bootDirReply);
            }
        }
コード例 #7
0
        private void SendAddressLookupReply(PUP p)
        {
            //
            // Need to find more... useful documentation, but here's what I have:
            // For the request PUP:
            //  A port (6 bytes).
            //
            // Response:
            //  A string consisting of an inter-network name expression that matches the request Port.
            //

            //
            // I am at this time unsure what exactly an "inter-network name expression" consists of.
            // Empirically, a simple string name seems to make the Alto happy.
            //

            //
            // The request PUP contains a port address, we will check the host and network (and ignore the socket).
            // and see if we have a match.
            //
            PUPPort lookupAddress = new PUPPort(p.Contents, 0);
            string  hostName      = DirectoryServices.Instance.AddressLookup(new HostAddress(lookupAddress.Network, lookupAddress.Host));

            if (!String.IsNullOrEmpty(hostName))
            {
                // We have a result, pack the name into the response.
                // NOTE: This is *not* a BCPL string, just the raw characters.
                byte[] interNetworkName = Helpers.StringToArray(hostName);

                PUPPort localPort   = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     lookupReply = new PUP(PupType.AddressLookupResponse, p.ID, p.SourcePort, localPort, interNetworkName);

                Router.Instance.SendPup(lookupReply);
            }
            else
            {
                // Unknown host, send an error reply
                string  errorString = "Unknown host.";
                PUPPort localPort   = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     errorReply  = new PUP(PupType.DirectoryLookupErrorReply, p.ID, p.SourcePort, localPort, Helpers.StringToArray(errorString));

                Router.Instance.SendPup(errorReply);
            }
        }
コード例 #8
0
        /// <summary>
        /// Load in an existing packet from a stream
        /// </summary>
        /// <param name="stream"></param>
        public PUP(MemoryStream stream, int length)
        {
            _rawData = new byte[length];
            stream.Read(_rawData, 0, length);

            // Read fields in.
            Length = Helpers.ReadUShort(_rawData, 0);

            // Sanity check size:
            if (Length > length)
            {
                throw new InvalidOperationException("Length field in PUP is invalid.");
            }

            TransportControl = _rawData[2];
            Type             = (PupType)_rawData[3];
            ID = Helpers.ReadUInt(_rawData, 4);
            DestinationPort = new PUPPort(_rawData, 8);
            SourcePort      = new PUPPort(_rawData, 14);

            int contentLength = Length - PUP_HEADER_SIZE - PUP_CHECKSUM_SIZE;

            Contents = new byte[contentLength];
            Array.Copy(_rawData, 20, Contents, 0, contentLength);

            // Length is the number of valid bytes in the PUP, which may be an odd number.
            // There are always an even number of bytes in the PUP, and the checksum
            // therefore begins on an even byte boundary.  Calculate the checksum offset
            // appropriately.  (Empirically, we could also just use the last word of the packet,
            // as the Alto PUP implementation never appears to pad any extra data after the PUP, but
            // this doesn't appear to be a requirement and I don't want to rely on it.)
            int checksumOffset = (Length % 2) == 0 ? Length - PUP_CHECKSUM_SIZE : Length - PUP_CHECKSUM_SIZE + 1;

            Checksum = Helpers.ReadUShort(_rawData, checksumOffset);

            // Validate checksum
            ushort cChecksum = CalculateChecksum();

            if (Checksum != 0xffff && cChecksum != Checksum)
            {
                // TODO: determine what to do with packets that are corrupted -- drop, or continue anyway?
                Log.Write(LogType.Warning, LogComponent.PUP, "PUP checksum is invalid. (got {0:x}, expected {1:x})", Checksum, cChecksum);
            }
        }
コード例 #9
0
        private void SendAltoTimeReply(PUP p)
        {
            // So the Alto epoch is 1/1/1901.  For the time being to keep things simple we're assuming
            // GMT and no DST at all.  TODO: make this take into account our TZ, etc.
            //
            // Additionally:  While certain routines seem to be Y2K compliant (the time requests made from
            // the Alto's "puptest" diagnostic, for example), the Executive is not.  To keep things happy,
            // we move things back 28 years so that the calendar at least matches up.
            //
            DateTime currentTime =
                new DateTime(
                    DateTime.Now.Year - 28,
                    DateTime.Now.Month,
                    DateTime.Now.Day,
                    DateTime.Now.Hour,
                    DateTime.Now.Minute,
                    DateTime.Now.Second);

            // The epoch for .NET is 1/1/0001 at 12 midnight and is counted in 100-ns intervals.
            // Some conversion is needed, is what I'm saying.
            DateTime altoEpoch = new DateTime(1901, 1, 1);

            TimeSpan timeSinceAltoEpoch = new TimeSpan(currentTime.Ticks - altoEpoch.Ticks);

            UInt32 altoTime = (UInt32)timeSinceAltoEpoch.TotalSeconds;

            // Build the response data
            AltoTime time = new AltoTime();

            time.DateTime = altoTime;
            time.TimeZone = 0;      // Hardcoded to GMT
            time.DSTStart = 366;    // DST not specified yet
            time.DSTEnd   = 366;

            PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);

            // Response must contain our network number; this is used to tell clients what network they're on if they don't already know.
            PUPPort remotePort = new PUPPort(DirectoryServices.Instance.LocalNetwork, p.SourcePort.Host, p.SourcePort.Socket);

            PUP response = new PUP(PupType.AltoTimeResponse, p.ID, remotePort, localPort, Serializer.Serialize(time));

            Router.Instance.SendPup(response);
        }
コード例 #10
0
        private void SendAuthenticationResponse(PUP p)
        {
            //
            // "Pup Contents: Two Mesa strings (more precisely StringBodys), packed in such a way that
            //  the maxLength of the first string may be used to locate the second string.  The first
            //  string is a user name and the second a password."
            //

            // I have chosen not to write a helper class encapsulating Mesa strings since this is the
            // first (and so far *only*) instance in which Mesa strings are used in IFS communications.
            //

            // Empirical analysis shows the format of a Mesa string to be:
            // Word 1: Length (bytes)
            // Word 2: MaxLength (bytes)
            // Byte 4 thru 4 + MaxLength: string data
            // data is padded to a word length.
            //
            string userName = Helpers.MesaArrayToString(p.Contents, 0);

            int    passwordOffset = (userName.Length % 2) == 0 ? userName.Length : userName.Length + 1;
            string password       = Helpers.MesaArrayToString(p.Contents, passwordOffset + 4);

            UserToken token = Authentication.Authenticate(userName, password);

            if (token == null)
            {
                string  errorString = "Invalid username or password.";
                PUPPort localPort   = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     errorReply  = new PUP(PupType.AuthenticateNegativeResponse, p.ID, p.SourcePort, localPort, Helpers.StringToArray(errorString));

                Router.Instance.SendPup(errorReply);
            }
            else
            {
                // S'ok!
                PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
                PUP     okReply   = new PUP(PupType.AuthenticatePositiveResponse, p.ID, p.SourcePort, localPort, new byte[] { });

                Router.Instance.SendPup(okReply);
            }
        }
コード例 #11
0
        private void SendStringTimeReply(PUP p)
        {
            //
            // From the spec, the response is:
            // "A string consisting of the current date and time in the form
            // '11-SEP-75 15:44:25'"
            // NOTE: This is *not* a BCPL string, just the raw characters.
            //
            // It makes no mention of timezone or DST, so I am assuming local time here.
            // Good enough for government work.
            //
            DateTime currentTime = DateTime.Now;

            byte[] timeString = Helpers.StringToArray(currentTime.ToString("dd-MMM-yy HH:mm:ss"));

            PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.SourcePort.Socket);
            PUP     response  = new PUP(PupType.StringTimeReply, p.ID, p.SourcePort, localPort, timeString);

            Router.Instance.SendPup(response);
        }
コード例 #12
0
 /// <summary>
 /// Same as above, but with no content (i.e. a zero-byte payload)
 /// </summary>
 /// <param name="type"></param>
 /// <param name="id"></param>
 /// <param name="destination"></param>
 /// <param name="source"></param>
 public PUP(PupType type, UInt32 id, PUPPort destination, PUPPort source) :
     this(type, id, destination, source, new byte[0])
 {
 }
コード例 #13
0
 /// <summary>
 /// Same as above, no garbage byte.
 /// </summary>
 /// <param name="type"></param>
 /// <param name="id"></param>
 /// <param name="destination"></param>
 /// <param name="source"></param>
 /// <param name="contents"></param>
 public PUP(PupType type, UInt32 id, PUPPort destination, PUPPort source, byte[] contents) :
     this(type, id, destination, source, contents, false)
 {
 }
コード例 #14
0
 /// <summary>
 /// Constructor that assumes a transport control of 0
 /// </summary>
 /// <param name="type"></param>
 /// <param name="id"></param>
 /// <param name="destination"></param>
 /// <param name="source"></param>
 /// <param name="contents"></param>
 /// <param name="contentsContainsGarbageByte"></param>
 public PUP(PupType type, UInt32 id, PUPPort destination, PUPPort source, byte[] contents, bool contentsContainsGarbageByte) :
     this(type, 0, id, destination, source, contents, contentsContainsGarbageByte)
 {
 }
コード例 #15
0
ファイル: DirectoryServices.cs プロジェクト: shirriff/IFS
 public HostAddress(PUPPort port)
 {
     Network = port.Network;
     Host    = port.Host;
 }