private void NewConnectionHandler() { var server = new TcpListener(ip, port); server.Start(); var policy = new TokenValidationPolicyBuilder() .RequireSignature(SymmetricJwk.FromBase64Url(confWebSocket.JwsKey), SignatureAlgorithm.HmacSha512) .RequireIssuer("Leierkasten Backend") .Build(); var reader = new JwtReader(); while (running) { TcpClient client; if (server.Pending()) { client = server.AcceptTcpClient(); } else { Thread.Sleep(100); continue; } var stream = client.GetStream(); stream.WriteTimeout = 10; while (client.Available < 3) { Thread.Sleep(100); } var bytes = new byte[client.Available]; stream.Read(bytes, 0, bytes.Length); // This is a textual request, decode it var request = Encoding.UTF8.GetString(bytes); // Check if this is a websocket handshake. If no, skip. if (!new Regex("^GET").IsMatch(request)) { Log.Warn("Denied websocket because client used wrong method."); stream.Write(AccessDeniedResponse, 0, AccessDeniedResponse.Length); continue; } const string eol = "\r\n"; const string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // HTML headers are case insensitive var match = new Regex("Sec-WebSocket-Key: (.*)", RegexOptions.IgnoreCase).Match(request); if (!match.Success) { Log.Warn("Sec-WebSocket-Key was not found in request."); Log.Trace("Request was (base64-encoded): " + Convert.ToBase64String(Encoding.ASCII.GetBytes(request))); stream.Write(AccessDeniedResponse, 0, AccessDeniedResponse.Length); continue; } if (match.Groups.Count != 2) { InvalidMatchNumber(match, stream); continue; } string key = match.Groups[1].Value.Trim(); byte[] hashedAcceptKey = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key + guid)); string base64AcceptKey = Convert.ToBase64String(hashedAcceptKey); Log.Trace("Received Sec-WebSocket-Key: " + key); if (Log.IsTraceEnabled) { StringBuilder hex = new StringBuilder(hashedAcceptKey.Length * 2); foreach (byte b in hashedAcceptKey) { hex.AppendFormat("{0:x2}", b); } Log.Trace("Hashed Sec-WebSocket-Key: " + hex); } Log.Trace("Base64 if Sec-WebSocket-Key Hash: " + base64AcceptKey); string responseStr = "HTTP/1.1 101 Switching Protocols" + eol + "Connection: Upgrade" + eol + "Upgrade: websocket" + eol + "Sec-WebSocket-Accept: " + base64AcceptKey + eol + eol; byte[] response = Encoding.UTF8.GetBytes(responseStr); // Get cookie match = new Regex("lk-session=([^;\r\n]*)", RegexOptions.IgnoreCase).Match(request); if (!match.Success) { Log.Warn("Denied websocket because session cookie is missing."); stream.Write(AccessDeniedResponse, 0, AccessDeniedResponse.Length); continue; } if (match.Groups.Count != 2) { InvalidMatchNumber(match, stream); continue; } // Validate session cookie of user var sessionCookie = match.Groups[1].ToString(); var result = reader.TryReadToken(sessionCookie, policy); if (!result.Succedeed) { Log.Warn($"Denied websocket because JWS token could not be validated: {result.ErrorHeader}, {result.ErrorClaim}."); stream.Write(AccessDeniedResponse, 0, AccessDeniedResponse.Length); continue; } if (result.Token?.Payload == null) { Log.Warn("Denied websocket because JWS token payload is empty."); stream.Write(AccessDeniedResponse, 0, AccessDeniedResponse.Length); continue; } var uid = JsonConvert.DeserializeObject <Dictionary <string, string> >(result.Token.Payload.ToString())["uid"]; // Send handshake response stream.Write(response, 0, response.Length); // Start handler for the connection var handler = new WebSocketConnection(client, uid); if (!ConnectedClients.TryAdd(clientCounter++, handler)) { handler.Stop(); } else { OnClientConnected?.Invoke(this, new ClientConnectedEventArgs(handler)); } } server.Stop(); }
public ClientConnectedEventArgs(WebSocketConnection client) { Client = client; }