/// <summary> /// Connects this instance to the server. /// </summary> /// <returns><c>true</c> if the connection was a success; otherwise, <c>false</c>.</returns> public bool Connect() { byte[] encryptedKey = null; // TODO: handle other universes? byte[] universeKey = KeyDictionary.GetPublicKey(EUniverse.Public); using (var rsa = new RSACrypto(universeKey)) { encryptedKey = rsa.Encrypt(sessionKey); } byte[] encryptedTicket = CryptoHelper.SymmetricEncrypt(appTicket, sessionKey); string payload = String.Format("sessionkey={0}&appticket={1}", WebHelpers.UrlEncode(encryptedKey), WebHelpers.UrlEncode(encryptedTicket)); webClient.Headers.Clear(); webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); string response; try { response = webClient.UploadString(BuildCommand(endPoint, "initsession"), payload); } catch (WebException) { return(false); } var responsekv = KeyValue.LoadFromString(response); var sessionidn = responsekv.Children.Where(c => c.Name == "sessionid").First(); var reqcountern = responsekv.Children.Where(c => c.Name == "req-counter").First(); sessionID = (ulong)(sessionidn.AsLong(0)); reqcounter = reqcountern.AsLong(0); try { AuthDepot(); } catch (WebException) { return(false); } return(true); }
/// <summary> /// Connects and authenticates to the specified content server. /// </summary> /// <param name="csServer">The content server to connect to.</param> /// <exception cref="System.ArgumentNullException">csServer was null.</exception> public void Connect(Server csServer) { DebugLog.Assert(steamClient.IsConnected, "CDNClient", "CMClient is not connected!"); if (csServer == null) { throw new ArgumentNullException("csServer"); } byte[] pubKey = KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse); sessionKey = CryptoHelper.GenerateRandomBlock(32); byte[] cryptedSessKey = null; using (var rsa = new RSACrypto(pubKey)) { cryptedSessKey = rsa.Encrypt(sessionKey); } string data; if (appTicket == null) { // no appticket, doing anonymous connection data = string.Format("sessionkey={0}&anonymoususer=1&steamid={1}", WebHelpers.UrlEncode(cryptedSessKey), steamClient.SteamID.ConvertToUInt64()); } else { byte[] encryptedAppTicket = CryptoHelper.SymmetricEncrypt(appTicket, sessionKey); data = string.Format("sessionkey={0}&appticket={1}", WebHelpers.UrlEncode(cryptedSessKey), WebHelpers.UrlEncode(encryptedAppTicket)); } KeyValue initKv = DoCommand(csServer, "initsession", data, WebRequestMethods.Http.Post); sessionId = ( ulong )initKv["sessionid"].AsLong(); reqCounter = initKv["req-counter"].AsLong(); if (appTicket == null) { data = string.Format("depotid={0}", depotId); } else { byte[] encryptedAppTicket = CryptoHelper.SymmetricEncrypt(appTicket, sessionKey); data = string.Format("appticket={0}", WebHelpers.UrlEncode(encryptedAppTicket)); } DoCommand(csServer, "authdepot", data, WebRequestMethods.Http.Post, true); connectedServer = csServer; }
/// <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); }
/// <summary> /// Connects and initializes a session to the specified content server. /// </summary> /// <param name="csServer">The content server to connect to.</param> /// <exception cref="System.ArgumentNullException">csServer was null.</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 ConnectAsync(Server csServer) { DebugLog.Assert(steamClient.IsConnected, "CDNClient", "CMClient is not connected!"); if (csServer == null) { throw new ArgumentNullException(nameof(csServer)); } // Nothing needs to be done to initialize a session to a CDN server if (csServer.Type == "CDN") { connectedServer = csServer; return; } byte[] pubKey = KeyDictionary.GetPublicKey(steamClient.Universe); sessionKey = CryptoHelper.GenerateRandomBlock(32); byte[] cryptedSessKey = null; using (var rsa = new RSACrypto(pubKey)) { cryptedSessKey = rsa.Encrypt(sessionKey); } string data; if (appTicket == null) { // no appticket, doing anonymous connection data = string.Format("sessionkey={0}&anonymoususer=1&steamid={1}", WebHelpers.UrlEncode(cryptedSessKey), steamClient.SteamID.ConvertToUInt64()); } else { byte[] encryptedAppTicket = CryptoHelper.SymmetricEncrypt(appTicket, sessionKey); data = string.Format("sessionkey={0}&appticket={1}", WebHelpers.UrlEncode(cryptedSessKey), WebHelpers.UrlEncode(encryptedAppTicket)); } var initKv = await DoCommandAsync(csServer, HttpMethod.Post, "initsession", data).ConfigureAwait(false); sessionId = initKv["sessionid"].AsUnsignedLong(); reqCounter = initKv["req-counter"].AsLong(); connectedServer = csServer; }
/// <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="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 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)); }))); 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); }
async Task <KeyValue> CallAsyncCore(HttpMethod method, string func, int version = 1, Dictionary <string, string> args = null) { if (method == null) { throw new ArgumentNullException(nameof(method)); } if (func == null) { throw new ArgumentNullException(nameof(func)); } if (args == null) { args = new Dictionary <string, string>(); } var urlBuilder = new StringBuilder(); var paramBuilder = new StringBuilder(); urlBuilder.AppendFormat("{0}/{1}/v{2}", iface, func, version); var isGet = HttpMethod.Get.Equals(method); 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 request = new HttpRequestMessage(method, urlBuilder.ToString()); if (!isGet) { request.Content = new StringContent(paramBuilder.ToString()); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); } var response = await httpClient.SendAsync(request).ConfigureAwait(false); var kv = new KeyValue(); using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { try { kv.ReadAsText(stream); } 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); }