/// <summary>
        ///     Decodes the specified region of the buffer into an unadjusted frame length.  The default implementation is
        ///     capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer.  Override this method to
        ///     decode the length field encoded differently.
        ///     Note that this method must not modify the state of the specified buffer (e.g.
        ///     <see cref="IByteBuffer.ReaderIndex" />,
        ///     <see cref="IByteBuffer.WriterIndex" />, and the content of the buffer.)
        /// </summary>
        /// <param name="buffer">The buffer we'll be extracting the frame length from.</param>
        /// <param name="offset">The offset from the absolute <see cref="IByteBuffer.ReaderIndex" />.</param>
        /// <param name="length">The length of the framelenght field. Expected: 1, 2, 3, 4, or 8.</param>
        /// <param name="order">The preferred <see cref="ByteOrder" /> of buffer.</param>
        /// <returns>A long integer that represents the unadjusted length of the next frame.</returns>
        protected virtual long GetUnadjustedFrameLength(IByteBuffer buffer, int offset, int length, ByteOrder order)
        {
            long frameLength;

            switch (length)
            {
            case 1:
                frameLength = buffer.GetByte(offset);
                break;

            case 2:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetUnsignedShort(offset) : buffer.GetUnsignedShortLE(offset);
                break;

            case 3:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetUnsignedMedium(offset) : buffer.GetUnsignedMediumLE(offset);
                break;

            case 4:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetInt(offset) : buffer.GetIntLE(offset);
                break;

            case 8:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetLong(offset) : buffer.GetLongLE(offset);
                break;

            default:
                throw new DecoderException("unsupported lengthFieldLength: " + this.lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
            }
            return(frameLength);
        }
 /// <summary>
 ///     Decodes the specified region of the buffer into an unadjusted frame length.  The default implementation is
 ///     capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer.  Override this method to
 ///     decode the length field encoded differently.
 ///     Note that this method must not modify the state of the specified buffer (e.g.
 ///     <see cref="IByteBuffer.ReaderIndex" />,
 ///     <see cref="IByteBuffer.WriterIndex" />, and the content of the buffer.)
 /// </summary>
 /// <param name="buffer">The buffer we'll be extracting the frame length from.</param>
 /// <param name="offset">The offset from the absolute <see cref="IByteBuffer.ReaderIndex" />.</param>
 /// <param name="length">The length of the framelenght field. Expected: 1, 2, 3, 4, or 8.</param>
 /// <returns>A long integer that represents the unadjusted length of the next frame.</returns>
 protected static long GetUnadjustedFrameLength(IByteBuffer buffer, int offset, int length)
 {
     return(length switch
     {
         1 => buffer.GetByte(offset),
         2 => buffer.GetUnsignedShort(offset),
         3 => buffer.GetUnsignedMedium(offset),
         4 => buffer.GetInt(offset),
         8 => buffer.GetLong(offset),
         _ => CThrowHelper.ThrowDecoderException(length),
     });
Esempio n. 3
0
        protected override ThriftMessage Decode(IChannelHandlerContext ctx, IByteBuffer buffer)
        {
            //buffer.WithOrder(ByteOrder.BigEndian);
            if (!buffer.IsReadable())
            {
                return(null);
            }

            ushort firstByte = buffer.GetUnsignedShort(0);

            if (firstByte >= 0x80)
            {
                IByteBuffer messageBuffer = this.TryDecodeUnframedMessage(ctx, ctx.Channel, buffer, _inputProtocolFactory);

                if (messageBuffer == null)
                {
                    return(null);
                }
                // A non-zero MSB for the first byte of the message implies the message starts with a
                // protocol id (and thus it is unframed).
                return(new ThriftMessage(messageBuffer, ThriftTransportType.Unframed));
            }
            else if (buffer.ReadableBytes < MessageFrameSize)
            {
                // Expecting a framed message, but not enough bytes available to read the frame size
                return(null);
            }
            else
            {
                IByteBuffer messageBuffer = this.TryDecodeFramedMessage(ctx, ctx.Channel, buffer, true);

                if (messageBuffer == null)
                {
                    return(null);
                }
                // Messages with a zero MSB in the first byte are framed messages
                return(new ThriftMessage(messageBuffer, ThriftTransportType.Framed));
            }
        }
Esempio n. 4
0
        /// <summary>
        ///     Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
        ///     the readerIndex of the given <see cref="IByteBuffer"/>.
        /// </summary>
        /// <param name="buffer">
        ///     The <see cref="IByteBuffer"/> to read from. Be aware that it must have at least
        ///     <see cref="SSL_RECORD_HEADER_LENGTH"/> bytes to read,
        ///     otherwise it will throw an <see cref="ArgumentException"/>.
        /// </param>
        /// <param name="offset">Offset to record start.</param>
        /// <returns>
        ///     The length of the encrypted packet that is included in the buffer. This will
        ///     return <c>-1</c> if the given <see cref="IByteBuffer"/> is not encrypted at all.
        /// </returns>
        public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset)
        {
            int packetLength = 0;

            // SSLv3 or TLS - Check ContentType
            switch (buffer.GetByte(offset))
            {
            case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC:
            case SSL_CONTENT_TYPE_ALERT:
            case SSL_CONTENT_TYPE_HANDSHAKE:
            case SSL_CONTENT_TYPE_APPLICATION_DATA:
                break;

            default:
                // SSLv2 or bad data
                return(-1);
            }
            // SSLv3 or TLS - Check ProtocolVersion
            int majorVersion = buffer.GetByte(offset + 1);

            if (majorVersion == 3)
            {
                // SSLv3 or TLS
                packetLength = buffer.GetUnsignedShort(offset + 3) + SSL_RECORD_HEADER_LENGTH;
                if (packetLength <= SSL_RECORD_HEADER_LENGTH)
                {
                    // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
                    return(-1);
                }
            }
            else
            {
                // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
                return(-1);
            }

            return(packetLength);
        }
Esempio n. 5
0
        /// <summary>
        ///     Decodes the specified region of the buffer into an unadjusted frame length.  The default implementation is
        ///     capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer.  Override this method to
        ///     decode the length field encoded differently.
        ///     Note that this method must not modify the state of the specified buffer (e.g.
        ///     <see cref="IByteBuffer.ReaderIndex" />,
        ///     <see cref="IByteBuffer.WriterIndex" />, and the content of the buffer.)
        /// </summary>
        /// <param name="buffer">The buffer we'll be extracting the frame length from.</param>
        /// <param name="offset">The offset from the absolute <see cref="IByteBuffer.ReaderIndex" />.</param>
        /// <param name="length">The length of the framelenght field. Expected: 1, 2, 3, 4, or 8.</param>
        /// <param name="order">The preferred <see cref="ByteOrder" /> of buffer.</param>
        /// <returns>A long integer that represents the unadjusted length of the next frame.</returns>
        protected long GetUnadjustedFrameLength(IByteBuffer buffer, int offset, int length, ByteOrder order)
        {
            long frameLength;

            switch (length)
            {
            case 1:
                frameLength = buffer.GetByte(offset);
                break;

            case 2:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetUnsignedShort(offset) : buffer.GetUnsignedShortLE(offset);
                break;

            case 3:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetUnsignedMedium(offset) : buffer.GetUnsignedMediumLE(offset);
                break;

            case 4:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetInt(offset) : buffer.GetIntLE(offset);
                break;

            case 8:
                frameLength = order == ByteOrder.BigEndian ? buffer.GetLong(offset) : buffer.GetLongLE(offset);
                break;

            default:
                throw new DecoderException("unsupported lengthFieldLength: " + this.lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
            }
            byte[] bytes = BitConverter.GetBytes(frameLength);

            string slength = Encoding.UTF8.GetString(bytes);

            frameLength = long.Parse(slength);

            return(frameLength);
        }
Esempio n. 6
0
        const int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14

        #endregion Fields

        #region Methods

        /// <summary>
        ///     Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
        ///     the readerIndex of the given <see cref="IByteBuffer"/>.
        /// </summary>
        /// <param name="buffer">
        ///     The <see cref="IByteBuffer"/> to read from. Be aware that it must have at least
        ///     <see cref="SSL_RECORD_HEADER_LENGTH"/> bytes to read,
        ///     otherwise it will throw an <see cref="ArgumentException"/>.
        /// </param>
        /// <param name="offset">Offset to record start.</param>
        /// <returns>
        ///     The length of the encrypted packet that is included in the buffer. This will
        ///     return <c>-1</c> if the given <see cref="IByteBuffer"/> is not encrypted at all.
        /// </returns>
        public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset)
        {
            int packetLength = 0;

            // SSLv3 or TLS - Check ContentType
            switch (buffer.GetByte(offset))
            {
                case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC:
                case SSL_CONTENT_TYPE_ALERT:
                case SSL_CONTENT_TYPE_HANDSHAKE:
                case SSL_CONTENT_TYPE_APPLICATION_DATA:
                    break;
                default:
                    // SSLv2 or bad data
                    return -1;
            }
            // SSLv3 or TLS - Check ProtocolVersion
            int majorVersion = buffer.GetByte(offset + 1);
            if (majorVersion == 3)
            {
                // SSLv3 or TLS
                packetLength = buffer.GetUnsignedShort(offset + 3) + SSL_RECORD_HEADER_LENGTH;
                if (packetLength <= SSL_RECORD_HEADER_LENGTH)
                {
                    // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
                    return -1;
                }
            }
            else
            {
                // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
                return -1;
            }

            return packetLength;
        }
Esempio n. 7
0
        protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List <object> output)
        {
            if (!this.suppressRead && !this.handshakeFailed)
            {
                int       writerIndex = input.WriterIndex;
                Exception error       = null;
                try
                {
                    bool continueLoop = true;
                    for (int i = 0; i < MAX_SSL_RECORDS && continueLoop; i++)
                    {
                        int readerIndex   = input.ReaderIndex;
                        int readableBytes = writerIndex - readerIndex;
                        if (readableBytes < TlsUtils.SSL_RECORD_HEADER_LENGTH)
                        {
                            // Not enough data to determine the record type and length.
                            return;
                        }

                        int command = input.GetByte(readerIndex);
                        // tls, but not handshake command
                        switch (command)
                        {
                        case TlsUtils.SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC:
                        case TlsUtils.SSL_CONTENT_TYPE_ALERT:
                            int len = TlsUtils.GetEncryptedPacketLength(input, readerIndex);

                            // Not an SSL/TLS packet
                            if (len == TlsUtils.NOT_ENCRYPTED)
                            {
                                this.handshakeFailed = true;
                                var e = new NotSslRecordException(
                                    "not an SSL/TLS record: " + ByteBufferUtil.HexDump(input));
                                input.SkipBytes(input.ReadableBytes);

                                TlsUtils.NotifyHandshakeFailure(context, e);
                                throw e;
                            }
                            if (len == TlsUtils.NOT_ENOUGH_DATA ||
                                writerIndex - readerIndex - TlsUtils.SSL_RECORD_HEADER_LENGTH < len)
                            {
                                // Not enough data
                                return;
                            }

                            // increase readerIndex and try again.
                            input.SkipBytes(len);
                            continue;

                        case TlsUtils.SSL_CONTENT_TYPE_HANDSHAKE:
                            int majorVersion = input.GetByte(readerIndex + 1);

                            // SSLv3 or TLS
                            if (majorVersion == 3)
                            {
                                int packetLength = input.GetUnsignedShort(readerIndex + 3) + TlsUtils.SSL_RECORD_HEADER_LENGTH;

                                if (readableBytes < packetLength)
                                {
                                    // client hello incomplete; try again to decode once more data is ready.
                                    return;
                                }

                                // See https://tools.ietf.org/html/rfc5246#section-7.4.1.2
                                //
                                // Decode the ssl client hello packet.
                                // We have to skip bytes until SessionID (which sum to 43 bytes).
                                //
                                // struct {
                                //    ProtocolVersion client_version;
                                //    Random random;
                                //    SessionID session_id;
                                //    CipherSuite cipher_suites<2..2^16-2>;
                                //    CompressionMethod compression_methods<1..2^8-1>;
                                //    select (extensions_present) {
                                //        case false:
                                //            struct {};
                                //        case true:
                                //            Extension extensions<0..2^16-1>;
                                //    };
                                // } ClientHello;
                                //

                                int endOffset = readerIndex + packetLength;
                                int offset    = readerIndex + 43;

                                if (endOffset - offset < 6)
                                {
                                    continueLoop = false;
                                    break;
                                }

                                int sessionIdLength = input.GetByte(offset);
                                offset += sessionIdLength + 1;

                                int cipherSuitesLength = input.GetUnsignedShort(offset);
                                offset += cipherSuitesLength + 2;

                                int compressionMethodLength = input.GetByte(offset);
                                offset += compressionMethodLength + 1;

                                int extensionsLength = input.GetUnsignedShort(offset);
                                offset += 2;
                                int extensionsLimit = offset + extensionsLength;

                                if (extensionsLimit > endOffset)
                                {
                                    // Extensions should never exceed the record boundary.
                                    continueLoop = false;
                                    break;
                                }

                                for (;;)
                                {
                                    if (extensionsLimit - offset < 4)
                                    {
                                        continueLoop = false;
                                        break;
                                    }

                                    int extensionType = input.GetUnsignedShort(offset);
                                    offset += 2;

                                    int extensionLength = input.GetUnsignedShort(offset);
                                    offset += 2;

                                    if (extensionsLimit - offset < extensionLength)
                                    {
                                        continueLoop = false;
                                        break;
                                    }

                                    // SNI
                                    // See https://tools.ietf.org/html/rfc6066#page-6
                                    if (extensionType == 0)
                                    {
                                        offset += 2;
                                        if (extensionsLimit - offset < 3)
                                        {
                                            continueLoop = false;
                                            break;
                                        }

                                        int serverNameType = input.GetByte(offset);
                                        offset++;

                                        if (serverNameType == 0)
                                        {
                                            int serverNameLength = input.GetUnsignedShort(offset);
                                            offset += 2;

                                            if (serverNameLength <= 0 || extensionsLimit - offset < serverNameLength)
                                            {
                                                continueLoop = false;
                                                break;
                                            }

                                            string hostname = input.ToString(offset, serverNameLength, Encoding.UTF8);
                                            //try
                                            //{
                                            //    select(ctx, IDN.toASCII(hostname,
                                            //                            IDN.ALLOW_UNASSIGNED).toLowerCase(Locale.US));
                                            //}
                                            //catch (Throwable t)
                                            //{
                                            //    PlatformDependent.throwException(t);
                                            //}

                                            var idn = new IdnMapping()
                                            {
                                                AllowUnassigned = true
                                            };

                                            hostname = idn.GetAscii(hostname);
#if NETSTANDARD1_3
                                            // TODO: netcore does not have culture sensitive tolower()
                                            hostname = hostname.ToLowerInvariant();
#else
                                            hostname = hostname.ToLower(new CultureInfo("en-US"));
#endif
                                            this.Select(context, hostname);
                                            return;
                                        }
                                        else
                                        {
                                            // invalid enum value
                                            continueLoop = false;
                                            break;
                                        }
                                    }

                                    offset += extensionLength;
                                }
                            }

                            break;

                        // Fall-through
                        default:
                            //not tls, ssl or application data, do not try sni
                            continueLoop = false;
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    error = e;

                    // unexpected encoding, ignore sni and use default
                    //if (//Logger.DebugEnabled)
                    //{
                    //    //Logger.Warn($"Unexpected client hello packet: {ByteBufferUtil.HexDump(input)}", e);
                    //}
                }

                if (this.serverTlsSniSettings.DefaultServerHostName != null)
                {
                    // Just select the default server TLS setting
                    this.Select(context, this.serverTlsSniSettings.DefaultServerHostName);
                }
                else
                {
                    this.handshakeFailed = true;
                    var e = new DecoderException($"failed to get the server TLS setting {error}");
                    TlsUtils.NotifyHandshakeFailure(context, e);
                    throw e;
                }
            }
        }
Esempio n. 8
0
        /// <summary>
        ///     Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
        ///     the readerIndex of the given <see cref="IByteBuffer"/>.
        /// </summary>
        /// <param name="buffer">
        ///     The <see cref="IByteBuffer"/> to read from. Be aware that it must have at least
        ///     <see cref="SSL_RECORD_HEADER_LENGTH"/> bytes to read,
        ///     otherwise it will throw an <see cref="ArgumentException"/>.
        /// </param>
        /// <param name="offset">Offset to record start.</param>
        /// <returns>
        ///     The length of the encrypted packet that is included in the buffer. This will
        ///     return <c>-1</c> if the given <see cref="IByteBuffer"/> is not encrypted at all.
        /// </returns>
        public static int GetEncryptedPacketLength(IByteBuffer buffer, int offset)
        {
            int packetLength = 0;

            // SSLv3 or TLS - Check ContentType
            bool tls;

            switch (buffer.GetByte(offset))
            {
            case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC:
            case SSL_CONTENT_TYPE_ALERT:
            case SSL_CONTENT_TYPE_HANDSHAKE:
            case SSL_CONTENT_TYPE_APPLICATION_DATA:
            case SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT:
                tls = true;
                break;

            default:
                // SSLv2 or bad data
                tls = false;
                break;
            }

            if (tls)
            {
                // SSLv3 or TLS - Check ProtocolVersion
                int majorVersion = buffer.GetByte(offset + 1);
                if (majorVersion == 3)
                {
                    // SSLv3 or TLS
                    packetLength = buffer.GetUnsignedShort(offset + 3) + SSL_RECORD_HEADER_LENGTH;
                    if ((uint)packetLength <= SSL_RECORD_HEADER_LENGTH)
                    {
                        // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
                        tls = false;
                    }
                }
                else
                {
                    // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
                    tls = false;
                }
            }

            if (!tls)
            {
                // SSLv2 or bad data - Check the version
                uint uHeaderLength = (buffer.GetByte(offset) & 0x80) != 0 ? 2u : 3u;
                uint uMajorVersion = buffer.GetByte(offset + (int)uHeaderLength + 1);
                if (uMajorVersion == 2u || uMajorVersion == 3u)
                {
                    // SSLv2
                    packetLength = uHeaderLength == 2u ?
                                   (buffer.GetShort(offset) & 0x7FFF) + 2 : (buffer.GetShort(offset) & 0x3FFF) + 3;
                    if (uHeaderLength >= (uint)packetLength)
                    {
                        return(NOT_ENOUGH_DATA);
                    }
                }
                else
                {
                    return(NOT_ENCRYPTED);
                }
            }

            return(packetLength);
        }
Esempio n. 9
0
        protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List <object> output)
        {
            if (!_suppressRead && !_handshakeFailed)
            {
                Exception error = null;
                try
                {
                    int readerIndex     = input.ReaderIndex;
                    int readableBytes   = input.ReadableBytes;
                    int handshakeLength = -1;

                    // Check if we have enough data to determine the record type and length.
                    while (readableBytes >= TlsUtils.SSL_RECORD_HEADER_LENGTH)
                    {
                        int contentType = input.GetByte(readerIndex);
                        // tls, but not handshake command
                        switch (contentType)
                        {
                        case TlsUtils.SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC:
                        // fall-through
                        case TlsUtils.SSL_CONTENT_TYPE_ALERT:
                            int len = TlsUtils.GetEncryptedPacketLength(input, readerIndex);

                            // Not an SSL/TLS packet
                            if (len == TlsUtils.NOT_ENCRYPTED)
                            {
                                _handshakeFailed = true;
                                var e = new NotSslRecordException(
                                    "not an SSL/TLS record: " + ByteBufferUtil.HexDump(input));
                                _ = input.SkipBytes(input.ReadableBytes);
                                _ = context.FireUserEventTriggered(new SniCompletionEvent(e));
                                TlsUtils.NotifyHandshakeFailure(context, e, true);
                                throw e;
                            }
                            if (len == TlsUtils.NOT_ENOUGH_DATA)
                            {
                                // Not enough data
                                return;
                            }
                            // SNI can't be present in an ALERT or CHANGE_CIPHER_SPEC record, so we'll fall back and
                            // assume no SNI is present. Let's let the actual TLS implementation sort this out.
                            // Just select the default SslContext
                            goto SelectDefault;

                        case TlsUtils.SSL_CONTENT_TYPE_HANDSHAKE:
                            int majorVersion = input.GetByte(readerIndex + 1);

                            // SSLv3 or TLS
                            if (majorVersion == 3)
                            {
                                int packetLength = input.GetUnsignedShort(readerIndex + 3) + TlsUtils.SSL_RECORD_HEADER_LENGTH;

                                if (readableBytes < packetLength)
                                {
                                    // client hello incomplete; try again to decode once more data is ready.
                                    return;
                                }
                                else if (packetLength == TlsUtils.SSL_RECORD_HEADER_LENGTH)
                                {
                                    goto SelectDefault;
                                }


                                int endOffset = readerIndex + packetLength;

                                // Let's check if we already parsed the handshake length or not.
                                if (handshakeLength == -1)
                                {
                                    if (readerIndex + 4 > endOffset)
                                    {
                                        // Need more data to read HandshakeType and handshakeLength (4 bytes)
                                        return;
                                    }

                                    int handshakeType = input.GetByte(readerIndex + TlsUtils.SSL_RECORD_HEADER_LENGTH);

                                    // Check if this is a clientHello(1)
                                    // See https://tools.ietf.org/html/rfc5246#section-7.4
                                    if (handshakeType != 1)
                                    {
                                        goto SelectDefault;
                                    }

                                    // Read the length of the handshake as it may arrive in fragments
                                    // See https://tools.ietf.org/html/rfc5246#section-7.4
                                    handshakeLength = input.GetUnsignedMedium(readerIndex + TlsUtils.SSL_RECORD_HEADER_LENGTH + 1);

                                    // Consume handshakeType and handshakeLength (this sums up as 4 bytes)
                                    readerIndex  += 4;
                                    packetLength -= 4;

                                    if (handshakeLength + 4 + TlsUtils.SSL_RECORD_HEADER_LENGTH <= packetLength)
                                    {
                                        // We have everything we need in one packet.
                                        // Skip the record header
                                        readerIndex += TlsUtils.SSL_RECORD_HEADER_LENGTH;
                                        Select(context, ExtractSniHostname(input, readerIndex, readerIndex + handshakeLength));
                                        return;
                                    }
                                    else
                                    {
                                        if (_handshakeBuffer is null)
                                        {
                                            _handshakeBuffer = context.Allocator.Buffer(handshakeLength);
                                        }
                                        else
                                        {
                                            // Clear the buffer so we can aggregate into it again.
                                            _ = _handshakeBuffer.Clear();
                                        }
                                    }
                                }

                                // Combine the encapsulated data in one buffer but not include the SSL_RECORD_HEADER
                                _ = _handshakeBuffer.WriteBytes(input, readerIndex + TlsUtils.SSL_RECORD_HEADER_LENGTH,
                                                                packetLength - TlsUtils.SSL_RECORD_HEADER_LENGTH);
                                readerIndex   += packetLength;
                                readableBytes -= packetLength;
                                if (handshakeLength <= _handshakeBuffer.ReadableBytes)
                                {
                                    Select(context, ExtractSniHostname(_handshakeBuffer, 0, handshakeLength));
                                    return;
                                }
                            }
                            break;

                        default:
                            //not tls, ssl or application data, do not try sni
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    error = e;

                    // unexpected encoding, ignore sni and use default
                    if (Logger.WarnEnabled)
                    {
                        Logger.UnexpectedClientHelloPacket(input, e);
                    }
                }

SelectDefault:
                if (_serverTlsSniSettings.DefaultServerHostName is object)
                {
                    // Just select the default server TLS setting
                    Select(context, _serverTlsSniSettings.DefaultServerHostName);
                }
                else
                {
                    ReleaseHandshakeBuffer();

                    _handshakeFailed = true;
                    var e = new DecoderException($"failed to get the server TLS setting {error}");
                    TlsUtils.NotifyHandshakeFailure(context, e, true);
                    throw e;
                }
            }
        }
Esempio n. 10
0
        private static string ExtractSniHostname(IByteBuffer input, int offset, int endOffset)
        {
            // See https://tools.ietf.org/html/rfc5246#section-7.4.1.2
            //
            // Decode the ssl client hello packet.
            //
            // struct {
            //    ProtocolVersion client_version;
            //    Random random;
            //    SessionID session_id;
            //    CipherSuite cipher_suites<2..2^16-2>;
            //    CompressionMethod compression_methods<1..2^8-1>;
            //    select (extensions_present) {
            //        case false:
            //            struct {};
            //        case true:
            //            Extension extensions<0..2^16-1>;
            //    };
            // } ClientHello;
            //

            // We have to skip bytes until SessionID (which sum to 34 bytes in this case).
            offset += 34;

            if (endOffset - offset >= 6)
            {
                int sessionIdLength = input.GetByte(offset);
                offset += sessionIdLength + 1;

                int cipherSuitesLength = input.GetUnsignedShort(offset);
                offset += cipherSuitesLength + 2;

                int compressionMethodLength = input.GetByte(offset);
                offset += compressionMethodLength + 1;

                int extensionsLength = input.GetUnsignedShort(offset);
                offset += 2;
                int extensionsLimit = offset + extensionsLength;

                // Extensions should never exceed the record boundary.
                if (extensionsLimit <= endOffset)
                {
                    while (extensionsLimit - offset >= 4)
                    {
                        int extensionType = input.GetUnsignedShort(offset);
                        offset += 2;

                        int extensionLength = input.GetUnsignedShort(offset);
                        offset += 2;

                        if (extensionsLimit - offset < extensionLength)
                        {
                            break;
                        }

                        // SNI
                        // See https://tools.ietf.org/html/rfc6066#page-6
                        if (0u >= (uint)extensionType)
                        {
                            offset += 2;
                            if (extensionsLimit - offset < 3)
                            {
                                break;
                            }

                            int serverNameType = input.GetByte(offset);
                            offset++;

                            if (0u >= (uint)serverNameType)
                            {
                                int serverNameLength = input.GetUnsignedShort(offset);
                                offset += 2;

                                if ((uint)(serverNameLength - 1) > SharedConstants.TooBigOrNegative /*serverNameLength <= 0*/ ||
                                    extensionsLimit - offset < serverNameLength)
                                {
                                    break;
                                }

                                string hostname = input.ToString(offset, serverNameLength, Encoding.UTF8);
                                var    idn      = new IdnMapping()
                                {
                                    AllowUnassigned = true
                                };
                                return(idn.GetAscii(hostname).ToLower(UnitedStatesCultureInfo));
                            }
                            else
                            {
                                // invalid enum value
                                break;
                            }
                        }

                        offset += extensionLength;
                    }
                }
            }
            return(null);
        }
Esempio n. 11
0
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            while (true)
            {
                if (_isWebSocket)
                {
                    IByteBuffer buffer;
                    _lastReadBuffer?.ResetReaderIndex();
                    if (_lastReadBuffer != null && _lastReadBuffer.ReadableBytes != 0)
                    {
                        buffer = ByteBufferUtil.DefaultAllocator.HeapBuffer(
                            _lastReadBuffer.ReadableBytes + ((IByteBuffer)message).ReadableBytes);
                        buffer.WriteBytes(_lastReadBuffer);
                        buffer.WriteBytes((IByteBuffer)message);
                        _lastReadBuffer = buffer;
                    }
                    else
                    {
                        buffer          = (IByteBuffer)message;
                        _lastReadBuffer = buffer;
                    }

                    if (buffer.ReadableBytes < 2)
                    {
                        return;
                    }

                    IByteBuffer bufferCopy = ByteBufferUtil.DefaultAllocator.HeapBuffer(buffer.Capacity);
                    buffer.ReadBytes(bufferCopy, 2);
                    if ((bufferCopy.GetByte(0) & 8) == 8)
                    {
                        //操作码位8表示断开连接
                        context.CloseAsync();

                        return;
                    }

                    byte[] maskKey = { 0, 0, 0, 0 };
                    bool   masked  = false;
                    byte   lenMark = bufferCopy.GetByte(1);
                    if (lenMark >= 128)
                    {
                        masked   = true;
                        lenMark -= 128;
                    }

                    int offset = 0;
                    int len    = 0;
                    if (lenMark <= 125)
                    {
                        offset = 2;
                        len    = lenMark;
                    }
                    else if (lenMark == 126)
                    {
                        offset = 4;

                        if (buffer.ReadableBytes < 2)
                        {
                            return;
                        }

                        buffer.ReadBytes(bufferCopy, 2);
                        len = bufferCopy.GetUnsignedShort(2);
                    }
                    else if (lenMark == 127)
                    {
                        offset = 10;

                        if (buffer.ReadableBytes < 8)
                        {
                            return;
                        }

                        buffer.ReadBytes(bufferCopy, 8);
                        len = (int)bufferCopy.GetLong(2);
                    }

                    if (masked)
                    {
                        if (buffer.ReadableBytes < 4)
                        {
                            return;
                        }

                        buffer.ReadBytes(bufferCopy, 4);
                        for (int i = 0; i < 4; i++)
                        {
                            maskKey[i] = bufferCopy.GetByte(offset + i);
                        }

                        offset += 4;
                    }

                    if (buffer.ReadableBytes < len)
                    {
                        return;
                    }

                    buffer.ReadBytes(bufferCopy, len);
                    IByteBuffer output = ByteBufferUtil.DefaultAllocator.HeapBuffer(len);
                    for (int i = 0; i < len; i++)
                    {
                        output.WriteByte(bufferCopy.GetByte(offset + i) ^ maskKey[i % 4]);
                    }

                    _lastReadBuffer.MarkReaderIndex();
                    base.ChannelRead(context, output);

                    if (_lastReadBuffer.ReadableBytes <= 0)
                    {
                        return;
                    }

                    _lastReadBuffer = null;
                    message         = buffer;

                    continue;
                }

                try
                {
                    var         buffer     = (IByteBuffer)message;
                    IByteBuffer bufferCopy = buffer.Copy();
                    var         bytes      = new byte[bufferCopy.ReadableBytes];
                    bufferCopy.ReadBytes(bytes);
                    string       data = Encoding.ASCII.GetString(bytes);
                    const string requestWebSocketMark = "Sec-WebSocket-Key:";
                    int          index = data.IndexOf(requestWebSocketMark, StringComparison.Ordinal);
                    if (index < 0)
                    {
                        throw new Exception();
                    }

                    data = data.Substring(index + requestWebSocketMark.Length + 1);
                    var key = new StringBuilder();
                    foreach (char t in data)
                    {
                        if (IsBase64Char(t))
                        {
                            key.Append(t);
                        }
                        else
                        {
                            break;
                        }
                    }

                    key.Append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
                    SHA1 sha1 = new SHA1CryptoServiceProvider();
                    data = Convert.ToBase64String(sha1.ComputeHash(Encoding.UTF8.GetBytes(key.ToString())));
                    sha1.Dispose();
                    StringBuilder ret = new StringBuilder();
                    ret.Append("HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ");
                    ret.Append(data);
                    ret.Append("\r\nUpgrade: websocket\r\n\r\n");
                    IByteBuffer output = ByteBufferUtil.DefaultAllocator.HeapBuffer();
                    output.WriteBytes(Encoding.UTF8.GetBytes(ret.ToString()));
                    context.WriteAndFlushAsync(output);

                    _isWebSocket = true;
                }
                catch
                {
                    base.ChannelRead(context, message);
                }
                finally
                {
                    if (_isWebSocket)
                    {
                        context.Channel.Pipeline.Remove <LengthFieldBasedFrameDecoder>();
                        context.Channel.Pipeline.Remove <LengthFieldPrepender>();
                    }
                    else
                    {
                        context.Channel.Pipeline.Remove(this);
                    }
                }

                break;
            }
        }