ClientHandshake.cs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. using System;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. using System.Text;
  5. namespace Mirror.SimpleWeb
  6. {
  7. /// <summary>
  8. /// Handles Handshake to the server when it first connects
  9. /// <para>The client handshake does not need buffers to reduce allocations since it only happens once</para>
  10. /// </summary>
  11. internal class ClientHandshake
  12. {
  13. public bool TryHandshake(Connection conn, Uri uri)
  14. {
  15. try
  16. {
  17. Stream stream = conn.stream;
  18. byte[] keyBuffer = new byte[16];
  19. using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
  20. {
  21. rng.GetBytes(keyBuffer);
  22. }
  23. string key = Convert.ToBase64String(keyBuffer);
  24. string keySum = key + Constants.HandshakeGUID;
  25. byte[] keySumBytes = Encoding.ASCII.GetBytes(keySum);
  26. Log.Verbose($"Handshake Hashing {Encoding.ASCII.GetString(keySumBytes)}");
  27. byte[] keySumHash = SHA1.Create().ComputeHash(keySumBytes);
  28. string expectedResponse = Convert.ToBase64String(keySumHash);
  29. string handshake =
  30. $"GET {uri.PathAndQuery} HTTP/1.1\r\n" +
  31. $"Host: {uri.Host}:{uri.Port}\r\n" +
  32. $"Upgrade: websocket\r\n" +
  33. $"Connection: Upgrade\r\n" +
  34. $"Sec-WebSocket-Key: {key}\r\n" +
  35. $"Sec-WebSocket-Version: 13\r\n" +
  36. "\r\n";
  37. byte[] encoded = Encoding.ASCII.GetBytes(handshake);
  38. stream.Write(encoded, 0, encoded.Length);
  39. byte[] responseBuffer = new byte[1000];
  40. int? lengthOrNull = ReadHelper.SafeReadTillMatch(stream, responseBuffer, 0, responseBuffer.Length, Constants.endOfHandshake);
  41. if (!lengthOrNull.HasValue)
  42. {
  43. Log.Error("Connected closed before handshake");
  44. return false;
  45. }
  46. string responseString = Encoding.ASCII.GetString(responseBuffer, 0, lengthOrNull.Value);
  47. string acceptHeader = "Sec-WebSocket-Accept: ";
  48. int startIndex = responseString.IndexOf(acceptHeader, StringComparison.InvariantCultureIgnoreCase) + acceptHeader.Length;
  49. int endIndex = responseString.IndexOf("\r\n", startIndex);
  50. string responseKey = responseString.Substring(startIndex, endIndex - startIndex);
  51. if (responseKey != expectedResponse)
  52. {
  53. Log.Error($"Response key incorrect, Response:{responseKey} Expected:{expectedResponse}");
  54. return false;
  55. }
  56. return true;
  57. }
  58. catch (Exception e)
  59. {
  60. Log.Exception(e);
  61. return false;
  62. }
  63. }
  64. }
  65. }