Example #1
0
        internal static ClassifyState TransitionExpectProtocolError(
            ClassifyState state, Frame frame, ref ProtocolError error, ref FrameDisposition disposition, Logger logger)
        {
            if (state != ClassifyState.ExpectProtocolError || frame == null || frame.Count == 0
                || frame.Framelets[0].Type != FrameletType.ProtocolError)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count > 1)
            {
                logger.Site().Error("Protocol error frame had trailing framelets.");
                return ClassifyState.ErrorInErrorFrame;
            }

            var framelet = frame.Framelets[0];

            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);
            switch (errorDeserializer.TryDeserialize(fastBinaryReader, out error))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(ProtocolError));
                    return ClassifyState.ErrorInErrorFrame;
            }

            logger.Site().Debug("Deserialized {0} with code {1}.", nameof(ProtocolError), error.error_code);
            disposition = FrameDisposition.HandleProtocolError;
            return ClassifyState.ClassifiedValidFrame;
        }
Example #2
0
        internal static ClassifyState TransitionFrameComplete(
            ClassifyState state, EpoxyHeaders headers, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            if (state != ClassifyState.FrameComplete || headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            switch (headers.message_type)
            {
                case EpoxyMessageType.REQUEST:
                case EpoxyMessageType.RESPONSE:
                case EpoxyMessageType.EVENT:
                    return ClassifyState.ValidFrame;

                default:
                    logger.Site().Warning("Received unrecognized message type {0}.", headers.message_type);
                    errorCode = ProtocolErrorCode.NOT_SUPPORTED;
                    return ClassifyState.MalformedFrame;
            }
        }
Example #3
0
        internal static ClassifyState TransitionExpectConfig(
            ClassifyState state, Frame frame, ref ProtocolErrorCode? errorCode, ref FrameDisposition disposition,
            Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectConfig);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyConfig)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count != 1)
            {
                logger.Site().Error("Config frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[0];

            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);

            // We don't currently do anything with the config aside from try to deserialize it.
            EpoxyConfig config;
            switch (configDeserializer.TryDeserialize(fastBinaryReader, out config))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyConfig));
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }

            disposition = FrameDisposition.ProcessConfig;
            return ClassifyState.ClassifiedValidFrame;
        }
Example #4
0
        internal static ClassifyState TransitionExpectMessageData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ArraySegment<byte> layerData,
            ref MessageData messageData, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectMessageData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            int messageDataIndex = (layerData.Array == null ? 1 : 2);
            if (messageDataIndex >= frame.Count)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[messageDataIndex];
            if (framelet.Type != FrameletType.PayloadData && framelet.Type != FrameletType.ErrorData)
            {
                logger.Site().Error("Frame had headers but no message data. Unexpected framelet type {0}", (int)framelet.Type);
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            messageData = new MessageData(
                isError: framelet.Type == FrameletType.ErrorData,
                data: framelet.Contents);

            logger.Site().Debug("Extracted {0}-byte message in conversation ID {1}.",
                messageData.Data.Count, headers.conversation_id);
            return ClassifyState.ExpectEndOfFrame;
        }
Example #5
0
        internal static ClassifyState TransitionExpectEndOfFrame(
            ClassifyState state, Frame frame, ArraySegment<byte> layerData, ref ProtocolErrorCode? errorCode,
            Logger logger)
        {
            // FIXME: Change all of these to asserts.
            if (state != ClassifyState.ExpectEndOfFrame || frame == null)
            {
                return ClassifyState.InternalStateError;
            }

            var validFrameSize = (layerData.Array == null ? 2 : 3);
            if (frame.Count == validFrameSize)
            {
                return ClassifyState.FrameComplete;
            }
            else
            {
                logger.Site().Error("Frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }
        }
Example #6
0
        internal static ClassifyState TransitionExpectEpoxyHeaders(
            ClassifyState state, Frame frame, ref EpoxyHeaders headers, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectEpoxyHeaders);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyHeaders)
            {
                return ClassifyState.InternalStateError;
            }

            var framelet = frame.Framelets[0];
            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);
            switch (headersDeserializer.TryDeserialize(fastBinaryReader, out headers))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyHeaders));
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }

            logger.Site().Debug("Deserialized {0} with conversation ID {1} and message type {2}.",
                nameof(EpoxyHeaders), headers.conversation_id, headers.message_type);
            return ClassifyState.ExpectOptionalLayerData;
        }
