private bool ShakeHandsHixie76() { // Ensure we have all the data we need if ((_Header.ContainsKey("Key1")) && (_Header.ContainsKey("Key2")) && (_Header.ContainsKey("Host")) && (_Header.ContainsKey("Origin")) && (_Header.ContainsKey("Path"))) { List <byte> ToHash = new List <byte>(); byte[] TempBytes; // Get the data to hash TempBytes = BitConverter.GetBytes(CalculateWebSocketKey(_Header["Key1"])); for (int i = 3; i >= 0; i--) { ToHash.Add(TempBytes[i]); } TempBytes = BitConverter.GetBytes(CalculateWebSocketKey(_Header["Key2"])); for (int i = 3; i >= 0; i--) { ToHash.Add(TempBytes[i]); } ToHash.AddRange(ReadBytes(8)); // Hash the data byte[] Hashed = MD5.Create().ComputeHash(ToHash.ToArray()); // Setup the handshake response string Response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Origin: " + _Header["Origin"] + "\r\n" + "Sec-WebSocket-Location: ws://" + _Header["Host"] + _Header["Path"] + "\r\n"; if (_Header.ContainsKey("SubProtocol")) { Response += "Sec-WebSocket-Protocol: plain\r\n"; // Only sub-protocol we support } Response += "\r\n"; // Send the response and return WriteBytes(Encoding.ASCII.GetBytes(Response)); WriteBytes(Hashed); return(true); } else { // We're missing some pice of data, log what we do have RMLog.Debug("Missing some piece of handshake data. Here's what we have:"); foreach (DictionaryEntry DE in _Header) { RMLog.Debug(DE.Key + " => " + DE.Value); } return(false); } }
private bool ShakeHandsRFC6455() { // Ensure we have all the data we need // TODOX Firefox (v49, maybe others) is not sending an Origin header, which breaks things // TODOX if ((_Header.ContainsKey("Key")) && (_Header.ContainsKey("Host")) && (_Header.ContainsKey("Origin")) && (_Header.ContainsKey("Path"))) if ((_Header.ContainsKey("Key")) && (_Header.ContainsKey("Host")) && (_Header.ContainsKey("Path"))) { string AcceptGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Combine Key and GUID string ToHash = _Header["Key"] + AcceptGUID; // Hash the string byte[] Hashed = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(ToHash)); // Encode the hash string Encoded = Convert.ToBase64String(Hashed); // Setup the handshake response var Response = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: " + Encoded + "\r\n"; if (_Header.ContainsKey("SubProtocol")) { Response += "Sec-WebSocket-Protocol: plain\r\n"; // Only sub-protocol we support } Response += "\r\n"; // Send the response and return WriteBytes(Encoding.ASCII.GetBytes(Response)); return(true); } else { // We're missing some pice of data, log what we do have RMLog.Debug("Missing some piece of handshake data. Here's what we have:"); foreach (DictionaryEntry DE in _Header) { RMLog.Debug(DE.Key + " => " + DE.Value); } return(false); } }
public bool Open(int ASocketHandle) { if (OSUtils.IsWindows) { try { NativeMethods.WSAData WSA = new NativeMethods.WSAData(); SocketError Result = NativeMethods.WSAStartup((short)0x0202, out WSA); if (Result != SocketError.Success) { throw new SocketException(NativeMethods.WSAGetLastError()); } SocketPermission SP = new SocketPermission(System.Security.Permissions.PermissionState.Unrestricted); SP.Demand(); SocketInformation SI = new SocketInformation() { Options = SocketInformationOptions.Connected, ProtocolInformation = new byte[Marshal.SizeOf(typeof(NativeMethods.WSAPROTOCOL_INFO))], }; Result = SocketError.Success; unsafe { fixed(byte *pinnedBuffer = SI.ProtocolInformation) { Result = NativeMethods.WSADuplicateSocket(new IntPtr(ASocketHandle), (uint)Process.GetCurrentProcess().Id, pinnedBuffer); } } if (Result != SocketError.Success) { throw new SocketException(NativeMethods.WSAGetLastError()); } return(Open(new Socket(SI))); } catch (SocketException sex) { RMLog.DebugException(sex, "SocketException in TcpConnection::Open(). ErrorCode=" + sex.ErrorCode.ToString()); return(false); } catch (Exception ex) { RMLog.DebugException(ex, "Exception in TcpConnection::Open()"); return(false); } } else { RMLog.Debug("MONO cannot open an existing socket handle"); return(false); //try //{ // SocketInformation SI = new SocketInformation(); // SI.Options = SocketInformationOptions.Connected; // SI.ProtocolInformation = new byte[24]; // // From Mono's Socket.cs DuplicateAndClose() SI.ProtocolInformation = Mono.DataConverter.Pack("iiiil", (int)address_family, (int)socket_type, (int)protocol_type, isbound ? 1 : 0, (long)socket); // byte[] B1 = BitConverter.GetBytes((int)AddressFamily.InterNetwork); // byte[] B2 = BitConverter.GetBytes((int)SocketType.Stream); // byte[] B3 = BitConverter.GetBytes((int)ProtocolType.Tcp); // byte[] B4 = BitConverter.GetBytes((int)1); // byte[] B5 = BitConverter.GetBytes((long)ASocketHandle); // Array.Copy(B1, 0, SI.ProtocolInformation, 0, B1.Length); // Array.Copy(B2, 0, SI.ProtocolInformation, 4, B2.Length); // Array.Copy(B3, 0, SI.ProtocolInformation, 8, B3.Length); // Array.Copy(B4, 0, SI.ProtocolInformation, 12, B4.Length); // Array.Copy(B5, 0, SI.ProtocolInformation, 16, B5.Length); // return Open(new Socket(SI)); //} //catch (SocketException sex) //{ // RMLog.DebugException(sex, "SocketException in TcpConnection::Open(). ErrorCode=" + sex.ErrorCode.ToString()); // return false; //} //catch (Exception ex) //{ // RMLog.DebugException(ex, "Exception in TcpConnection::Open()"); // return false; //} } }
private bool ShakeHands() { _Header["Version"] = "0"; try { // Peek first byte for 22, 128 (indicates ssl) // Don't use class methods for peek/read since they eat data into the input buffer, and AuthenticateAsServer needs that data if (_Socket.Poll(5 * 1000 * 1000, SelectMode.SelectRead)) { byte[] FirstByte = new byte[1]; _Socket.Receive(FirstByte, 0, 1, SocketFlags.Peek); if ((FirstByte[0] == 22) || (FirstByte[0] == 128)) { if (_Certificate == null) { throw new Exception("wss:// requires a certificate"); } else { var SSL = new SslStream(_Stream, false); _Stream = SSL; try { SSL.AuthenticateAsServer(_Certificate, false, SslProtocols.Tls, false); } catch (Exception ex) { RMLog.Debug("Error during SSL.AuthenticateAsServer(): " + ex.Message); return(false); } Protocol = "wss"; } } } else { RMLog.Debug("Timeout exceeded while waiting for complete handshake"); return(false); } // Keep reading header data until we get all the data we want DateTime LoopStart = DateTime.Now; while (true) { // Read another line, and abort if we don't get one within 5 seconds string InLine = ReadLn(new string[] { "\r\n", "\0" }, false, '\0', 5000).Trim(); if (ReadTimedOut) { RMLog.Debug("Timeout exceeded while waiting for next handshake line"); return(false); } else if (DateTime.Now.Subtract(LoopStart).TotalSeconds > 30.0) { RMLog.Debug("Timeout exceeded while waiting for handshake to complete"); return(false); } RMLog.Trace("Handshake Line: " + InLine); // Check for blank line (indicates we have most of the header, and only the last 8 bytes remain if (string.IsNullOrEmpty(InLine)) { switch (_Header["Version"]) { case "0": if (_Header.ContainsKey("Sec-WebSocket-Key1")) { _ProtocolVersion = ProtocolVersion.Hixie76; return(ShakeHandsHixie76()); } else { // Only used by Chrome 4 and iOS 5.0.0 so probably not worth bothering _ProtocolVersion = ProtocolVersion.Hixie75; return(false); } case "7": case "8": case "13": _ProtocolVersion = ProtocolVersion.RFC6455; return(ShakeHandsRFC6455()); default: // TODO If this version does not // match a version understood by the server, the server MUST // abort the websocket handshake described in this section and // instead send an appropriate HTTP error code (such as 426 // Upgrade Required), and a |Sec-WebSocket-Version| header // indicating the version(s) the server is capable of // understanding. return(false); } } else if (InLine.StartsWith("Connection:")) { // Example: "Connection: Upgrade" // NB: New in protocol 8+ _Header["Connection"] = InLine.Replace("Connection:", "").Trim(); } else if (InLine.StartsWith("GET")) { // Example: "GET /demo HTTP/1.1" string[] GET = InLine.Split(' '); _Header["Path"] = GET[1]; } else if (InLine.StartsWith("Host:")) { // Example: "Host: example.com" _Header["Host"] = InLine.Replace("Host:", "").Trim(); } else if (InLine.StartsWith("Origin:")) { // Example: "Origin: http://example.com" // NB: Not used in protocol 8+ _Header["Origin"] = InLine.Replace("Origin:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Key:")) { // Example: "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" // NB: New in protocol 8+ _Header["Key"] = InLine.Replace("Sec-WebSocket-Key:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Key1:")) { // Example: "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5" // NB: Not used in protocol 8+ _Header["Key1"] = InLine.Replace("Sec-WebSocket-Key1:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Key2:")) { // Example: "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00" // NB: Not used in protocol 8+ _Header["Key2"] = InLine.Replace("Sec-WebSocket-Key2:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Origin:")) { // Example: "Sec-WebSocket-Origin: http://example.com" // NB: New in protocol 8+ _Header["Origin"] = InLine.Replace("Sec-WebSocket-Origin:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Protocol:")) { // Example: "Sec-WebSocket-Protocol: sample" _Header["SubProtocol"] = InLine.Replace("Sec-WebSocket-Protocol:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Draft")) { // Example: "Sec-WebSocket-Draft: 2" _Header["Version"] = InLine.Replace("Sec-WebSocket-Draft:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Version")) { // Example: "Sec-WebSocket-Version: 8" _Header["Version"] = InLine.Replace("Sec-WebSocket-Version:", "").Trim(); } else if (InLine.StartsWith("Upgrade:")) { // Example: "Upgrade: websocket" // NB: New in protocol 8+ _Header["Upgrade"] = InLine.Replace("Upgrade:", "").Trim(); } else if (InLine.StartsWith("<policy-file-request")) { string PolicyResponse = "<?xml version=\"1.0\"?>\n" + "<cross-domain-policy>\n" + " <allow-access-from domain=\"*\" to-ports=\"*\"/>\n" + " <site-control permitted-cross-domain-policies=\"all\"/>\n" + "</cross-domain-policy>\n" + "\0"; WriteRaw(Encoding.UTF8.GetBytes(PolicyResponse)); FlashPolicyFileRequest = true; return(false); } } } catch (Exception ex) { RMLog.DebugException(ex, "Exception in WebSocketConnection::ShakeHands()"); } return(false); }