refactoring

This commit is contained in:
Mikhail Thompson
2024-06-28 21:13:21 +03:00
parent c7250cdf24
commit 1229104086
10 changed files with 228 additions and 310 deletions

View File

@@ -1,4 +1,5 @@
using EmbedIO; using EmbedIO;
using nksrv.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -19,14 +20,14 @@ namespace nksrv.IntlServer
protected override async Task HandleAsync() protected override async Task HandleAsync()
{ {
Console.WriteLine("li-sg redirect in: " + Content); Console.WriteLine("li-sg redirect in: " + Content);
HttpClientHandler handler = new HttpClientHandler() HttpClientHandler handler = new()
{ {
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true, ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true,
AllowAutoRedirect = true // from gameassembly dll AllowAutoRedirect = true // from gameassembly dll
}; };
HttpClient client = new HttpClient(new LoggingHandler(handler)); HttpClient client = new(new LoggingHttpHandler(handler));
client.DefaultRequestHeaders client.DefaultRequestHeaders
.Accept .Accept
.Add(new MediaTypeWithQualityHeaderValue("*/*"));//ACCEPT header .Add(new MediaTypeWithQualityHeaderValue("*/*"));//ACCEPT header

View File

@@ -6,6 +6,7 @@ using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using EmbedIO; using EmbedIO;
using nksrv.Utils;
namespace nksrv.IntlServer namespace nksrv.IntlServer
{ {
@@ -19,14 +20,14 @@ namespace nksrv.IntlServer
protected override async Task HandleAsync() protected override async Task HandleAsync()
{ {
Console.WriteLine("AWS NA redirect in: " + Content); Console.WriteLine("AWS NA redirect in: " + Content);
HttpClientHandler handler = new HttpClientHandler() HttpClientHandler handler = new()
{ {
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true, ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true,
AllowAutoRedirect = true // from gameassembly dll AllowAutoRedirect = true // from gameassembly dll
}; };
HttpClient client = new HttpClient(new LoggingHandler(handler)); HttpClient client = new(new LoggingHttpHandler(handler));
client.DefaultRequestHeaders client.DefaultRequestHeaders
.Accept .Accept
.Add(new MediaTypeWithQualityHeaderValue("*/*"));//ACCEPT header .Add(new MediaTypeWithQualityHeaderValue("*/*"));//ACCEPT header
@@ -36,7 +37,7 @@ namespace nksrv.IntlServer
// client.DefaultRequestHeaders.Remove("User-agent"); // client.DefaultRequestHeaders.Remove("User-agent");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://50.18.221.30" + ctx.Request.RawUrl); HttpRequestMessage request = new(HttpMethod.Post, "https://50.18.221.30" + ctx.Request.RawUrl);
request.Version = HttpVersion.Version11; request.Version = HttpVersion.Version11;
request.Headers.TryAddWithoutValidation("Host", "aws-na.intlgame.com"); request.Headers.TryAddWithoutValidation("Host", "aws-na.intlgame.com");

View File

@@ -93,7 +93,7 @@ namespace nksrv.LobbyServer
{ {
if (ctx == null) if (ctx == null)
{ {
T msg2 = new T(); T msg2 = new();
msg2.MergeFrom(Contents); msg2.MergeFrom(Contents);
return msg2; return msg2;
} }
@@ -102,16 +102,8 @@ namespace nksrv.LobbyServer
var bin = await PacketDecryption.DecryptOrReturnContentAsync(ctx); var bin = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
// return grpc IMessage from byte array with type T // return grpc IMessage from byte array with type T
T msg = default(T); T msg = new();
try
{
msg = new T();
msg.MergeFrom(bin.Contents); msg.MergeFrom(bin.Contents);
}
catch
{
;
}
UserId = bin.UserId; UserId = bin.UserId;
UsedAuthToken = bin.UsedAuthToken; UsedAuthToken = bin.UsedAuthToken;
@@ -122,9 +114,7 @@ namespace nksrv.LobbyServer
public User GetUser() public User GetUser()
{ {
User? user = JsonDb.GetUser(UserId); return JsonDb.GetUser(UserId) ?? throw new Exception("null user");
if (user == null) throw new Exception("null user");
return user;
} }
} }
} }

View File

@@ -24,7 +24,7 @@ namespace nksrv.LobbyServer.Msgs
AllowAutoRedirect = true // from gameassembly dll AllowAutoRedirect = true // from gameassembly dll
}; };
HttpClient client = new HttpClient(new LoggingHandler(handler)); HttpClient client = new(new LoggingHttpHandler(handler));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream+protobuf")); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream+protobuf"));
client.BaseAddress = new Uri("https://global-lobby.nikke-kr.com"); client.BaseAddress = new Uri("https://global-lobby.nikke-kr.com");
client.DefaultRequestVersion = HttpVersion.Version20; client.DefaultRequestVersion = HttpVersion.Version20;
@@ -47,38 +47,21 @@ namespace nksrv.LobbyServer.Msgs
ctx.Response.OutputStream.Flush(); ctx.Response.OutputStream.Flush();
} }
public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx, bool decompress = false) public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx)
{ {
byte[] bin = Array.Empty<byte>();
using MemoryStream buffer = new MemoryStream(); using MemoryStream buffer = new MemoryStream();
var stream = ctx.Request.InputStream; var stream = ctx.Request.InputStream;
var encoding = ctx.Request.Headers[HttpHeaderNames.ContentEncoding]?.Trim(); var encoding = ctx.Request.Headers[HttpHeaderNames.ContentEncoding]?.Trim();
Stream decryptedStream = encoding switch
Stream decryptedStream;
switch (encoding)
{ {
case CompressionMethodNames.Gzip: CompressionMethodNames.Gzip => new GZipStream(stream, CompressionMode.Decompress),
decryptedStream = new GZipStream(stream, CompressionMode.Decompress); CompressionMethodNames.Deflate => new DeflateStream(stream, CompressionMode.Decompress),
break; CompressionMethodNames.None or null => stream,
case CompressionMethodNames.Deflate: "gzip,enc" => stream,
decryptedStream = new DeflateStream(stream, CompressionMode.Decompress); _ => throw HttpException.BadRequest($"Unsupported content encoding \"{encoding}\""),
break; };
case CompressionMethodNames.None:
case null:
decryptedStream = stream;
break;
case "gzip,enc":
decryptedStream = stream;
break;
default:
throw HttpException.BadRequest($"Unsupported content encoding \"{encoding}\"");
}
await stream.CopyToAsync(buffer, 81920, ctx.CancellationToken).ConfigureAwait(continueOnCapturedContext: false); await stream.CopyToAsync(buffer, 81920, ctx.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
return new PacketDecryptResponse() { Contents = buffer.ToArray() }; return new PacketDecryptResponse() { Contents = buffer.ToArray() };
} }

View File

@@ -24,7 +24,8 @@ namespace nksrv
{ {
internal class Program internal class Program
{ {
static async Task Main(string[] args) private static readonly HttpClient AssetDownloader = new();
static async Task Main()
{ {
Logger.UnregisterLogger<ConsoleLogger>(); Logger.UnregisterLogger<ConsoleLogger>();
Logger.RegisterLogger(new GreatLogger()); Logger.RegisterLogger(new GreatLogger());
@@ -32,12 +33,9 @@ namespace nksrv
LobbyHandler.Init(); LobbyHandler.Init();
// Start Webserver // Start Webserver
using (var server = CreateWebServer()) using var server = CreateWebServer();
{
await server.RunAsync(); await server.RunAsync();
} }
}
private static WebServer CreateWebServer() private static WebServer CreateWebServer()
{ {
var cert = new X509Certificate2(new X509Certificate(@"C:\Users\Misha\nkcert\site.pfx")); var cert = new X509Certificate2(new X509Certificate(@"C:\Users\Misha\nkcert\site.pfx"));
@@ -61,22 +59,21 @@ namespace nksrv
return server; return server;
} }
private static async Task HandleBatchRequests(IHttpContext ctx) private static async Task HandleBatchRequests(IHttpContext ctx)
{ {
var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx, true); var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
// this actually uses gzip compression, unlike other requests. // this actually uses gzip compression, unlike other requests.
using MemoryStream streamforparser = new MemoryStream(theBytes.Contents); using MemoryStream streamforparser = new(theBytes.Contents);
var content = new StreamContent(streamforparser); StreamContent content = new(streamforparser);
content.Headers.Remove("Content-Type"); content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", ctx.Request.Headers["Content-Type"]); content.Headers.TryAddWithoutValidation("Content-Type", ctx.Request.Headers["Content-Type"]);
// we have the form contents, // we have the form contents,
var multipart = await content.ReadAsMultipartAsync(); var multipart = await content.ReadAsMultipartAsync();
HttpClient cl = new HttpClient(); HttpClient cl = new();
// TODO: the server returns different boundary each time, looks like a GUID // TODO: the server returns different boundary each time, looks like a GUID
List<byte> response = [.. Encoding.UTF8.GetBytes("--f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\r\n")]; List<byte> response = [.. Encoding.UTF8.GetBytes("--f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\r\n")];
@@ -102,7 +99,7 @@ namespace nksrv
.. Encoding.UTF8.GetBytes($"\r\n"), .. Encoding.UTF8.GetBytes($"\r\n"),
.. res, .. res,
]; ];
response.AddRange(ResponseWithBytes.ToArray()); response.AddRange([.. ResponseWithBytes]);
} }
else else
{ {
@@ -128,150 +125,46 @@ namespace nksrv
ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\""; ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\"";
ctx.Response.OutputStream.Write(responseBytes); ctx.Response.OutputStream.Write(responseBytes);
} }
private static (string key, string value) GetHeader(string line)
{
var pieces = line.Split([':'], 2);
return (pieces[0].Trim(), pieces[1].Trim());
}
private static async Task<byte[]?> SendReqLocalAndReadResponseAsync(byte[] bytes)
{
int line = 0;
var bodyStartStr = Encoding.UTF8.GetString(bytes);
string method = "";
string url = "";
string httpVer = "";
string authToken = "";
List<NameValueHeaderValue> headers = new List<NameValueHeaderValue>();
int currentByte = 0;
foreach (var item in bodyStartStr.Split("\r\n"))
{
if (line == 0)
{
var parts = item.Split(" ");
method = parts[0];
url = parts[1];
httpVer = parts[2];
}
else if (item == null || string.IsNullOrEmpty(item))
{
currentByte += 2;
break;
}
else
{
var h = GetHeader(item);
headers.Add(new NameValueHeaderValue(h.key, h.value));
if (h.key == "Authorization")
{
authToken = h.value.Replace("Bearer ", "");
}
}
currentByte += (2 + item.Length);
line++;
}
byte[] body;
if (currentByte == bytes.Length)
{
// empty body
body = [];
}
else
{
// not empty body, TODO
File.WriteAllBytes("notemptybody", bytes);
body = bytes.Skip(currentByte).ToArray();
}
if (!url.StartsWith("/v1/"))
{
throw new NotImplementedException("handler for " + url + " not implemented");
}
url = url.Replace("/v1", "");
// find appropriate handler
Logger.Info("BATCH: /v1" + url);
foreach (var item in LobbyHandler.Handlers)
{
if (item.Key == url)
{
item.Value.Reset();
item.Value.Contents = body;
await item.Value.HandleAsync(authToken);
return item.Value.ReturnBytes;
}
}
Logger.Error("HANDLER NOT FOUND: " + url);
return null;
}
private static byte[] ReadStream(Stream stream)
{
byte[] data = new byte[1024];
List<byte> allData = new List<byte>();
do
{
int numBytesRead = stream.Read(data, 0, data.Length);
if (numBytesRead == data.Length)
{
allData.AddRange(data);
}
else if (numBytesRead > 0)
{
allData.AddRange(data.Take(numBytesRead));
}
else if (numBytesRead == 0)
{
break;
}
} while (true);
return allData.ToArray();
}
private static async Task HandleDataEndpoint(IHttpContext ctx) private static async Task HandleDataEndpoint(IHttpContext ctx)
{ {
// this endpoint does not appear to be needed, it is used for telemetry // this endpoint does not appear to be needed, it is used for telemetry
if (ctx.RequestedPath == "/v1/dsr/query") if (ctx.RequestedPath == "/v1/dsr/query")
{ {
WriteJsonString(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}"); await WriteJsonStringAsync(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}");
} }
else else
{ {
ctx.Response.StatusCode = 404; ctx.Response.StatusCode = 404;
} }
} }
private static HttpClient hs = new HttpClient();
private static async Task HandleAsset(IHttpContext ctx) private static async Task HandleAsset(IHttpContext ctx)
{ {
string fs = AppDomain.CurrentDomain.BaseDirectory + "cache" + ctx.RequestedPath; string targetFile = AppDomain.CurrentDomain.BaseDirectory + "cache" + ctx.RequestedPath;
Directory.CreateDirectory(Path.GetDirectoryName(fs)); var targetDir = Path.GetDirectoryName(targetFile);
if (!File.Exists(fs)) if (targetDir == null)
{ {
Logger.Info("Download " + fs); Logger.Error($"ERROR: Directory name cannot be null for request " + ctx.RequestedPath + ", file path is " + targetFile);
return;
}
Directory.CreateDirectory(targetDir);
if (!File.Exists(targetFile))
{
Logger.Info("Download " + targetFile);
// TODO: Ip might change // TODO: Ip might change
string @base = ctx.Request.RawUrl.StartsWith("/prdenv") ? "prdenv" : "media"; string @base = ctx.Request.RawUrl.StartsWith("/prdenv") ? "prdenv" : "media";
var requestUri = new Uri("https://43.132.66.200/" + @base + ctx.RequestedPath); var requestUri = new Uri("https://43.132.66.200/" + @base + ctx.RequestedPath);
using var request = new HttpRequestMessage(HttpMethod.Get, requestUri); using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.TryAddWithoutValidation("host", "cloud.nikke-kr.com"); request.Headers.TryAddWithoutValidation("host", "cloud.nikke-kr.com");
using var response = await hs.SendAsync(request); using var response = await AssetDownloader.SendAsync(request);
if (response.StatusCode == HttpStatusCode.OK) if (response.StatusCode == HttpStatusCode.OK)
{ {
using (var fss = new FileStream(fs, FileMode.CreateNew)) using var fss = new FileStream(targetFile, FileMode.CreateNew);
{ await response.Content.CopyToAsync(fss, ctx.CancellationToken);
await response.Content.CopyToAsync(fss);
fss.Close(); fss.Close();
} }
}
else else
{ {
Logger.Error("FAILED TO DOWNLOAD FILE: " + ctx.RequestedPath); Logger.Error("FAILED TO DOWNLOAD FILE: " + ctx.RequestedPath);
@@ -281,13 +174,8 @@ namespace nksrv
} }
try try
{ {
using var fss = new FileStream(targetFile, FileMode.Open, FileAccess.Read, FileShare.Read);
using var responseStream = ctx.OpenResponseStream();
using (var fss = new FileStream(fs, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var responseStream = ctx.OpenResponseStream())
{
if (ctx.RequestedPath.EndsWith(".mp4")) if (ctx.RequestedPath.EndsWith(".mp4"))
{ {
ctx.Response.ContentType = "video/mp4"; ctx.Response.ContentType = "video/mp4";
@@ -297,40 +185,17 @@ namespace nksrv
ctx.Response.ContentType = "application/json"; ctx.Response.ContentType = "application/json";
} }
ctx.Response.StatusCode = 200; ctx.Response.StatusCode = 200;
//ctx.Response.ContentLength64 = fss.Length; //ctx.Response.ContentLength64 = fss.Length; // TODO: This causes chrome to download content very slowl
await fss.CopyToAsync(responseStream, ctx.CancellationToken);
fss.CopyTo(responseStream);
fss.Close(); fss.Close();
} }
}
}
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error(ex.ToString()); Logger.Error(ex.ToString());
} }
} }
private static void WriteData<T>(IHttpContext ctx, T data, bool encrypted = false) where T : IMessage, new()
{
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/octet-stream+protobuf";
ctx.Response.ContentLength64 = data.CalculateSize();
var x = new CodedOutputStream(ctx.Response.OutputStream);
data.WriteTo(x);
x.Flush();
}
private static void WriteJsonString(IHttpContext ctx, string data)
{
var bt = Encoding.UTF8.GetBytes(data);
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/json";
ctx.Response.ContentLength64 = bt.Length;
ctx.Response.OutputStream.Write(bt, 0, bt.Length);
ctx.Response.OutputStream.Flush();
}
private static async Task HandleRouteData(IHttpContext ctx) private static async Task HandleRouteData(IHttpContext ctx)
{ {
if (ctx.RequestedPath.Contains("/route_config.json")) if (ctx.RequestedPath.Contains("/route_config.json"))
@@ -406,35 +271,96 @@ namespace nksrv
ctx.Response.StatusCode = 404; ctx.Response.StatusCode = 404;
} }
} }
private static async Task WriteJsonStringAsync(IHttpContext ctx, string data)
{
var bt = Encoding.UTF8.GetBytes(data);
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/json";
ctx.Response.ContentLength64 = bt.Length;
await ctx.Response.OutputStream.WriteAsync(bt, ctx.CancellationToken);
await ctx.Response.OutputStream.FlushAsync();
} }
public class LoggingHandler : DelegatingHandler private static (string key, string value) GetHeader(string line)
{ {
public LoggingHandler(HttpMessageHandler innerHandler) var pieces = line.Split([':'], 2);
: base(innerHandler)
return (pieces[0].Trim(), pieces[1].Trim());
}
private static async Task<byte[]?> SendReqLocalAndReadResponseAsync(byte[] bytes)
{ {
int line = 0;
var bodyStartStr = Encoding.UTF8.GetString(bytes);
string method;
string url = "";
string httpVer;
string authToken = "";
List<NameValueHeaderValue> headers = [];
int currentByte = 0;
foreach (var item in bodyStartStr.Split("\r\n"))
{
if (line == 0)
{
var parts = item.Split(" ");
method = parts[0];
url = parts[1];
httpVer = parts[2];
}
else if (item == null || string.IsNullOrEmpty(item))
{
currentByte += 2;
break;
}
else
{
var (key, value) = GetHeader(item);
headers.Add(new NameValueHeaderValue(key, value));
if (key == "Authorization")
{
authToken = value.Replace("Bearer ", "");
}
}
currentByte += 2 + item.Length;
line++;
}
byte[] body;
if (currentByte == bytes.Length)
{
// empty body
body = [];
}
else
{
// not empty body, TODO
File.WriteAllBytes("notemptybody", bytes);
body = bytes.Skip(currentByte).ToArray();
} }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) if (!url.StartsWith("/v1/"))
{ {
Console.WriteLine("Request:"); throw new NotImplementedException("handler for " + url + " not implemented");
Console.WriteLine(request.ToString()); }
if (request.Content != null)
url = url.Replace("/v1", "");
// find appropriate handler
Logger.Info("BATCH: /v1" + url);
foreach (var item in LobbyHandler.Handlers)
{ {
Console.WriteLine(await request.Content.ReadAsStringAsync()); if (item.Key == url)
}
Console.WriteLine();
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
{ {
Console.WriteLine(await response.Content.ReadAsStringAsync()); item.Value.Reset();
item.Value.Contents = body;
await item.Value.HandleAsync(authToken);
return item.Value.ReturnBytes;
} }
Console.WriteLine(); }
Logger.Error("HANDLER NOT FOUND: " + url);
return response; return null;
} }
} }
} }

