protected override int ProcessIncoming(NetContext context, Connection connection, Stream incomingBuffer) { int len = FindHeadersLength(incomingBuffer); if (len <= 0) return 0; incomingBuffer.Position = 0; int extraConsumed; if (len < NetContext.BufferSize) { byte[] buffer = null; try { buffer = context.GetBuffer(); NetContext.Fill(incomingBuffer, buffer, len); using (var ms = new MemoryStream(buffer, 0, len)) { extraConsumed = ProcessHeadersAndUpgrade(context, connection, ms, incomingBuffer, len); } } finally { context.Recycle(buffer); } } else { using (var ss = new SubStream(incomingBuffer, len)) { extraConsumed = ProcessHeadersAndUpgrade(context, connection, ss, incomingBuffer, len); } } if (extraConsumed < 0) return 0; // means: wasn't usable (yet) return len + extraConsumed; }
} // doesn't need to be 100% accurate void IFrame.Write(NetContext context, Connection connection, Stream stream) { //Debug.WriteLine("write:" + this); byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = 0; buffer[offset++] = (byte)(((int)flags & 240) | ((int)OpCode & 15)); if (PayloadLength > ushort.MaxValue) { // write as a 64-bit length buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127); buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = (byte)(PayloadLength >> 24); buffer[offset++] = (byte)(PayloadLength >> 16); buffer[offset++] = (byte)(PayloadLength >> 8); buffer[offset++] = (byte)(PayloadLength); } else if (PayloadLength > 125) { // write as a 16-bit length buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126); buffer[offset++] = (byte)(PayloadLength >> 8); buffer[offset++] = (byte)(PayloadLength); } else { // write in the header buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength); } if (Mask.HasValue) { int mask = Mask.Value; buffer[offset++] = (byte)(mask >> 24); buffer[offset++] = (byte)(mask >> 16); buffer[offset++] = (byte)(mask >> 8); buffer[offset++] = (byte)(mask); } stream.Write(buffer, 0, offset); if (PayloadLength != 0) { int totalRead = WebSocketsProcessor_RFC6455_13.Copy(Payload, stream, buffer, Mask, PayloadLength); if (totalRead != PayloadLength) { throw new EndOfStreamException("Wrong payload length sent"); } } } finally { context.Recycle(buffer); } }
protected override int ProcessIncoming(NetContext context, Connection connection, Stream incomingBuffer) { int len = FindHeadersLength(incomingBuffer); if (len <= 0) { return(0); } incomingBuffer.Position = 0; int extraConsumed; if (len < NetContext.BufferSize) { byte[] buffer = null; try { buffer = context.GetBuffer(); NetContext.Fill(incomingBuffer, buffer, len); using (var ms = new MemoryStream(buffer, 0, len)) { extraConsumed = ProcessHeadersAndUpgrade(context, connection, ms, incomingBuffer, len); } } finally { context.Recycle(buffer); } } else { using (var ss = new SubStream(incomingBuffer, len)) { extraConsumed = ProcessHeadersAndUpgrade(context, connection, ss, incomingBuffer, len); } } if (extraConsumed < 0) { return(0); // means: wasn't usable (yet) } return(len + extraConsumed); }
internal static string ComputeReply(NetContext context, string webSocketKey) { //To prove that the handshake was received, the server has to take two //pieces of information and combine them to form a response. The first //piece of information comes from the |Sec-WebSocket-Key| header field //in the client handshake: // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== //For this header field, the server has to take the value (as present //in the header field, e.g., the base64-encoded [RFC4648] version minus //any leading and trailing whitespace) and concatenate this with the //Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA- //95CA-C5AB0DC85B11" in string form, which is unlikely to be used by //network endpoints that do not understand the WebSocket Protocol. A //SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of //[RFC4648]), of this concatenation is then returned in the server's //handshake. if (string.IsNullOrEmpty(webSocketKey) || webSocketKey.Length != 24) { throw new ArgumentException("webSocketKey"); } const string WebSocketKeySuffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = Encoding.ASCII.GetBytes(webSocketKey, 0, webSocketKey.Length, buffer, 0); int len = offset + Encoding.ASCII.GetBytes(WebSocketKeySuffix, 0, WebSocketKeySuffix.Length, buffer, offset); using (var sha = SHA1.Create()) { return(Convert.ToBase64String(sha.ComputeHash(buffer, 0, len))); } } finally { context.Recycle(buffer); } }
protected override int ProcessIncoming(NetContext context, Connection connection, Stream incoming) { if (incoming.Length < 2) return 0; // can't read that; frame takes at minimum two bytes byte[] buffer = null; try { buffer = context.GetBuffer(); int read; // read as much as possible up to 14 bytes; that gives us space for 2 bytes frame header, 8 bytes length, and 4 bytes mask read = NetContext.TryFill(incoming, buffer, 14); int headerLength; var frame = WebSocketsFrame.TryParseFrameHeader(buffer, read, out headerLength); if (frame == null) return 0; int payloadLen = frame.PayloadLength; #if VERBOSE Debug.WriteLine("Parsed header from: " + BitConverter.ToString(buffer, 0, headerLength)); #endif if (incoming.Length < headerLength + payloadLen) return 0; // not enough data to read the payload if (payloadLen != 0) { Stream payload = null; try { payload = new BufferStream(context, context.Handler.MaxIncomingQuota); if (read != headerLength) incoming.Position -= (read - headerLength); // we got it wrong... Copy(incoming, payload, buffer, frame.Mask, payloadLen); if (payloadLen != payload.Length) { throw new InvalidOperationException("I munged the data"); } frame.Payload = payload; payload = null; } finally { if (payload != null) payload.Dispose(); } } foreach (var final in ApplyExtensions(context, connection, frame, true)) { ProcessFrame(context, connection, final); } return headerLength + payloadLen; } finally { context.Recycle(buffer); } }
internal static string ComputeReply(NetContext context, string webSocketKey) { //To prove that the handshake was received, the server has to take two //pieces of information and combine them to form a response. The first //piece of information comes from the |Sec-WebSocket-Key| header field //in the client handshake: // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== //For this header field, the server has to take the value (as present //in the header field, e.g., the base64-encoded [RFC4648] version minus //any leading and trailing whitespace) and concatenate this with the //Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA- //95CA-C5AB0DC85B11" in string form, which is unlikely to be used by //network endpoints that do not understand the WebSocket Protocol. A //SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of //[RFC4648]), of this concatenation is then returned in the server's //handshake. if (string.IsNullOrEmpty(webSocketKey) || webSocketKey.Length != 24) throw new ArgumentException("webSocketKey"); const string WebSocketKeySuffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = Encoding.ASCII.GetBytes(webSocketKey, 0, webSocketKey.Length, buffer, 0); int len = offset + Encoding.ASCII.GetBytes(WebSocketKeySuffix, 0, WebSocketKeySuffix.Length, buffer, offset); using(var sha = SHA1.Create()) { return Convert.ToBase64String(sha.ComputeHash(buffer, 0, len)); } } finally { context.Recycle(buffer); } }
private void Process(NetContext context, Connection connection, Stream stream, WebSocketsFrame.OpCodes opCode) { using (stream) { switch (opCode) { case WebSocketsFrame.OpCodes.Text: string s; if (stream == null || stream.Length == 0) s = ""; else { stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { s = reader.ReadToEnd(); } } context.Handler.OnReceived(connection, s); break; case WebSocketsFrame.OpCodes.Binary: byte[] blob; int len; if (stream == null || (len = ((int)stream.Length - (int)stream.Position)) == 0) blob = new byte[0]; else { blob = new byte[len]; byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = 0, read; stream.Position = 0; while((read = stream.Read(buffer, offset, Math.Min(len, buffer.Length))) > 0) { offset += read; len -= read; } if(len != 0) throw new EndOfStreamException(); } finally { context.Recycle(buffer); } } context.Handler.OnReceived(connection, blob); break; default: throw new InvalidOperationException(); } } }
private void ProcessFrame(NetContext context, Connection connection, WebSocketsFrame frame) { if(IsClient) { if(frame.Mask.HasValue) throw new InvalidOperationException("Clients only expect unmasked frames"); } else { if (!frame.Mask.HasValue) throw new InvalidOperationException("Servers only expect masked frames"); } var ws = (WebSocketConnection) connection; if(frame.IsControlFrame) { if(!frame.IsFinal) throw new InvalidOperationException("control frames cannot be fragmented"); switch(frame.OpCode) { case WebSocketsFrame.OpCodes.Close: // respond with a closing handshake GracefulShutdown(context, connection); break; case WebSocketsFrame.OpCodes.Pong: // could add some reaction here break; case WebSocketsFrame.OpCodes.Ping: // send a "pong" with the same payload var pong = new WebSocketsFrame(); if (IsClient) pong.Mask = CreateMask(); pong.OpCode = WebSocketsFrame.OpCodes.Binary; pong.PayloadLength = frame.PayloadLength; if(frame.Payload != null) { var ms = new MemoryStream((int)frame.Payload.Length); byte[] buffer = null; try { buffer = context.GetBuffer(); Copy(frame.Payload, ms, buffer, null, frame.PayloadLength); } finally { context.Recycle(buffer); } pong.Payload = ms; } SendControl(context, pong); connection.PromptToSend(context); break; default: throw new InvalidOperationException(); } } else { if (!ws.AllowIncomingDataFrames) throw new InvalidOperationException("Data frames disabled"); int pendingCount = FrameCount; if (frame.OpCode == WebSocketsFrame.OpCodes.Continuation) { if(pendingCount == 0) throw new InvalidOperationException("Continuation of nothing"); } else if (pendingCount != 0) { throw new InvalidOperationException("Should be a continuation"); } if (!frame.IsFinal) { Push(context, frame); } else { if(pendingCount == 0) { Process(context, connection, frame.Payload, frame.OpCode); } else { throw new NotImplementedException(); } } } if (frame.OpCode == WebSocketsFrame.OpCodes.Close) { Debug.WriteLine("Sent close frame: " + connection); } }
protected override int ProcessIncoming(NetContext context, Connection connection, System.IO.Stream incoming) { if (incoming.Length < 2) return 0; // can't read that; start/end markers take at least 2 bytes switch(incoming.ReadByte()) { case 0x00: var ws = (WebSocketConnection) connection; if(!ws.AllowIncomingDataFrames) throw new InvalidOperationException("Data frames disabled"); // data frame is 0x00 [data] 0xFF int len = 0, cur; while((cur = incoming.ReadByte()) != 0xFF && cur >= 0) { len++; } if(cur < 0) throw new EndOfStreamException(); incoming.Position = 1; string value; if (len == 0) value = ""; else if (len <= NetContext.BufferSize) { byte[] buffer = null; try { buffer = context.GetBuffer(); NetContext.Fill(incoming, buffer, len); value = Encoding.UTF8.GetString(buffer, 0, len); } finally { context.Recycle(buffer); } } else { var buffer = new byte[len]; NetContext.Fill(incoming, buffer, len); value = Encoding.UTF8.GetString(buffer, 0, len); } context.Handler.OnReceived(connection, value); return len + 2; case 0xFF: // shutdown is 0xFF 0x00 if(incoming.ReadByte() == 0x00) { GracefulShutdown(context, connection); return 2; } else { throw new InvalidOperationException("protocol fail"); } default: throw new InvalidOperationException("protocol fail"); } }
IEnumerable <WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes) { if (frame.Reserved1) { throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?"); } int headerBytes = 0; if (outbound == null) { outbound = new ZStream(); const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15) outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS); headerBytes = 2; } BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { inBuffer = context.GetBuffer(); outBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); outbound.next_out = outBuffer; outbound.next_in = inBuffer; int remaining = frame.PayloadLength; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) { break; } remaining -= readCount; outbound.next_in_index = 0; outbound.avail_in = readCount; do { outbound.next_out_index = 0; outbound.avail_out = outBuffer.Length; long priorOut = outbound.total_out; int err = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH); if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END) { throw new ZStreamException("deflating: " + outbound.msg); } int outCount = (int)(outbound.total_out - priorOut); if (outCount > 0) { if (headerBytes == 0) { tmp.Write(outBuffer, 0, outCount); } else { if (outCount < headerBytes) { throw new InvalidOperationException("Failed to write entire header"); } // check the generated header meets our expectations // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window) if ((outBuffer[0] & 15) != 8) { throw new InvalidOperationException("Zlib CM header was incorrect"); } if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid { throw new InvalidOperationException("Zlib CINFO header was incorrect"); } // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary if ((outBuffer[1] & 32) != 0) { throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)"); } // skip the header, and write anything else outCount -= headerBytes; if (outCount > 0) { tmp.Write(outBuffer, headerBytes, outCount); } headerBytes = 0; // all written now } } } while (outbound.avail_in > 0 || outbound.avail_out == 0); } if (remaining != 0) { throw new EndOfStreamException(); } if (headerBytes != 0) { throw new InvalidOperationException("Zlib header was not written"); } // verify the last 4 bytes, then drop them tmp.Position = tmp.Length - 4; NetContext.Fill(tmp, outBuffer, 4); if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF)) { throw new InvalidOperationException("expectation failed: 0000FFFF in the tail"); } if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength) { // compressing it didn't do anything useful; since we're going to discard // the compression context, we might as well stick with the original data payload.Position = 0; payload = null; // so that it doesn't get disposed } else { // set our final output tmp.Position = 0; tmp.SetLength(tmp.Length - 4); long bytesSaved = frame.PayloadLength - tmp.Length; frame.Payload = tmp; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = true; tmp = payload as BufferStream; parent.RegisterOutboundBytesSaved(bytesSaved); } } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (outbound != null) { outbound.next_out = null; outbound.next_in = null; } if (tmp != null) { tmp.Dispose(); } if (inBuffer != null) { context.Recycle(inBuffer); } if (outBuffer != null) { context.Recycle(outBuffer); } if (parent.disableContextTakeover) { ClearContext(false, true); } } } yield return(frame); }
private void Process(NetContext context, Connection connection, Stream stream, WebSocketsFrame.OpCodes opCode) { using (stream) { switch (opCode) { case WebSocketsFrame.OpCodes.Text: string s; if (stream == null || stream.Length == 0) { s = ""; } else { stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { s = reader.ReadToEnd(); } } context.Handler.OnReceived(connection, s); break; case WebSocketsFrame.OpCodes.Binary: byte[] blob; int len; if (stream == null || (len = ((int)stream.Length - (int)stream.Position)) == 0) { blob = new byte[0]; } else { blob = new byte[len]; byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = 0, read; stream.Position = 0; while ((read = stream.Read(buffer, offset, Math.Min(len, buffer.Length))) > 0) { offset += read; len -= read; } if (len != 0) { throw new EndOfStreamException(); } } finally { context.Recycle(buffer); } } context.Handler.OnReceived(connection, blob); break; default: throw new InvalidOperationException(); } } }
private void ProcessFrame(NetContext context, Connection connection, WebSocketsFrame frame) { if (IsClient) { if (frame.Mask.HasValue) { throw new InvalidOperationException("Clients only expect unmasked frames"); } } else { if (!frame.Mask.HasValue) { throw new InvalidOperationException("Servers only expect masked frames"); } } var ws = (WebSocketConnection)connection; if (frame.IsControlFrame) { if (!frame.IsFinal) { throw new InvalidOperationException("control frames cannot be fragmented"); } switch (frame.OpCode) { case WebSocketsFrame.OpCodes.Close: // respond with a closing handshake GracefulShutdown(context, connection); break; case WebSocketsFrame.OpCodes.Pong: // could add some reaction here break; case WebSocketsFrame.OpCodes.Ping: // send a "pong" with the same payload var pong = new WebSocketsFrame(); if (IsClient) { pong.Mask = CreateMask(); } pong.OpCode = WebSocketsFrame.OpCodes.Binary; pong.PayloadLength = frame.PayloadLength; if (frame.Payload != null) { var ms = new MemoryStream((int)frame.Payload.Length); byte[] buffer = null; try { buffer = context.GetBuffer(); Copy(frame.Payload, ms, buffer, null, frame.PayloadLength); } finally { context.Recycle(buffer); } pong.Payload = ms; } SendControl(context, pong); connection.PromptToSend(context); break; default: throw new InvalidOperationException(); } } else { if (!ws.AllowIncomingDataFrames) { throw new InvalidOperationException("Data frames disabled"); } int pendingCount = FrameCount; if (frame.OpCode == WebSocketsFrame.OpCodes.Continuation) { if (pendingCount == 0) { throw new InvalidOperationException("Continuation of nothing"); } } else if (pendingCount != 0) { throw new InvalidOperationException("Should be a continuation"); } if (!frame.IsFinal) { Push(context, frame); } else { if (pendingCount == 0) { Process(context, connection, frame.Payload, frame.OpCode); } else { throw new NotImplementedException(); } } } if (frame.OpCode == WebSocketsFrame.OpCodes.Close) { Debug.WriteLine("Sent close frame: " + connection); } }
protected override int ProcessIncoming(NetContext context, Connection connection, Stream incoming) { if (incoming.Length < 2) { return(0); // can't read that; frame takes at minimum two bytes } byte[] buffer = null; try { buffer = context.GetBuffer(); int read; // read as much as possible up to 14 bytes; that gives us space for 2 bytes frame header, 8 bytes length, and 4 bytes mask read = NetContext.TryFill(incoming, buffer, 14); int headerLength; var frame = WebSocketsFrame.TryParseFrameHeader(buffer, read, out headerLength); if (frame == null) { return(0); } int payloadLen = frame.PayloadLength; #if VERBOSE Debug.WriteLine("Parsed header from: " + BitConverter.ToString(buffer, 0, headerLength)); #endif if (incoming.Length < headerLength + payloadLen) { return(0); // not enough data to read the payload } if (payloadLen != 0) { Stream payload = null; try { payload = new BufferStream(context, context.Handler.MaxIncomingQuota); if (read != headerLength) { incoming.Position -= (read - headerLength); // we got it wrong... } Copy(incoming, payload, buffer, frame.Mask, payloadLen); if (payloadLen != payload.Length) { throw new InvalidOperationException("I munged the data"); } frame.Payload = payload; payload = null; } finally { if (payload != null) { payload.Dispose(); } } } foreach (var final in ApplyExtensions(context, connection, frame, true)) { ProcessFrame(context, connection, final); } return(headerLength + payloadLen); } finally { context.Recycle(buffer); } }
internal int GetLengthEstimate() { return PayloadLength + 2; } // doesn't need to be 100% accurate void IFrame.Write(NetContext context, Connection connection, Stream stream) { //Debug.WriteLine("write:" + this); byte[] buffer = null; try { buffer = context.GetBuffer(); int offset = 0; buffer[offset++] = (byte)(((int)flags & 240) | ((int)OpCode & 15)); if (PayloadLength > ushort.MaxValue) { // write as a 64-bit length buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127); buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = 0; buffer[offset++] = (byte)(PayloadLength >> 24); buffer[offset++] = (byte)(PayloadLength >> 16); buffer[offset++] = (byte)(PayloadLength >> 8); buffer[offset++] = (byte)(PayloadLength); } else if (PayloadLength > 125) { // write as a 16-bit length buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126); buffer[offset++] = (byte)(PayloadLength >> 8); buffer[offset++] = (byte)(PayloadLength); } else { // write in the header buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength); } if (Mask.HasValue) { int mask = Mask.Value; buffer[offset++] = (byte)(mask >> 24); buffer[offset++] = (byte)(mask >> 16); buffer[offset++] = (byte)(mask >> 8); buffer[offset++] = (byte)(mask); } stream.Write(buffer, 0, offset); if (PayloadLength != 0) { int totalRead = WebSocketsProcessor_RFC6455_13.Copy(Payload, stream, buffer, Mask, PayloadLength); if (totalRead != PayloadLength) { throw new EndOfStreamException("Wrong payload length sent"); } } } finally { context.Recycle(buffer); } }
IEnumerable <WebSocketsFrame> IExtension.ApplyIncoming(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (frame.Reserved1 && !frame.IsControlFrame) { BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { outBuffer = context.GetBuffer(); inBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); if (inbound == null) { inbound = new ZStream(); inbound.inflateInit(); // fake a zlib header with: // CMF: // CM = 8 (deflate) // CINFO = 7 (32k window) // FLG: // FCHECK: 26 (checksum of other bits) // FDICT: 0 (no dictionary) // FLEVEL: 3 (maximum) inBuffer[0] = 120; inBuffer[1] = 218; inbound.next_in = inBuffer; int chk = Inflate(tmp, outBuffer, 2); if (chk != 0) { throw new InvalidOperationException("Spoofed zlib header suggested data"); } } inbound.next_in = inBuffer; int remaining = frame.PayloadLength; //bool first = true; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) { break; } remaining -= readCount; //if (first) //{ // kill the BFINAL flag from the first block, if set; we don't want zlib // // trying to verify the ADLER checksum; unfortunately, a frame can contain // // multiple blocks, and a *later* block could have BFINAL set. That sucks. // inBuffer[0] &= 254; // first = false; //} Inflate(tmp, outBuffer, readCount); } if (remaining != 0) { throw new EndOfStreamException(); } // spoof the missing 4 bytes from the tail inBuffer[0] = inBuffer[1] = 0x00; inBuffer[2] = inBuffer[3] = 0xFF; Inflate(tmp, outBuffer, 4); // set our final output tmp.Position = 0; frame.Payload = tmp; long bytesSaved = tmp.Length - frame.PayloadLength; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = false; tmp = payload as BufferStream; parent.RegisterInboundBytesSaved(bytesSaved); } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (inbound != null) { inbound.next_out = null; inbound.next_in = null; } if (tmp != null) { tmp.Dispose(); } if (inBuffer != null) { context.Recycle(inBuffer); } if (outBuffer != null) { context.Recycle(outBuffer); } if (parent.disableContextTakeover) { ClearContext(true, false); } } } yield return(frame); }
IEnumerable<WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes) { if (frame.Reserved1) { throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?"); } int headerBytes = 0; if (outbound == null) { outbound = new ZStream(); const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15) outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS); headerBytes = 2; } BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { inBuffer = context.GetBuffer(); outBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); outbound.next_out = outBuffer; outbound.next_in = inBuffer; int remaining = frame.PayloadLength; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) break; remaining -= readCount; outbound.next_in_index = 0; outbound.avail_in = readCount; do { outbound.next_out_index = 0; outbound.avail_out = outBuffer.Length; long priorOut = outbound.total_out; int err = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH); if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END) throw new ZStreamException("deflating: " + outbound.msg); int outCount = (int)(outbound.total_out - priorOut); if (outCount > 0) { if (headerBytes == 0) { tmp.Write(outBuffer, 0, outCount); } else { if (outCount < headerBytes) { throw new InvalidOperationException("Failed to write entire header"); } // check the generated header meets our expectations // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window) if ((outBuffer[0] & 15) != 8) { throw new InvalidOperationException("Zlib CM header was incorrect"); } if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid { throw new InvalidOperationException("Zlib CINFO header was incorrect"); } // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary if ((outBuffer[1] & 32) != 0) { throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)"); } // skip the header, and write anything else outCount -= headerBytes; if (outCount > 0) { tmp.Write(outBuffer, headerBytes, outCount); } headerBytes = 0; // all written now } } } while (outbound.avail_in > 0 || outbound.avail_out == 0); } if (remaining != 0) throw new EndOfStreamException(); if (headerBytes != 0) throw new InvalidOperationException("Zlib header was not written"); // verify the last 4 bytes, then drop them tmp.Position = tmp.Length - 4; NetContext.Fill(tmp, outBuffer, 4); if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF)) { throw new InvalidOperationException("expectation failed: 0000FFFF in the tail"); } if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength) { // compressing it didn't do anything useful; since we're going to discard // the compression context, we might as well stick with the original data payload.Position = 0; payload = null; // so that it doesn't get disposed } else { // set our final output tmp.Position = 0; tmp.SetLength(tmp.Length - 4); long bytesSaved = frame.PayloadLength - tmp.Length; frame.Payload = tmp; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = true; tmp = payload as BufferStream; parent.RegisterOutboundBytesSaved(bytesSaved); } } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (outbound != null) { outbound.next_out = null; outbound.next_in = null; } if (tmp != null) tmp.Dispose(); if (inBuffer != null) context.Recycle(inBuffer); if (outBuffer != null) context.Recycle(outBuffer); if (parent.disableContextTakeover) ClearContext(false, true); } } yield return frame; }
IEnumerable<WebSocketsFrame> IExtension.ApplyIncoming(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (frame.Reserved1 && !frame.IsControlFrame) { BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { outBuffer = context.GetBuffer(); inBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); if (inbound == null) { inbound = new ZStream(); inbound.inflateInit(); // fake a zlib header with: // CMF: // CM = 8 (deflate) // CINFO = 7 (32k window) // FLG: // FCHECK: 26 (checksum of other bits) // FDICT: 0 (no dictionary) // FLEVEL: 3 (maximum) inBuffer[0] = 120; inBuffer[1] = 218; inbound.next_in = inBuffer; int chk = Inflate(tmp, outBuffer, 2); if (chk != 0) throw new InvalidOperationException("Spoofed zlib header suggested data"); } inbound.next_in = inBuffer; int remaining = frame.PayloadLength; //bool first = true; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) break; remaining -= readCount; //if (first) //{ // kill the BFINAL flag from the first block, if set; we don't want zlib // // trying to verify the ADLER checksum; unfortunately, a frame can contain // // multiple blocks, and a *later* block could have BFINAL set. That sucks. // inBuffer[0] &= 254; // first = false; //} Inflate(tmp, outBuffer, readCount); } if (remaining != 0) throw new EndOfStreamException(); // spoof the missing 4 bytes from the tail inBuffer[0] = inBuffer[1] = 0x00; inBuffer[2] = inBuffer[3] = 0xFF; Inflate(tmp, outBuffer, 4); // set our final output tmp.Position = 0; frame.Payload = tmp; long bytesSaved = tmp.Length - frame.PayloadLength; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = false; tmp = payload as BufferStream; parent.RegisterInboundBytesSaved(bytesSaved); } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (inbound != null) { inbound.next_out = null; inbound.next_in = null; } if (tmp != null) tmp.Dispose(); if (inBuffer != null) context.Recycle(inBuffer); if (outBuffer != null) context.Recycle(outBuffer); if (parent.disableContextTakeover) ClearContext(true, false); } } yield return frame; }
protected override int ProcessIncoming(NetContext context, Connection connection, System.IO.Stream incoming) { if (incoming.Length < 2) { return(0); // can't read that; start/end markers take at least 2 bytes } switch (incoming.ReadByte()) { case 0x00: var ws = (WebSocketConnection)connection; if (!ws.AllowIncomingDataFrames) { throw new InvalidOperationException("Data frames disabled"); } // data frame is 0x00 [data] 0xFF int len = 0, cur; while ((cur = incoming.ReadByte()) != 0xFF && cur >= 0) { len++; } if (cur < 0) { throw new EndOfStreamException(); } incoming.Position = 1; string value; if (len == 0) { value = ""; } else if (len <= NetContext.BufferSize) { byte[] buffer = null; try { buffer = context.GetBuffer(); NetContext.Fill(incoming, buffer, len); value = Encoding.UTF8.GetString(buffer, 0, len); } finally { context.Recycle(buffer); } } else { var buffer = new byte[len]; NetContext.Fill(incoming, buffer, len); value = Encoding.UTF8.GetString(buffer, 0, len); } context.Handler.OnReceived(connection, value); return(len + 2); case 0xFF: // shutdown is 0xFF 0x00 if (incoming.ReadByte() == 0x00) { GracefulShutdown(context, connection); return(2); } else { throw new InvalidOperationException("protocol fail"); } default: throw new InvalidOperationException("protocol fail"); } }