/// <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), });
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)); } }
/// <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); }
/// <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); }
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; }
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; } } }
/// <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); }
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; } } }
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); }
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; } }