View File

@@ -10,18 +10,15 @@ namespace nksrv.Utils
public class GreatLogger : ILogger public class GreatLogger : ILogger
{ {
public LogLevel LogLevel => LogLevel.Info; public LogLevel LogLevel => LogLevel.Info;
static readonly object lockObject = new();
public void Dispose()
{
}
static object lockObject = new object();
public void Log(LogMessageReceivedEventArgs logEvent) public void Log(LogMessageReceivedEventArgs logEvent)
{ {
var msg = logEvent.Message; var msg = logEvent.Message;
if (msg.StartsWith("["))
// strip out request id that embedio prints
if (msg.StartsWith('['))
{ {
msg = msg.Substring(msg.IndexOf("]") + 2); msg = msg[(msg.IndexOf("]") + 2)..];
} }
// ignore telemtry server errors // ignore telemtry server errors
@@ -42,31 +39,27 @@ namespace nksrv.Utils
} }
private ConsoleColor GetColorForMsg(LogMessageReceivedEventArgs logEvent) private static ConsoleColor GetColorForMsg(LogMessageReceivedEventArgs logEvent)
{ {
if (logEvent.Message.Contains("404 Not Found")) if (logEvent.Message.Contains("404 Not Found"))
return ConsoleColor.Red; return ConsoleColor.Red;
else if (logEvent.Message.Contains("200 OK")) else if (logEvent.Message.Contains("200 OK"))
return ConsoleColor.DarkGreen; return ConsoleColor.DarkGreen;
switch (logEvent.MessageType) return logEvent.MessageType switch
{ {
case LogLevel.None: LogLevel.None => ConsoleColor.White,
return ConsoleColor.White; LogLevel.Trace => ConsoleColor.Gray,
case LogLevel.Trace: LogLevel.Debug => ConsoleColor.Gray,
return ConsoleColor.Gray; LogLevel.Info => ConsoleColor.Gray,
case LogLevel.Debug: LogLevel.Warning => ConsoleColor.Yellow,
return ConsoleColor.Gray; LogLevel.Error => ConsoleColor.Red,
case LogLevel.Info: LogLevel.Fatal => ConsoleColor.Red,
return ConsoleColor.Gray; _ => ConsoleColor.White,
case LogLevel.Warning: };
return ConsoleColor.Yellow; }
case LogLevel.Error: public void Dispose()
return ConsoleColor.Red; {
case LogLevel.Fatal: GC.SuppressFinalize(this);
return ConsoleColor.Red;
default:
return ConsoleColor.White;
}
} }
} }
} }