Example #7
0
        internal static ClassifyState TransitionExpectOptionalLayerData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ref ArraySegment<byte> layerData,
            ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectOptionalLayerData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count < 2)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[1];
            if (framelet.Type == FrameletType.LayerData)
            {
                layerData = framelet.Contents;
                logger.Site().Debug("Extracted {0}-byte layer data in conversation ID {1}.",
                    layerData.Count, headers.conversation_id);
            }

            return ClassifyState.ExpectMessageData;
        }
Example #8
0
        internal static ClassifyState TransitionExpectFirstFramelet(
            ClassifyState state, Frame frame, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectFirstFramelet);
            Debug.Assert(frame != null);

            if (frame.Framelets.Count == 0)
            {
                logger.Site().Error("Frame was empty.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            switch (frame.Framelets[0].Type)
            {
                case FrameletType.EpoxyHeaders:
                    return ClassifyState.ExpectEpoxyHeaders;

                case FrameletType.EpoxyConfig:
                    return ClassifyState.ExpectConfig;

                case FrameletType.ProtocolError:
                    return ClassifyState.ExpectProtocolError;

                default:
                    logger.Site().Error("Frame began with invalid FrameletType {0}.", frame.Framelets[0].Type);
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }
        }
Example #9
0
        internal static ClassifyResult Classify(Frame frame, Logger logger)
        {
            if (frame == null)
            {
                return new ClassifyResult
                {
                    Disposition = FrameDisposition.Indeterminate
                };
            }

            logger.Site().Debug("Processing {0} framelets.", frame.Count);

            var state = ClassifyState.ExpectFirstFramelet;
            var disposition = FrameDisposition.Indeterminate;
            EpoxyHeaders headers = null;
            var layerData = new ArraySegment<byte>();
            var messageData = default(MessageData);
            ProtocolError error = null;
            ProtocolErrorCode? errorCode = null;
            uint transitions = 0;
            while (true)
            {
                // If it looks like we have a bug and are looping forever, bail out of the state machine.
                if (transitions++ > maximumTransitions)
                {
                    return new ClassifyResult
                    {
                        Disposition = FrameDisposition.Indeterminate
                    };
                }

                switch (state)
                {
                    case ClassifyState.ExpectFirstFramelet:
                        state = TransitionExpectFirstFramelet(state, frame, ref errorCode, logger);
                        continue;

                    case ClassifyState.ExpectEpoxyHeaders:
                        state = TransitionExpectEpoxyHeaders(state, frame, ref headers, ref errorCode, logger);
                        continue;

                    case ClassifyState.ExpectOptionalLayerData:
                        state = TransitionExpectOptionalLayerData(state, frame, headers, ref layerData, ref errorCode, logger);
                        continue;

                    case ClassifyState.ExpectMessageData:
                        state = TransitionExpectMessageData(state, frame, headers, layerData, ref messageData, ref errorCode, logger);
                        continue;

                    case ClassifyState.ExpectEndOfFrame:
                        state = TransitionExpectEndOfFrame(state, frame, layerData, ref errorCode, logger);
                        continue;

                    case ClassifyState.FrameComplete:
                        state = TransitionFrameComplete(state, headers, ref errorCode, logger);
                        continue;

                    case ClassifyState.ValidFrame:
                        state = TransitionValidFrame(state, headers, ref disposition);
                        continue;

                    case ClassifyState.ExpectConfig:
                        state = TransitionExpectConfig(state, frame, ref errorCode, ref disposition, logger);
                        continue;

                    case ClassifyState.ExpectProtocolError:
                        state = TransitionExpectProtocolError(state, frame, ref error, ref disposition, logger);
                        continue;

                    case ClassifyState.ClassifiedValidFrame:
                        if (disposition == FrameDisposition.Indeterminate)
                        {
                            state = ClassifyState.InternalStateError;
                            continue;
                        }

                        return new ClassifyResult
                        {
                            Disposition = disposition,
                            Headers = headers,
                            LayerData = layerData,
                            MessageData = messageData,
                            Error = error
                        };

                    case ClassifyState.MalformedFrame:
                        if (errorCode == null)
                        {
                            state = ClassifyState.InternalStateError;
                            continue;
                        }

                        return new ClassifyResult
                        {
                            Disposition = FrameDisposition.SendProtocolError,
                            ErrorCode = errorCode
                        };

                    case ClassifyState.ErrorInErrorFrame:
                        return new ClassifyResult
                        {
                            Disposition = FrameDisposition.HangUp,
                            Error = new ProtocolError
                            {
                                error_code = ProtocolErrorCode.ERROR_IN_ERROR
                            }
                        };

                    case ClassifyState.InternalStateError:
                        return new ClassifyResult
                        {
                            Disposition = FrameDisposition.Indeterminate
                        };

                    default:
                        logger.Site().Error("Unhandled state {0}. Dropping frame.", state);
                        return new ClassifyResult
                        {
                            Disposition = FrameDisposition.Indeterminate
                        };
                }
            }
        }
Example #10
0
        public static async Task<EpoxyNetworkStream> MakeClientStreamAsync(
            string remoteHostname,
            Socket socket,
            EpoxyClientTlsConfig tlsConfig,
            Logger logger)
        {
            Stream clientStream;
            NetworkStream networkStream = new NetworkStream(socket, ownsSocket: false);

            if (tlsConfig == null)
            {
                clientStream = networkStream;
            }
            else
            {
                const bool leaveInnerStreamOpen = false;

                var sslStream = new SslStream(
                    networkStream,
                    leaveInnerStreamOpen,
                    tlsConfig.RemoteCertificateValidationCallback);

                var clientCertificates = new X509CertificateCollection();
                if (tlsConfig.Certificate != null)
                {
                    clientCertificates.Add(tlsConfig.Certificate);
                }

                await sslStream.AuthenticateAsClientAsync(
                    remoteHostname,
                    clientCertificates,
                    AllowedTlsProtocols,
                    tlsConfig.CheckCertificateRevocation);

                logger.Site().Debug("Authenticated connection to {0}[{1}]", remoteHostname, socket.RemoteEndPoint);

                clientStream = sslStream;
            }

            return new EpoxyNetworkStream(socket, clientStream, logger);
        }
Example #11
0
        private static RemoteCertificateValidationCallback MakeServerCertificateValidationCallback(
            EpoxyServerTlsConfig tlsConfig,
            Logger logger)
        {
            if (tlsConfig.ClientCertificateRequired)
            {
                // If client certificates are required, then add an explicit
                // check that the client provided a certificate. The default
                // behavior is to allow the connection even if the client
                // didn't present a certificate.
                return (sender, certificate, chain, errors) =>
                {
                    if (certificate == null)
                    {
                        logger.Site().Error("Rejecting client. Certificate required, but client did not provide one.");
                        return false;
                    }

                    if (tlsConfig.RemoteCertificateValidationCallback != null)
                    {
                        // There's a user-provided validation callback, so
                        // delegate to that.
                        return tlsConfig.RemoteCertificateValidationCallback(
                            sender,
                            certificate,
                            chain,
                            errors);
                    }
                    else
                    {
                        // Otherwise, require no errors at all to accept the
                        // certificate.
                        return errors == SslPolicyErrors.None;
                    }
                };
            }
            else
            {
                // Client certificates are not required, so just use the
                // user-provided validation callback. This may be null, but
                // that's fine. SslStream will just use its default behavior
                // then.
                return tlsConfig.RemoteCertificateValidationCallback;
            }
        }
Example #12
0
        public static async Task<EpoxyNetworkStream> MakeServerStreamAsync(
            Socket socket,
            EpoxyServerTlsConfig tlsConfig,
            Logger logger)
        {
            Stream serverStream;
            var networkStream = new NetworkStream(socket, ownsSocket: false);

            if (tlsConfig == null)
            {
                serverStream = networkStream;
            }
            else
            {
                const bool leaveInnerStreamOpen = false;

                var sslStream = new SslStream(
                    networkStream,
                    leaveInnerStreamOpen,
                    MakeServerCertificateValidationCallback(tlsConfig, logger));

                await sslStream.AuthenticateAsServerAsync(
                    tlsConfig.Certificate,
                    tlsConfig.ClientCertificateRequired,
                    enabledSslProtocols: AllowedTlsProtocols,
                    checkCertificateRevocation: tlsConfig.CheckCertificateRevocation);

                if (tlsConfig.ClientCertificateRequired && !sslStream.IsMutuallyAuthenticated)
                {
                    sslStream.Dispose();
                    throw new AuthenticationException("Mutual authentication was required, but it could not be performed.");
                }

                logger.Site().Debug(
                    "Authenticated connection from {0}. Mutually authenticated?: {1}",
                    socket.RemoteEndPoint,
                    sslStream.IsMutuallyAuthenticated);

                serverStream = sslStream;
            }

            return new EpoxyNetworkStream(socket, serverStream, logger);
        }