private async Task RunHeartbeatAsync(int interval, CancellationToken token) { #region Valve sucks at naming enum members /* * Let me tell you a story about 10/14/2017 * So I was working on this lib ironing out some kinks, rewriting the connection code * when I finally completed it and decided to test it. So I pop open a console just to * find obvious bugs like the whole thing exploding for no reason. Suddenly at about 5:08 PM * it starts working properly. Stuff starts printing in the console and it's working properly. * It works correctly until about 20 seconds later when the console window vanishes and up pops an error * "IOException: Unable to transfer data" bla bla bla BASICALLY the connection was aborted. * This brought up another bug in my TCP client where I wouldn't actually tell anyone the connection disconnected, * but who cares about that. This brought in a more important problem: Steam drops my connection even though my heart is beating * So I try a WebSocket. Same thing. Check the headers (because there's nothing the in body), everything lines up. * I reference the SteamKit, everything related to serialization is in order. Everything is right except one thing. * One small piece that nobody would notice. * The correct message type for a heartbeat is "ClientHeartBeat", not "Heartbeat"... * I found this out after 3 hours. Please kill me. * * * TLDR: Valve sucks at naming enum members. */ #endregion // cache the data so we don't serialize 500 times per session. Our session ID will never change and neither will our Steam ID var beat = NetworkMessage.CreateProtobufMessage(MessageType.ClientHeartBeat, new CMsgClientHeartBeat()).WithClientInfo(SteamId, SessionId).Serialize(); try { await NetLog.DebugAsync($"Heartbeat started on a {interval} ms interval").ConfigureAwait(false); while (!token.IsCancellationRequested) { await Task.Delay(interval, token).ConfigureAwait(false); try { await SendAsync(beat).ConfigureAwait(false); } catch (Exception ex) { await NetLog.ErrorAsync($"The heartbeat task encountered an unknown exception while sending the heartbeat message", ex).ConfigureAwait(false); } } } catch (OperationCanceledException) { await NetLog.DebugAsync("Heartbeat stopped").ConfigureAwait(false); } catch (Exception ex) { await NetLog.ErrorAsync($"The heartbeat task encountered an unknown exception", ex).ConfigureAwait(false); } }
private async Task ProcessEncryptRequest(NetworkMessage message) { ChannelEncryptRequest encryptRequest = message.Deserialize <ChannelEncryptRequest>(); await NetLog.VerboseAsync($"Encrypting channel on protocol version {encryptRequest.ProtocolVersion} in universe {encryptRequest.Universe}").ConfigureAwait(false); byte[] challange = encryptRequest.Challenge.All(b => b == 0) ? encryptRequest.Challenge : null; // check if all the values were made 0 by the marshal byte[] publicKey = UniverseUtils.GetPublicKey(encryptRequest.Universe); if (publicKey == null) { await NetLog.ErrorAsync($"Cannot find public key for universe {encryptRequest.Universe}").ConfigureAwait(false); throw new InvalidOperationException($"Public key does not exist for universe {encryptRequest.Universe}"); } byte[] tempSessionKey = CryptoUtils.GenerateBytes(32); byte[] encryptedHandshake = null; using (RsaCrypto rsa = new RsaCrypto(publicKey)) { if (challange != null) { byte[] handshakeToEncrypt = new byte[tempSessionKey.Length + challange.Length]; Array.Copy(tempSessionKey, handshakeToEncrypt, tempSessionKey.Length); Array.Copy(challange, 0, handshakeToEncrypt, tempSessionKey.Length, challange.Length); encryptedHandshake = rsa.Encrypt(handshakeToEncrypt); } else { encryptedHandshake = rsa.Encrypt(tempSessionKey); } } Encryption = challange != null ? (IEncryptor) new HmacEncryptor(tempSessionKey) : new SimpleEncryptor(tempSessionKey); var encryptResponse = NetworkMessage.CreateMessage(MessageType.ChannelEncryptResponse, new ChannelEncryptResponse { KeySize = 128, KeyHash = CryptoUtils.CrcHash(encryptedHandshake), EncryptedHandshake = encryptedHandshake, ProtocolVersion = 1, }); await SendAsync(encryptResponse).ConfigureAwait(false); }