/** * Proxies any request made to this web server to the clientconfig service. Rewrites the response * to have any chat servers point to localhost at the specified port. */ private async Task ProxyAndRewriteResponse(string configUrl, int chatPort, IHttpContext ctx) { var url = configUrl + ctx.Request.RawUrl; using var message = new HttpRequestMessage(HttpMethod.Get, url); // Cloudflare bitches at us without a user agent. message.Headers.TryAddWithoutValidation("User-Agent", ctx.Request.Headers["user-agent"]); // Add authorization headers for player config. if (ctx.Request.Headers["x-riot-entitlements-jwt"] != null) { message.Headers.TryAddWithoutValidation("X-Riot-Entitlements-JWT", ctx.Request.Headers["x-riot-entitlements-jwt"]); } if (ctx.Request.Headers["authorization"] != null) { message.Headers.TryAddWithoutValidation("Authorization", ctx.Request.Headers["authorization"]); } var result = await _client.SendAsync(message); var content = await result.Content.ReadAsStringAsync(); var modifiedContent = content; Trace.WriteLine("ORIGINAL CLIENTCONFIG: " + content); try { var configObject = (JsonObject)SimpleJson.DeserializeObject(content); string riotChatHost = null; var riotChatPort = 0; // Set fallback host to localhost. if (configObject.ContainsKey("chat.host")) { // Save fallback host riotChatHost = configObject["chat.host"].ToString(); configObject["chat.host"] = "127.0.0.1"; } // Set chat port. if (configObject.ContainsKey("chat.port")) { riotChatPort = int.Parse(configObject["chat.port"].ToString()); configObject["chat.port"] = chatPort; } // Set chat.affinities (a dictionary) to all localhost. if (configObject.ContainsKey("chat.affinities")) { var affinities = (JsonObject)configObject["chat.affinities"]; if ((bool)configObject["chat.affinity.enabled"]) { var pasRequest = new HttpRequestMessage(HttpMethod.Get, "https://riot-geo.pas.si.riotgames.com/pas/v1/service/chat"); pasRequest.Headers.TryAddWithoutValidation("Authorization", ctx.Request.Headers["authorization"]); var pasJwt = await(await _client.SendAsync(pasRequest)).Content.ReadAsStringAsync(); Trace.WriteLine("PAS TOKEN:" + pasJwt); var affinity = new JsonWebToken(pasJwt).GetPayloadValue <string>("affinity"); // replace fallback host with host by player affinity riotChatHost = affinities[affinity] as string; } foreach (var key in new List <string>(affinities.Keys)) // clone to prevent concurrent modification { affinities[key] = "127.0.0.1"; } } // Allow an invalid cert. if (configObject.ContainsKey("chat.allow_bad_cert.enabled")) { configObject["chat.allow_bad_cert.enabled"] = true; } modifiedContent = SimpleJson.SerializeObject(configObject); Trace.WriteLine("MODIFIED CLIENTCONFIG: " + modifiedContent); if (riotChatHost != null && riotChatPort != 0) { PatchedChatServer?.Invoke(this, new ChatServerEventArgs { ChatHost = riotChatHost, ChatPort = riotChatPort }); } } catch (Exception ex) { Trace.WriteLine(ex); // Show a message instead of failing silently. MessageBox.Show( "Deceive was unable to rewrite a League of Legends configuration file. This normally happens because Riot changed something on their end. " + "Please check if there's a new version of Deceive available, or contact the creator through GitHub (https://github.com/molenzwiebel/deceive) or Discord if there's not.\n\n" + ex, StartupHandler.DeceiveTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1 ); Application.Exit(); } // Using the builtin EmbedIO methods for sending the response adds some garbage in the front of it. // This seems to do the trick. var responseBytes = Encoding.UTF8.GetBytes(modifiedContent); ctx.Response.StatusCode = (int)result.StatusCode; ctx.Response.SendChunked = false; ctx.Response.ContentLength64 = responseBytes.Length; ctx.Response.ContentType = "application/json"; await ctx.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); ctx.Response.OutputStream.Close(); }
/** * Proxies any request made to this web server to the clientconfig service. Rewrites the response * to have any chat servers point to localhost at the specified port. */ private async Task ProxyAndRewriteResponse(string configUrl, int chatPort, IHttpContext ctx) { var url = configUrl + ctx.Request.RawUrl; using (var message = new HttpRequestMessage(HttpMethod.Get, url)) { // Cloudflare bitches at us without a user agent. message.Headers.TryAddWithoutValidation("User-Agent", ctx.Request.Headers["user-agent"]); // Add authorization headers for player config. if (ctx.Request.Headers["x-riot-entitlements-jwt"] != null) { message.Headers.TryAddWithoutValidation("X-Riot-Entitlements-JWT", ctx.Request.Headers["x-riot-entitlements-jwt"]); } if (ctx.Request.Headers["authorization"] != null) { message.Headers.TryAddWithoutValidation("Authorization", ctx.Request.Headers["authorization"]); } var result = await _client.SendAsync(message); var content = await result.Content.ReadAsStringAsync(); var modifiedContent = content; Debug.WriteLine(content); try { var configObject = JObject.Parse(content); string riotChatHost = null; var riotChatPort = 0; // Set fallback host to localhost. if (configObject.ContainsKey("chat.host")) { // Save fallback host riotChatHost = configObject["chat.host"].ToString(); configObject["chat.host"] = "127.0.0.1"; } // Set chat port. if (configObject.ContainsKey("chat.port")) { riotChatPort = int.Parse(configObject["chat.port"].ToString()); configObject["chat.port"] = chatPort; } // Set chat.affinities (a dictionary) to all localhost. if (configObject.ContainsKey("chat.affinities")) { var affinities = (JObject)configObject["chat.affinities"]; if ((bool)configObject["chat.affinity.enabled"]) { var pasRequest = new HttpRequestMessage(HttpMethod.Get, "https://pas.geo.si.riotgames.com/pas/v1/service/chat"); pasRequest.Headers.TryAddWithoutValidation("Authorization", ctx.Request.Headers["authorization"]); var pasJwt = await(await _client.SendAsync(pasRequest)).Content.ReadAsStringAsync(); Debug.WriteLine(pasJwt); var affinity = new JsonWebToken(pasJwt).GetPayloadValue <string>("affinity"); // replace fallback host with host by player affinity riotChatHost = affinities[affinity].ToString(); } foreach (var key in affinities.Properties()) // clone to prevent concurrent modification { affinities[key.Name] = "127.0.0.1"; } } // Allow an invalid cert. if (configObject.ContainsKey("chat.allow_bad_cert.enabled")) { configObject["chat.allow_bad_cert.enabled"] = true; } modifiedContent = JsonConvert.SerializeObject(configObject); if (riotChatHost != null && riotChatPort != 0) { PatchedChatServer?.Invoke(this, new ChatServerEventArgs { ChatHost = riotChatHost, ChatPort = riotChatPort }); } } catch (Exception ex) { Trace.WriteLine(ex); Logger.Error(ex.Message); } // Using the builtin EmbedIO methods for sending the response adds some garbage in the front of it. // This seems to do the trick. var responseBytes = Encoding.UTF8.GetBytes(modifiedContent); ctx.Response.StatusCode = (int)result.StatusCode; ctx.Response.SendChunked = false; ctx.Response.ContentLength64 = responseBytes.Length; ctx.Response.ContentType = "application/json"; await ctx.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); ctx.Response.OutputStream.Close(); } }