public NetFilterEncryption(byte[] sessionKey) { DebugLog.Assert(sessionKey.Length == 32, "NetFilterEncryption", "AES session key was not 32 bytes!"); this.sessionKey = sessionKey; }
async Task <byte[]> ReadMessageAsync(CancellationToken cancellationToken) { using (var ms = new MemoryStream()) { var buffer = new byte[1024]; var segment = new ArraySegment <byte>(buffer); WebSocketReceiveResult result; do { try { result = await socket.ReceiveAsync(segment, cancellationToken).ConfigureAwait(false); } catch (ObjectDisposedException) { connection.DisconnectCore(userInitiated: cancellationToken.IsCancellationRequested, specificContext: this); return(null); } catch (WebSocketException) { connection.DisconnectCore(userInitiated: false, specificContext: this); return(null); } catch (Win32Exception) { connection.DisconnectCore(userInitiated: false, specificContext: this); return(null); } switch (result.MessageType) { case WebSocketMessageType.Binary: ms.Write(buffer, 0, result.Count); DebugLog.WriteLine(nameof(WebSocketContext), "Recieved {0} bytes.", result.Count); break; case WebSocketMessageType.Text: try { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); DebugLog.WriteLine(nameof(WebSocketContext), "Recieved websocket text message: \"{0}\"", message); } catch { var frameBytes = new byte[result.Count]; Array.Copy(buffer, 0, frameBytes, 0, result.Count); var frameHexBytes = BitConverter.ToString(frameBytes).Replace("-", string.Empty); DebugLog.WriteLine(nameof(WebSocketContext), "Recieved websocket text message: 0x{0}", frameHexBytes); } break; case WebSocketMessageType.Close: default: connection.DisconnectCore(userInitiated: false, specificContext: this); return(null); } }while (!result.EndOfMessage); return(ms.ToArray()); } }
/// <summary> /// Receives the packet, performs all sanity checks and then passes it along as necessary. /// </summary> /// <param name="packet">The packet.</param> private void ReceivePacket(UdpPacket packet) { // Check for a malformed packet if (!packet.IsValid) { return; } else if (remoteConnId > 0 && packet.Header.SourceConnID != remoteConnId) { return; } DebugLog.WriteLine("UdpConnection", "<- Recv'd {0} Seq {1} Ack {2}; {3} bytes; Message: {4} bytes {5} packets", packet.Header.PacketType, packet.Header.SeqThis, packet.Header.SeqAck, packet.Header.PayloadSize, packet.Header.MsgSize, packet.Header.PacketsInMsg); // Throw away any duplicate messages we've already received, making sure to // re-ack it in case it got lost. if (packet.Header.PacketType == EUdpPacketType.Data && packet.Header.SeqThis < inSeq) { SendAck(); return; } // When we get a SeqAck, all packets with sequence numbers below that have been safely received by // the server; we are now free to remove our copies if (outSeqAcked < packet.Header.SeqAck) { outSeqAcked = packet.Header.SeqAck; // outSeqSent can be less than this in a very rare case involving resent packets. if (outSeqSent < outSeqAcked) { outSeqSent = outSeqAcked; } outPackets.RemoveAll(x => x.Header.SeqThis <= outSeqAcked); nextResend = DateTime.Now.AddSeconds(RESEND_DELAY); } // inSeq should always be the latest value that we can ack, so advance it as far as is possible. if (packet.Header.SeqThis == inSeq + 1) { do { inSeq++; }while (inPackets.ContainsKey(inSeq + 1)); } switch (packet.Header.PacketType) { case EUdpPacketType.Challenge: ReceiveChallenge(packet); break; case EUdpPacketType.Accept: ReceiveAccept(packet); break; case EUdpPacketType.Data: ReceiveData(packet); break; case EUdpPacketType.Disconnect: DebugLog.WriteLine("UdpConnection", "Disconnected by server"); state = (int)State.Disconnected; return; case EUdpPacketType.Datagram: break; default: DebugLog.WriteLine("UdpConnection", "Received unexpected packet type " + packet.Header.PacketType); break; } }
// this is now a steamkit meme /// <summary> /// Nets the loop. /// </summary> void NetLoop(object param) { // poll for readable data every 100ms const int POLL_MS = 100; Socket socket = param as Socket; while (!wantsNetShutdown) { bool canRead = false; try { canRead = socket.Poll(POLL_MS * 1000, SelectMode.SelectRead); } catch (Exception ex) { DebugLog.WriteLine("TcpConnection", "Socket exception while polling: {0}", ex); Cleanup(); return; } if (!canRead) { // nothing to read yet continue; } // potential here is to be waiting to acquire the lock when Disconnect is trying to join us while (!wantsNetShutdown && !netLock.TryEnterUpgradeableReadLock(500)) { } byte[] packData = null; try { if (wantsNetShutdown || netStream == null) { return; } // read the packet off the network packData = ReadPacket(); // decrypt the data off the wire if needed if (filter != null) { packData = filter.ProcessIncoming(packData); } } catch (IOException ex) { DebugLog.WriteLine("TcpConnection", "Socket exception occurred while reading packet: {0}", ex); // signal that our connection is dead Cleanup(); return; } finally { if (netLock.IsUpgradeableReadLockHeld) { netLock.ExitUpgradeableReadLock(); } } OnNetMsgReceived(new NetMsgEventArgs(packData, socket.RemoteEndPoint as IPEndPoint)); } }
static bool TryReadAsBinaryCore(Stream input, KeyValue current, KeyValue?parent) { current.Children = new List <KeyValue>(); while (true) { var type = ( Type )input.ReadByte(); if (type == Type.End) { break; } current.Name = input.ReadNullTermString(Encoding.UTF8); switch (type) { case Type.None: { var child = new KeyValue(); var didReadChild = TryReadAsBinaryCore(input, child, current); if (!didReadChild) { return(false); } break; } case Type.String: { current.Value = input.ReadNullTermString(Encoding.UTF8); break; } case Type.WideString: { DebugLog.WriteLine("KeyValue", "Encountered WideString type when parsing binary KeyValue, which is unsupported. Returning false."); return(false); } case Type.Int32: case Type.Color: case Type.Pointer: { current.Value = Convert.ToString(input.ReadInt32()); break; } case Type.UInt64: { current.Value = Convert.ToString(input.ReadUInt64()); break; } case Type.Float32: { current.Value = Convert.ToString(input.ReadFloat()); break; } case Type.Int64: { current.Value = Convert.ToString(input.ReadInt64()); break; } default: { return(false); } } if (parent != null) { parent.Children.Add(current); } current = new KeyValue(); } return(true); }
/// <summary> /// Processes incoming packets, maintains connection consistency, and oversees outgoing packets. /// </summary> private void NetLoop(object param) { // Variables that will be used deeper in the function; locating them here avoids recreating // them since they don't need to be. var userRequestedDisconnect = false; EndPoint packetSender = (EndPoint) new IPEndPoint(IPAddress.Any, 0); byte[] buf = new byte[2048]; var epTask = param as Task <IPEndPoint>; try { if (epTask != null) { remoteEndPoint = epTask.Result; } else { DebugLog.WriteLine("UdpConnection", "Invalid endpoint supplied for connection: {0}", param); } } catch (AggregateException ae) { foreach (var ex in ae.Flatten().InnerExceptions) { DebugLog.WriteLine("UdpConnection", "Endpoint task threw exception: {0}", ex); } } if (remoteEndPoint != null) { timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY); nextResend = DateTime.Now.AddSeconds(RESEND_DELAY); if (Interlocked.CompareExchange(ref state, (int)State.ChallengeReqSent, (int)State.Disconnected) != (int)State.Disconnected) { state = (int)State.Disconnected; userRequestedDisconnect = true; } else { // Begin by sending off the challenge request SendPacket(new UdpPacket(EUdpPacketType.ChallengeReq)); } } while (state != (int)State.Disconnected) { try { // Wait up to 150ms for data, if none is found and the timeout is exceeded, we're done here. if (!sock.Poll(150000, SelectMode.SelectRead) && DateTime.Now > timeOut) { DebugLog.WriteLine("UdpConnection", "Connection timed out"); state = (int)State.Disconnected; break; } // By using a 10ms wait, we allow for multiple packets sent at the time to all be processed before moving on // to processing output and therefore Acks (the more we process at the same time, the fewer acks we have to send) while (sock.Poll(10000, SelectMode.SelectRead)) { int length = sock.ReceiveFrom(buf, ref packetSender); // Ignore packets that aren't sent by the server we're connected to. if (!packetSender.Equals(remoteEndPoint)) { continue; } // Data from the desired server was received; delay timeout timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY); MemoryStream ms = new MemoryStream(buf, 0, length); UdpPacket packet = new UdpPacket(ms); ReceivePacket(packet); } } catch (IOException ex) { DebugLog.WriteLine("UdpConnection", "Exception occurred while reading packet: {0}", ex); state = (int)State.Disconnected; break; } catch (SocketException e) { DebugLog.WriteLine("UdpConnection", "Critical socket failure: " + e.ErrorCode); state = (int)State.Disconnected; break; } // Send or resend any sequenced packets; a call to ReceivePacket can set our state to disconnected // so don't send anything we have queued in that case if (state != (int)State.Disconnected) { SendPendingMessages(); } // If we received data but had no data to send back, we need to manually Ack (usually tags along with // outgoing data); also acks disconnections if (inSeq != inSeqAcked) { SendAck(); } // If a graceful shutdown has been requested, nothing in the outgoing queue is discarded. // Once it's empty, we exit, since the last packet was our disconnect notification. if (state == (int)State.Disconnecting && outPackets.Count == 0) { DebugLog.WriteLine("UdpConnection", "Graceful disconnect completed"); state = (int)State.Disconnected; userRequestedDisconnect = true; break; } } DebugLog.WriteLine("UdpConnection", "Calling OnDisconnected"); OnDisconnected(new DisconnectedEventArgs(userRequestedDisconnect)); }
/// <summary> /// Fetches a list of content servers. /// </summary> /// <param name="csServer"> /// The optional Steam3 content server to fetch the list from. /// If this parameter is not specified, a random CS server will be selected. /// </param> /// <param name="cellId"> /// The optional CellID used to specify which regional servers should be returned in the list. /// If this parameter is not specified, Steam's GeoIP suggested CellID will be used instead. /// </param> /// <param name="maxServers">The maximum amount of servers to request.</param> /// <returns>A list of servers.</returns> /// <exception cref="System.InvalidOperationException"> /// No Steam CS servers available, or the suggested CellID is unavailable. /// Check that the <see cref="SteamClient"/> associated with this <see cref="CDNClient"/> instance is logged onto Steam. /// </exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <IList <Server> > FetchServerListAsync(IPEndPoint csServer = null, uint?cellId = null, int maxServers = 20) { DebugLog.Assert(steamClient.IsConnected, "CDNClient", "CMClient is not connected!"); DebugLog.Assert(steamClient.CellID != null, "CDNClient", "CMClient is not logged on!"); if (csServer == null) { // if we're not specifying what CS server we want to fetch a server list from, randomly select a cached CS server var csServers = steamClient.GetServersOfType(EServerType.CS); if (csServers.Count == 0) { // steamclient doesn't know about any CS servers yet throw new InvalidOperationException("No CS servers available!"); } Random random = new Random(); csServer = csServers[random.Next(csServers.Count)]; } if (cellId == null) { if (steamClient.CellID == null) { throw new InvalidOperationException("Recommended CellID is not available. CMClient not logged on?"); } // fallback to recommended cellid cellId = steamClient.CellID.Value; } var serverKv = await DoCommandAsync(csServer, HttpMethod.Get, "serverlist", args : string.Format("{0}/{1}/", cellId, maxServers)).ConfigureAwait(false); var serverList = new List <Server>(maxServers); if (serverKv["deferred"].AsBoolean()) { return(serverList); } foreach (var server in serverKv.Children) { string type = server["type"].AsString(); string host = server["host"].AsString(); string vhost = server["vhost"].AsString(); string[] hostSplits = host.Split(':'); int port = 80; if (hostSplits.Length > 1) { int parsedPort; if (int.TryParse(hostSplits[1], out parsedPort)) { port = parsedPort; } } uint serverCell = ( uint )server["cell"].AsInteger(); int load = server["load"].AsInteger(); int weightedLoad = server["weightedload"].AsInteger(); int entries = server["NumEntriesInClientList"].AsInteger(1); int useTokenAuth = server["usetokenauth"].AsInteger(); string httpsSupport = server["https_support"].AsString(); // If usetokenauth is specified, we can treat this server as a CDN and request tokens if (useTokenAuth > 0) { type = "CDN"; } Server.ConnectionProtocol protocol = (httpsSupport == "optional" || httpsSupport == "mandatory") ? Server.ConnectionProtocol.HTTPS : Server.ConnectionProtocol.HTTP; serverList.Add(new Server { Protocol = protocol, Host = host, VHost = vhost, Port = protocol == Server.ConnectionProtocol.HTTPS ? 443 : port, Type = type, CellID = serverCell, Load = load, WeightedLoad = weightedLoad, NumEntries = entries }); } return(serverList); }
async Task <Stream> DoRawCommandAsStreamAsync(Server server, HttpMethod method, string command, string data = null, bool doAuth = false, string args = "", string authtoken = null) { var url = BuildCommand(server, command, args, authtoken); var request = new HttpRequestMessage(method, url); if (doAuth && server.Type == "CS") { var req = Interlocked.Increment(ref reqCounter); byte[] shaHash; using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { var uri = new Uri(url); bw.Write(sessionId); bw.Write(req); bw.Write(sessionKey); bw.Write(Encoding.UTF8.GetBytes(uri.AbsolutePath)); shaHash = CryptoHelper.SHAHash(ms.ToArray()); } string hexHash = Utils.EncodeHexString(shaHash); string authHeader = string.Format("sessionid={0};req-counter={1};hash={2};", sessionId, req, hexHash); request.Headers.Add("x-steam-auth", authHeader); } if (HttpMethod.Post.Equals(method)) { request.Content = new StringContent(data, Encoding.UTF8); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); } using (var cts = new CancellationTokenSource()) { cts.CancelAfter(RequestTimeout); try { var response = await httpClient.SendAsync(request, cts.Token).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { throw new SteamKitWebRequestException($"Response status code does not indicate success: {response.StatusCode} ({response.ReasonPhrase}).", response); } return(await response.Content.ReadAsStreamAsync().ConfigureAwait(false)); //var responseData = await response.Content.ReadAsByteArrayAsync().ConfigureAwait( false ); //return responseData; } catch (Exception ex) { DebugLog.WriteLine("CDNClient", "Failed to complete web request to {0}: {1}", url, ex.Message); throw; } } }
/// <summary> /// Manually calls the specified Web API function with the provided details. /// </summary> /// <param name="func">The function name to call.</param> /// <param name="version">The version of the function to call.</param> /// <param name="args">A dictionary of string key value pairs representing arguments to be passed to the API.</param> /// <param name="method">The http request method. Either "POST" or "GET".</param> /// <param name="secure">if set to <c>true</c> this method will be called through the secure API.</param> /// <returns>A <see cref="Task{T}"/> that contains a <see cref="KeyValue"/> object representing the results of the Web API call.</returns> /// <exception cref="ArgumentNullException">The function name or request method provided were <c>null</c>.</exception> /// <exception cref="WebException">An network error occurred when performing the request.</exception> /// <exception cref="InvalidDataException">An error occured when parsing the response from the WebAPI.</exception> public Task <KeyValue> Call(string func, int version = 1, Dictionary <string, string> args = null, string method = WebRequestMethods.Http.Get, bool secure = false) { if (func == null) { throw new ArgumentNullException("func"); } if (args == null) { args = new Dictionary <string, string>(); } if (method == null) { throw new ArgumentNullException("method"); } StringBuilder urlBuilder = new StringBuilder(); StringBuilder paramBuilder = new StringBuilder(); urlBuilder.Append(secure ? "https://" : "http://"); urlBuilder.Append(API_ROOT); urlBuilder.AppendFormat("/{0}/{1}/v{2}", iface, func, version); bool isGet = method.Equals(WebRequestMethods.Http.Get, StringComparison.OrdinalIgnoreCase); if (isGet) { // if we're doing a GET request, we'll build the params onto the url paramBuilder = urlBuilder; paramBuilder.Append("/?"); // start our GET params } args.Add("format", "vdf"); if (!string.IsNullOrEmpty(apiKey)) { args.Add("key", apiKey); } // append any args paramBuilder.Append(string.Join("&", args.Select(kvp => { // TODO: the WebAPI is a special snowflake that needs to appropriately handle url encoding // this is in contrast to the steam3 content server APIs which use an entirely different scheme of encoding string key = WebHelpers.UrlEncode(kvp.Key); string value = kvp.Value; // WebHelpers.UrlEncode( kvp.Value ); return(string.Format("{0}={1}", key, value)); }))); var task = Task.Factory.StartNew <KeyValue>(() => { byte[] data = null; if (isGet) { data = webClient.DownloadData(urlBuilder.ToString()); } else { byte[] postData = Encoding.Default.GetBytes(paramBuilder.ToString()); webClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded"); data = webClient.UploadData(urlBuilder.ToString(), postData); } KeyValue kv = new KeyValue(); using (var ms = new MemoryStream(data)) { try { kv.ReadAsText(ms); } catch (Exception ex) { throw new InvalidDataException( "An internal error occurred when attempting to parse the response from the WebAPI server. This can indicate a change in the VDF format.", ex ); } } return(kv); }); task.ContinueWith(t => { // we need to observe the exception in this OnlyOnFaulted continuation if our task throws an exception but we're not able to observe it // (such as when waiting for the task times out, and an exception is thrown later) // see: http://msdn.microsoft.com/en-us/library/dd997415.aspx DebugLog.WriteLine("WebAPI", "Threw an unobserved exception: {0}", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted); return(task); }
void HandleEncryptRequest( IPacketMsg packetMsg ) { var request = new Msg<MsgChannelEncryptRequest>( packetMsg ); var connectedUniverse = request.Body.Universe; var protoVersion = request.Body.ProtocolVersion; log.LogDebug( nameof(EnvelopeEncryptedConnection), "Got encryption request. Universe: {0} Protocol ver: {1}", connectedUniverse, protoVersion ); DebugLog.Assert( protoVersion == 1, nameof(EnvelopeEncryptedConnection), "Encryption handshake protocol version mismatch!" ); DebugLog.Assert( connectedUniverse == universe, nameof(EnvelopeEncryptedConnection), FormattableString.Invariant( $"Expected universe {universe} but server reported universe {connectedUniverse}" ) ); byte[]? randomChallenge; if ( request.Payload.Length >= 16 ) { randomChallenge = request.Payload.ToArray(); } else { randomChallenge = null; } var publicKey = KeyDictionary.GetPublicKey( connectedUniverse ); if ( publicKey == null ) { log.LogDebug( nameof(EnvelopeEncryptedConnection), "HandleEncryptRequest got request for invalid universe! Universe: {0} Protocol ver: {1}", connectedUniverse, protoVersion ); Disconnect( userInitiated: false ); return; } var response = new Msg<MsgChannelEncryptResponse>(); var tempSessionKey = CryptoHelper.GenerateRandomBlock( 32 ); byte[] encryptedHandshakeBlob; using ( var rsa = new RSACrypto( publicKey ) ) { if ( randomChallenge != null ) { var blobToEncrypt = new byte[ tempSessionKey.Length + randomChallenge.Length ]; Array.Copy( tempSessionKey, blobToEncrypt, tempSessionKey.Length ); Array.Copy( randomChallenge, 0, blobToEncrypt, tempSessionKey.Length, randomChallenge.Length ); encryptedHandshakeBlob = rsa.Encrypt( blobToEncrypt ); } else { encryptedHandshakeBlob = rsa.Encrypt( tempSessionKey ); } } var keyCrc = CryptoHelper.CRCHash( encryptedHandshakeBlob ); response.Write( encryptedHandshakeBlob ); response.Write( keyCrc ); response.Write( ( uint )0 ); if (randomChallenge != null) { encryption = new NetFilterEncryptionWithHMAC( tempSessionKey, log ); } else { encryption = new NetFilterEncryption( tempSessionKey, log ); } var serialized = response.Serialize(); try { debugNetworkListener?.OnOutgoingNetworkMessage( response.MsgType, serialized ); } catch ( Exception e ) { log.LogDebug( nameof( EnvelopeEncryptedConnection ), "DebugNetworkListener threw an exception: {0}", e ); } state = EncryptionState.Challenged; Send( serialized ); }
/// <summary> /// Fetches a list of content servers. /// </summary> /// <param name="csServer"> /// The optional Steam3 content server to fetch the list from. /// If this parameter is not specified, a random CS server will be selected. /// </param> /// <param name="cellId"> /// The optional CellID used to specify which regional servers should be returned in the list. /// If this parameter is not specified, Steam's GeoIP suggested CellID will be used instead. /// </param> /// <param name="maxServers">The maximum amount of servers to request.</param> /// <returns>A list of servers.</returns> /// <exception cref="System.InvalidOperationException"> /// No Steam CS servers available, or the suggested CellID is unavailable. /// Check that the <see cref="SteamClient"/> associated with this <see cref="CDNClient"/> instance is logged onto Steam. /// </exception> public List <Server> FetchServerList(IPEndPoint csServer = null, uint?cellId = null, int maxServers = 20) { DebugLog.Assert(steamClient.IsConnected, "CDNClient", "CMClient is not connected!"); DebugLog.Assert(steamClient.CellID != null, "CDNClient", "CMClient is not logged on!"); if (csServer == null) { // if we're not specifying what CS server we want to fetch a server list from, randomly select a cached CS server var csServers = steamClient.GetServersOfType(EServerType.CS); if (csServers.Count == 0) { // steamclient doesn't know about any CS servers yet throw new InvalidOperationException("No CS servers available!"); } Random random = new Random(); csServer = csServers[random.Next(csServers.Count)]; } if (cellId == null) { if (steamClient.CellID == null) { throw new InvalidOperationException("Recommended CellID is not available. CMClient not logged on?"); } // fallback to recommended cellid cellId = steamClient.CellID.Value; } KeyValue serverKv = DoCommand(csServer, "serverlist", args: string.Format("{0}/{1}/", cellId, maxServers)); var serverList = new List <Server>(maxServers); if (serverKv["deferred"].AsBoolean()) { return(serverList); } foreach (var server in serverKv.Children) { string type = server["type"].AsString(); string host = server["host"].AsString(); string[] hostSplits = host.Split(':'); int port = 80; if (hostSplits.Length > 1) { int parsedPort; if (int.TryParse(hostSplits[1], out parsedPort)) { port = parsedPort; } } uint serverCell = ( uint )server["cell"].AsInteger(); int load = server["load"].AsInteger(); int weightedLoad = server["weightedload"].AsInteger(); serverList.Add(new Server { Host = host, Port = port, Type = type, CellID = serverCell, Load = load, WeightedLoad = weightedLoad, }); } return(serverList); }
byte[] DoRawCommand(Server server, string command, string data = null, string method = WebRequestMethods.Http.Get, bool doAuth = false, string args = "", string authtoken = null) { string url = BuildCommand(server, command, args, authtoken); var webReq = HttpWebRequest.Create(url) as HttpWebRequest; webReq.Method = method; webReq.Pipelined = true; webReq.KeepAlive = true; if (doAuth && server.Type == "CS") { var req = Interlocked.Increment(ref reqCounter); byte[] shaHash; using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { var uri = new Uri(url); bw.Write(sessionId); bw.Write(req); bw.Write(sessionKey); bw.Write(Encoding.UTF8.GetBytes(uri.AbsolutePath)); shaHash = CryptoHelper.SHAHash(ms.ToArray()); } string hexHash = Utils.EncodeHexString(shaHash); string authHeader = string.Format("sessionid={0};req-counter={1};hash={2};", sessionId, req, hexHash); webReq.Headers["x-steam-auth"] = authHeader; } if (method == WebRequestMethods.Http.Post) { byte[] payload = Encoding.UTF8.GetBytes(data); webReq.ContentType = "application/x-www-form-urlencoded"; webReq.ContentLength = payload.Length; using (var reqStream = webReq.GetRequestStream()) { reqStream.Write(payload, 0, payload.Length); } } var result = webReq.BeginGetResponse(null, null); if (!result.AsyncWaitHandle.WaitOne(RequestTimeout)) { webReq.Abort(); } try { var response = webReq.EndGetResponse(result); using (var ms = new MemoryStream(( int )response.ContentLength)) { response.GetResponseStream().CopyTo(ms); response.Close(); return(ms.ToArray()); } } catch (Exception ex) { DebugLog.WriteLine("CDNClient", "Failed to complete web request to {0}: {1}", url, ex.Message); throw; } }
void HandleEncryptRequest(IPacketMsg packetMsg) { var encRequest = new Msg <MsgChannelEncryptRequest>(packetMsg); EUniverse eUniv = encRequest.Body.Universe; uint protoVersion = encRequest.Body.ProtocolVersion; DebugLog.WriteLine("UFSClient", "Got encryption request. Universe: {0} Protocol ver: {1}", eUniv, protoVersion); DebugLog.Assert(protoVersion == 1, "UFSClient", "Encryption handshake protocol version mismatch!"); byte[] randomChallenge; if (encRequest.Payload.Length >= 16) { randomChallenge = encRequest.Payload.ToArray(); } else { randomChallenge = null; } byte[] pubKey = KeyDictionary.GetPublicKey(eUniv); if (pubKey == null) { connection.Disconnect(); DebugLog.WriteLine("UFSClient", "HandleEncryptionRequest got request for invalid universe! Universe: {0} Protocol ver: {1}", eUniv, protoVersion); return; } ConnectedUniverse = eUniv; var encResp = new Msg <MsgChannelEncryptResponse>(); var tempSessionKey = CryptoHelper.GenerateRandomBlock(32); byte[] encryptedHandshakeBlob = null; using (var rsa = new RSACrypto(pubKey)) { if (randomChallenge != null) { var blobToEncrypt = new byte[tempSessionKey.Length + randomChallenge.Length]; Array.Copy(tempSessionKey, blobToEncrypt, tempSessionKey.Length); Array.Copy(randomChallenge, 0, blobToEncrypt, tempSessionKey.Length, randomChallenge.Length); encryptedHandshakeBlob = rsa.Encrypt(blobToEncrypt); } else { encryptedHandshakeBlob = rsa.Encrypt(tempSessionKey); } } var keyCrc = CryptoHelper.CRCHash(encryptedHandshakeBlob); encResp.Write(encryptedHandshakeBlob); encResp.Write(keyCrc); encResp.Write(( uint )0); if (randomChallenge != null) { pendingNetFilterEncryption = new NetFilterEncryptionWithHMAC(tempSessionKey); } else { pendingNetFilterEncryption = new NetFilterEncryption(tempSessionKey); } this.Send(encResp); }
// this is now a steamkit meme /// <summary> /// Nets the loop. /// </summary> void NetLoop() { // poll for readable data every 100ms const int POLL_MS = 100; while (!cancellationToken.IsCancellationRequested) { bool canRead = false; try { canRead = socket.Poll(POLL_MS * 1000, SelectMode.SelectRead); } catch (SocketException ex) { DebugLog.WriteLine("TcpConnection", "Socket exception while polling: {0}", ex); break; } if (!canRead) { // nothing to read yet continue; } byte[] packData = null; try { // read the packet off the network packData = ReadPacket(); // decrypt the data off the wire if needed if (netFilter != null) { packData = netFilter.ProcessIncoming(packData); } } catch (IOException ex) { DebugLog.WriteLine("TcpConnection", "Socket exception occurred while reading packet: {0}", ex); break; } try { OnNetMsgReceived(new NetMsgEventArgs(packData, destination)); } catch (Exception ex) { DebugLog.WriteLine("TcpConnection", "Unexpected exception propogated back to NetLoop: {0}", ex); } } // Thread is shutting down, ensure socket is shut down and disposed bool userShutdown = cancellationToken.IsCancellationRequested; if (userShutdown) { Shutdown(); } Release(userShutdown); }
/// <summary> /// Processes incoming packets, maintains connection consistency, and oversees outgoing packets. /// </summary> private void NetLoop() { // Variables that will be used deeper in the function; locating them here avoids recreating // them since they don't need to be. EndPoint packetSender = (EndPoint)new IPEndPoint(IPAddress.Any, 0); byte[] buf = new byte[2048]; timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY); nextResend = DateTime.Now.AddSeconds(RESEND_DELAY); // Begin by sending off the challenge request SendPacket(new UdpPacket(EUdpPacketType.ChallengeReq)); state = State.ChallengeReqSent; while (state != State.Disconnected) { try { // Wait up to 150ms for data, if none is found and the timeout is exceeded, we're done here. if (!sock.Poll(150000, SelectMode.SelectRead) && DateTime.Now > timeOut) { DebugLog.WriteLine("UdpConnection", "Connection timed out"); state = State.Disconnected; break; } // By using a 10ms wait, we allow for multiple packets sent at the time to all be processed before moving on // to processing output and therefore Acks (the more we process at the same time, the fewer acks we have to send) while (sock.Poll(10000, SelectMode.SelectRead)) { int length = sock.ReceiveFrom(buf, ref packetSender); // Ignore packets that aren't sent by the server we're connected to. if (!packetSender.Equals(remoteEndPoint)) continue; // Data from the desired server was received; delay timeout timeOut = DateTime.Now.AddSeconds(TIMEOUT_DELAY); MemoryStream ms = new MemoryStream(buf, 0, length); UdpPacket packet = new UdpPacket(ms); ReceivePacket(packet); } } catch (SocketException e) { DebugLog.WriteLine("UdpConnection", "Critical socket failure: " + e.ErrorCode); state = State.Disconnected; break; } // Send or resend any sequenced packets; a call to ReceivePacket can set our state to disconnected // so don't send anything we have queued in that case if (state != State.Disconnected) SendPendingMessages(); // If we received data but had no data to send back, we need to manually Ack (usually tags along with // outgoing data); also acks disconnections if (inSeq != inSeqAcked) SendAck(); // If a graceful shutdown has been requested, nothing in the outgoing queue is discarded. // Once it's empty, we exit, since the last packet was our disconnect notification. if (state == State.Disconnecting && outPackets.Count == 0) { DebugLog.WriteLine("UdpConnection", "Graceful disconnect completed"); state = State.Disconnected; } } DebugLog.WriteLine("UdpConnection", "Calling OnDisconnected"); OnDisconnected(EventArgs.Empty); }