View File

@@ -13,13 +13,13 @@ namespace nksrv.Utils
{ {
public class AccessToken public class AccessToken
{ {
public string Token; public string Token = "";
public long ExpirationTime; public long ExpirationTime;
public ulong UserID; public ulong UserID;
} }
public class FieldInfo public class FieldInfo
{ {
public List<NetFieldStageData> CompletedStages = new(); public List<NetFieldStageData> CompletedStages = [];
} }
public class Character public class Character
@@ -49,26 +49,26 @@ namespace nksrv.Utils
// Game data // Game data
public List<string> CompletedScenarios = new(); public List<string> CompletedScenarios = [];
public Dictionary<int, FieldInfo> FieldInfo = new(); public Dictionary<int, FieldInfo> FieldInfo = [];
public Dictionary<string, string> MapJson = new(); public Dictionary<string, string> MapJson = [];
public Dictionary<CurrencyType, long> Currency = new Dictionary<CurrencyType, long>() { public Dictionary<CurrencyType, long> Currency = new() {
{ CurrencyType.ContentStamina, 2 }, { CurrencyType.ContentStamina, 2 },
{ CurrencyType.CharPremiumTicket, 23422 } { CurrencyType.CharPremiumTicket, 23422 }
}; };
public List<Character> Characters = new(); public List<Character> Characters = [];
public NetWholeUserTeamData TeamData = new(); public NetWholeUserTeamData TeamData = new();
public List<int> ClearedTutorials = new(); public List<int> ClearedTutorials = [];
} }
public class CoreInfo public class CoreInfo
{ {
public List<User> Users = new List<User>(); public List<User> Users = [];
public List<AccessToken> LauncherAccessTokens = new List<AccessToken>(); public List<AccessToken> LauncherAccessTokens = [];
public Dictionary<string, GameClientInfo> GameClientTokens = new Dictionary<string, GameClientInfo>(); public Dictionary<string, GameClientInfo> GameClientTokens = [];
} }
internal class JsonDb internal class JsonDb
{ {

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.Utils
{
public class LoggingHttpHandler(HttpMessageHandler innerHandler) : DelegatingHandler(innerHandler)
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("Request:");
Console.WriteLine(request.ToString());
if (request.Content != null)
{
Console.WriteLine(await request.Content.ReadAsStringAsync(cancellationToken));
}
Console.WriteLine();
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
{
Console.WriteLine(await response.Content.ReadAsStringAsync(cancellationToken));
}
Console.WriteLine();
return response;
}
}
}

