diff --git a/VirtualDDNSRouter.Server/Interfaces/IYamlParser.cs b/VirtualDDNSRouter.Server/Interfaces/IYamlParser.cs new file mode 100644 index 0000000..646d177 --- /dev/null +++ b/VirtualDDNSRouter.Server/Interfaces/IYamlParser.cs @@ -0,0 +1,6 @@ +namespace VirtualDDNSRouter.Server.Interfaces; + +public interface IYamlParser +{ + Task> GetRules(); +} \ No newline at end of file diff --git a/VirtualDDNSRouter.Server/Models/Rule.cs b/VirtualDDNSRouter.Server/Models/Rule.cs new file mode 100644 index 0000000..74e3810 --- /dev/null +++ b/VirtualDDNSRouter.Server/Models/Rule.cs @@ -0,0 +1,24 @@ +using System.Net; +using YamlDotNet.Serialization; + +namespace VirtualDDNSRouter.Server.Models; + +public record Rule +{ + public string name { get; init; } = string.Empty; + public string apiKey { get; init; } = string.Empty; + public string path { get; init; } = string.Empty; + + public Rule() { } // Needed for AOT static deserializer +} + + +public record Route +{ + public string path { get; set; } = string.Empty; + public IPAddress ipAddress { get; set; } = IPAddress.None; + + public UInt16 port { get; set; } = 80; + + public Route() { } +} \ No newline at end of file diff --git a/VirtualDDNSRouter.Server/Program.cs b/VirtualDDNSRouter.Server/Program.cs index 0c18261..f977cb5 100644 --- a/VirtualDDNSRouter.Server/Program.cs +++ b/VirtualDDNSRouter.Server/Program.cs @@ -1,5 +1,10 @@ using System.Text.Json.Serialization; using Microsoft.AspNetCore.Http.HttpResults; +using VirtualDDNSRouter.Server.Interfaces; +using VirtualDDNSRouter.Server.Models; +using VirtualDDNSRouter.Server.Services; +using Route = VirtualDDNSRouter.Server.Models.Route; + var builder = WebApplication.CreateSlimBuilder(args); @@ -8,6 +13,8 @@ builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); }); +builder.Services.AddSingleton(); + // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); @@ -18,12 +25,42 @@ if (app.Environment.IsDevelopment()) app.MapOpenApi(); } +List routes = new List(); +app.MapPut("/setip/{path}/{port}/{apiKey}", async (IYamlParser yamlParser ,HttpContext context, string path, UInt16 port, string apiKey) => +{ + var rules = await yamlParser.GetRules(); + var ruleValid = rules.Any(r => r.path == path && r.apiKey == apiKey); + if (!ruleValid) + { + return Results.StatusCode(StatusCodes.Status403Forbidden); + } + var clientIp = context.Connection.RemoteIpAddress; + if (clientIp is null) return Results.BadRequest("Could not get the client ip address"); + routes.Add(new Route + { + ipAddress = clientIp, + path = path, + port = port + }); + return Results.Created(); +}); + +app.MapGet("/goto/{path}", async (string path) => +{ + var ruleExists = routes.Any(r => r.path == path); + if (!ruleExists) Results.NoContent(); + var redirectRoute = routes.FirstOrDefault(r => r.path == path); + return Results.Redirect($"http://{redirectRoute.ipAddress}:{redirectRoute.port}"); + +}); app.Run(); -[JsonSerializable(typeof(Object))] + +//[JsonSerializable(typeof(Rule))] +[JsonSerializable(typeof(Route))] internal partial class AppJsonSerializerContext : JsonSerializerContext { } \ No newline at end of file diff --git a/VirtualDDNSRouter.Server/Services/YamlParser.cs b/VirtualDDNSRouter.Server/Services/YamlParser.cs new file mode 100644 index 0000000..c06d77b --- /dev/null +++ b/VirtualDDNSRouter.Server/Services/YamlParser.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; +using VirtualDDNSRouter.Server.Interfaces; +using VirtualDDNSRouter.Server.Models; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace VirtualDDNSRouter.Server.Services; + +public class YamlParser : IYamlParser +{ + private readonly string _yamlFilePath = "rules.yaml"; + + public async Task> GetRules() + { + if (!File.Exists(_yamlFilePath)) + throw new FileNotFoundException($"YAML file not found: {_yamlFilePath}"); + + var yamlContent = await File.ReadAllTextAsync(_yamlFilePath).ConfigureAwait(false); + + // Build the deserializer with explicit naming convention + var deserializer = new DeserializerBuilder() + .WithNamingConvention(UnderscoredNamingConvention.Instance) // maps api_key -> apiKey + .IgnoreUnmatchedProperties() + .Build(); + + // Deserialize into a list of Rule + var rules = deserializer.Deserialize>(yamlContent); + + return rules ?? new List(); + } +} \ No newline at end of file diff --git a/VirtualDDNSRouter.Server/VirtualDDNSRouter.Server.csproj b/VirtualDDNSRouter.Server/VirtualDDNSRouter.Server.csproj index 7913330..aaefb7e 100644 --- a/VirtualDDNSRouter.Server/VirtualDDNSRouter.Server.csproj +++ b/VirtualDDNSRouter.Server/VirtualDDNSRouter.Server.csproj @@ -12,6 +12,7 @@ + diff --git a/VirtualDDNSRouter.Server/rules.example.yaml b/VirtualDDNSRouter.Server/rules.example.yaml new file mode 100644 index 0000000..ff39820 --- /dev/null +++ b/VirtualDDNSRouter.Server/rules.example.yaml @@ -0,0 +1,8 @@ +Rules: + - Name: "B" + ApiKey: "abc123XYZ" + Route: "B" + + - Name: "A" + ApiKey: "def456ABC" + Route: "A"