Represents the root debug logging functionality.
Esempio n. 1
0
        public NetFilterEncryption(byte[] sessionKey)
        {
            DebugLog.Assert(sessionKey.Length == 32, "NetFilterEncryption", "AES session key was not 32 bytes!");

            this.sessionKey = sessionKey;
        }
Esempio n. 2
0
            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());
                }
            }
Esempio n. 3
0
        /// <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;
            }
        }
Esempio n. 4
0
        // 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));
            }
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
        /// <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));
        }
Esempio n. 7
0
        /// <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);
        }
Esempio n. 8
0
        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;
                }
            }
        }
Esempio n. 9
0
            /// <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);
            }
Esempio n. 10
0
        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 );
        }
Esempio n. 11
0
        /// <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);
        }
Esempio n. 12
0
        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;
            }
        }
Esempio n. 13
0
        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);
        }
Esempio n. 14
0
        // 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);
        }
Esempio n. 15
0
        /// <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);
        }