View File

@@ -15,11 +15,11 @@ namespace nksrv.Utils
{ {
public class PacketDecryption public class PacketDecryption
{ {
public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx, bool decompress = false) public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx)
{ {
byte[] bin = Array.Empty<byte>(); byte[] bin = [];
using MemoryStream buffer = new MemoryStream(); using MemoryStream buffer = new();
var stream = ctx.Request.InputStream; var stream = ctx.Request.InputStream;
@@ -46,20 +46,15 @@ namespace nksrv.Utils
var decryptionToken = CBorReadString(stream); var decryptionToken = CBorReadString(stream);
var nonce = CBorReadByteString(stream); var nonce = CBorReadByteString(stream);
MemoryStream encryptedBytes = new MemoryStream(); MemoryStream encryptedBytes = new();
stream.CopyTo(encryptedBytes); stream.CopyTo(encryptedBytes);
var bytes = encryptedBytes.ToArray(); var bytes = encryptedBytes.ToArray();
var key = LobbyHandler.GetInfo(decryptionToken); var key = LobbyHandler.GetInfo(decryptionToken) ?? throw HttpException.BadRequest("Invalid decryption token");
if (key == null)
{
throw HttpException.BadRequest("Invalid decryption token");
}
var additionalData = GenerateAdditionalData(decryptionToken, false); var additionalData = GenerateAdditionalData(decryptionToken, false);
var x = SecretAeadXChaCha20Poly1305.Decrypt(bytes, nonce, key.Keys.ReadSharedSecret, additionalData.ToArray()); var x = SecretAeadXChaCha20Poly1305.Decrypt(bytes, nonce, key.Keys.ReadSharedSecret, [.. additionalData]);
var ms = new MemoryStream(x); var ms = new MemoryStream(x);
// File.WriteAllBytes("fullPkt-decr", ms.ToArray()); // File.WriteAllBytes("fullPkt-decr", ms.ToArray());
@@ -78,7 +73,7 @@ namespace nksrv.Utils
//File.WriteAllBytes("contentsgzip", contents); //File.WriteAllBytes("contentsgzip", contents);
// gzip compression is used // gzip compression is used
using Stream csStream = new GZipStream(new MemoryStream(contents), CompressionMode.Decompress); using Stream csStream = new GZipStream(new MemoryStream(contents), CompressionMode.Decompress);
using MemoryStream decoded = new MemoryStream(); using MemoryStream decoded = new();
csStream.CopyTo(decoded); csStream.CopyTo(decoded);
contents = decoded.ToArray(); contents = decoded.ToArray();
@@ -159,13 +154,8 @@ namespace nksrv.Utils
public static byte[] EncryptData(byte[] message, string authToken) public static byte[] EncryptData(byte[] message, string authToken)
{ {
var key = LobbyHandler.GetInfo(authToken); var key = LobbyHandler.GetInfo(authToken) ?? throw HttpException.BadRequest("Invalid decryption token");
if (key == null) MemoryStream m = new();
{
throw HttpException.BadRequest("Invalid decryption token");
}
MemoryStream m = new MemoryStream();
m.WriteByte(89); // cbor ushort m.WriteByte(89); // cbor ushort
@@ -205,13 +195,13 @@ namespace nksrv.Utils
var additionalData = GenerateAdditionalData(authToken, true); var additionalData = GenerateAdditionalData(authToken, true);
// prep payload // prep payload
MemoryStream msm = new MemoryStream(); MemoryStream msm = new();
msm.WriteByte(88); msm.WriteByte(88);
msm.WriteByte(0); msm.WriteByte(0);
msm.Write(message); msm.Write(message);
var encryptedBytes = SecretAeadXChaCha20Poly1305.Encrypt(msm.ToArray(), nonce, key.Keys.TransferSharedSecret, additionalData.ToArray()); var encryptedBytes = SecretAeadXChaCha20Poly1305.Encrypt(msm.ToArray(), nonce, key.Keys.TransferSharedSecret, [.. additionalData]);
// write encrypted data // write encrypted data
m.Write(encryptedBytes); m.Write(encryptedBytes);
@@ -293,16 +283,18 @@ namespace nksrv.Utils
{ {
var b = s.ReadByte(); var b = s.ReadByte();
var type = b & 0x1f; var type = b & 0x1f;
var res = new CBorItem(); CBorItem res = new()
res.MajorType = (b >> 5) & 7; {
MajorType = (b >> 5) & 7
};
switch (type) switch (type)
{ {
case 24: case 24:
// byte // byte
res.ByteValue = new byte[] { (byte)s.ReadByte() }; res.ByteValue = [(byte)s.ReadByte()];
res.type = CBorItemType.Byte; res.type = CBorItemType.Byte;
res.FullValue = (int)res.ByteValue[0]; res.FullValue = res.ByteValue[0];
break; break;
case 25: case 25:
byte[] arr = new byte[2]; byte[] arr = new byte[2];
@@ -331,9 +323,7 @@ namespace nksrv.Utils
while (i != buf.Length) while (i != buf.Length)
{ {
if (i > buf.Length) if (i > buf.Length)
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException(nameof(buf));
var pos = buf.Length - i;
var read = s.Read(buf, i, buf.Length - i); var read = s.Read(buf, i, buf.Length - i);
if (read == 0) if (read == 0)
break; break;
@@ -349,13 +339,13 @@ namespace nksrv.Utils
public class PacketDecryptResponse public class PacketDecryptResponse
{ {
public ulong UserId; public ulong UserId;
public string UsedAuthToken; public string UsedAuthToken = "";
public byte[] Contents; public byte[] Contents = [];
} }
public class CBorItem public class CBorItem
{ {
public CBorItemType type; public CBorItemType type;
public byte[] ByteValue; public byte[] ByteValue = [];
public ushort UShortValue; public ushort UShortValue;
public int MajorType; public int MajorType;