Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e5d0ec6
HttpListener start listening before GetContextAsync
babloo-singh Mar 5, 2024
63a26b0
Documentation updates, prep for .NET SDK 8
stephbu Nov 16, 2025
99d0c62
Planning updates
stephbu Nov 16, 2025
84a3e42
.NET 8 SDK update, any CPU.
stephbu Nov 16, 2025
cc41c6a
Fixed CA2241 warning
stephbu Nov 16, 2025
c0d6746
Fix issue #26, added improved test suite for circular loops
stephbu Nov 16, 2025
9d70f8a
Tree cleanup
stephbu Nov 16, 2025
3010037
Task list updates
stephbu Nov 16, 2025
a97b7d8
Task 2 planning
stephbu Nov 16, 2025
9236ce6
Bug planning
stephbu Nov 16, 2025
6c46c20
Fixes an endian bug when IPv4 addresses are reconstructed from the wire
stephbu Nov 16, 2025
d7caf2c
Merge pull request #27 from babloo-singh/babloo/httplisteners-start
stephbu Nov 16, 2025
dce4121
Code Reformatting
stephbu Nov 17, 2025
c4412da
T02 - Authoritative response verification suite
stephbu Nov 17, 2025
b10c222
Updated VSCode tasks for .NET 8
stephbu Nov 17, 2025
5c8a621
T22 Github Action - Continuous Integration
stephbu Nov 17, 2025
c7cda39
Github CI Badge
stephbu Nov 17, 2025
687e73d
Workflow does not contain permissions
stephbu Nov 17, 2025
364e41d
Log configured port
stephbu Nov 17, 2025
755fc54
T24 UDP Listener shutdown stability
stephbu Nov 17, 2025
3904e34
Github action branch names
stephbu Nov 17, 2025
d34741a
Linux Case-Sensitivity
stephbu Nov 17, 2025
d03f4ad
Action fix
stephbu Nov 17, 2025
bfa44b8
README CI badge
stephbu Nov 17, 2025
d050ba7
DIG example in README
stephbu Nov 17, 2025
32b739f
DIG example in README
stephbu Nov 17, 2025
63a5e6f
Task T06 - Ninject to MS.DI migration
stephbu Nov 17, 2025
f47ea13
Ignore documentation updates
stephbu Nov 17, 2025
71d835c
BIND Zone Provider, tests, documentation and task updates
stephbu Nov 17, 2025
9bad90d
Documentation update
stephbu Nov 17, 2025
71061f4
README update
stephbu Nov 17, 2025
498434f
Add IPv6 Tasks
stephbu Nov 17, 2025
8ff70fe
Merge remote-tracking branch 'upstream/master' into feature/merge-ups…
MBeijer Nov 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Dns.Cli/dns.db*
*.suo
*.user
*.sln.docstates
*.orig

# Build results

Expand Down
85 changes: 85 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# AGENTS GUIDE

> Scope: assistants may edit **code, tests, and documentation** in this repository. Infrastructure/deployment assets remain off-limits unless explicitly approved.

## 1. Mission
- Maintain and extend the `csharp-dns-server` so it becomes a production-ready DNS service with rich testing, observability, and zone-provider capabilities.
- Follow the roadmap in `docs/product_requirements.md`, respect the priority tiers in `docs/priorities.md` (P0 reliability/protocol accuracy, P1 security & maintenance, P2 features), and focus on open GitHub issues aligned with those tiers.
- Reference the prioritized backlog in `docs/task_list.md` when picking up work to stay aligned with near-term goals.

## 2. Repository Orientation
| Project | Purpose | Notes |
|---------|---------|-------|
| `Dns/` | Core library implementing DNS protocol, server loop, zone providers, HTTP status surface. | Entry point: `Dns/Program.cs`. Zone providers under `Dns/ZoneProvider`. |
| `dns-cli/` | Console host that runs the DNS server for local testing. | Mirrors `Dns/appsettings.json`. |
| `dnstest/` | xUnit suite covering protocol/utility components. | Expand here before adding new test assemblies. |
| `docs/` | Specs, PRD and future design docs. | `docs/product_requirements.md` drives priorities. |

Key classes & files:
- `Dns/Program.cs`: wiring for DI/config/servers via `Microsoft.Extensions.DependencyInjection`.
- `Dns/DnsServer.cs`: UDP DNS loop and upstream forwarding.
- `Dns/SmartZoneResolver.cs`: in-memory zone cache & round-robin dispenser.
- `Dns/HttpServer.cs`: embedded status/diagnostics surface.
- `Dns/ZoneProvider/**`: implementations (CSV, IP probes, BIND placeholder).

## 3. Getting Started
```bash
# restore & build
dotnet build csharp-dns-server.sln

# run tests
dotnet test csharp-dns-server.sln

# run server (localhost)
cd dns-cli
dotnet run -- ./appsettings.json
```
Gotchas:
- UDP port 53 may be taken by Docker/ICS on Windows; change listener port in `appsettings.json`.
- `Dns/appsettings.json` is copied to output; edit with care when adding samples.
- Zone providers may depend on local files (CSV) or ping-able IPs—mock or isolate tests accordingly.

## 4. Coding Standards
- C# 8 / .NET 3.1 currently, migrating to .NET 8 (see PRD). Prefer idiomatic C# and existing project style.
- Keep ASCII unless file already uses Unicode.
- Windows (/r/n) line delimiters
- Prefer spaces not tabs
- Add comments only where logic is non-obvious.
- ```dotnet format``` all code before submission
- MIT license headers already present — preserve them.

## 5. Allowed / Disallowed Work
- ✅ Modify C# source, tests, sample configs, docs within `docs/` and root (`AGENTS.md`, README).
- ✅ Add new tests or scripts that live in-repo (delete temporary tooling before submitting).
- 🚫 Do **not** edit deployment/infrastructure assets (Dockerfiles, systemd service files, external config stores) unless explicitly authorized by a maintainer.
- 🚫 No secret management or external network calls without approval.

## 6. Workflow
1. **Plan**: understand issue context (link to PRD sections). If multiple files touched, outline steps before coding.
2. **Implement**: keep changes scoped; ensure zone providers/tests stay deterministic.
3. **Validate**: run `dotnet build` and relevant `dotnet test` subsets. Document skipped tests or environment assumptions.
4. **Document**: update `docs/` where appropriate. Update README when adding features, config switches and any other project-wide relevant information.
5. **Submit Pull Request**: run `dotnet format`. Follow the contribution workflow in README (squash commits, include rationale).

## 7. Testing Expectations
- Minimum: `dotnet test csharp-dns-server.sln`.
- The dns-cli integration harness (`dnstest/Integration` + `DnsCliAuthoritativeBehaviorTests`) runs automatically with `dotnet test`, spins up `dns-cli` using the sample assets in `dnstest/TestData`, and needs free TCP/UDP ports; keep configs deterministic when extending it.
- For networking changes, add/extend unit tests in `dnstest` or new integration fixtures.
- Capture repro cases for fixed bugs (#26 compressed pointers, #11 BitPacker write) and ensure tests fail before fixes.

## 8. Observability & Diagnostics
- Prefer structured logging (use `Console.WriteLine` only as placeholder).
- When adding metrics or tracing, integrate with future Prometheus/OTel plan (see PRD §4).

## 9. Communication & Review
- DO STOP and ask questions if there is missing, ambiguous, or inconsistent information.
- Document assumptions and remaining risks in PR descriptions.
- If blocked by environmental constraints (e.g., network access), leave instructions for a maintainer.
- Keep PRs focused; split unrelated fixes.

## 10. Safety & Guardrails
- Treat `appsettings.json` samples as templates—do not embed secrets.
- Respect the agent scope limit: no infrastructure edits.
- When unsure, create an issue/comment instead of guessing.

Thank you for helping build a reliable C# DNS server!
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1">
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1" Condition="$(DebugProject) != true">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
5 changes: 2 additions & 3 deletions Dns.Cli/Migrations/20251011113400_InitialCreate.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Dns.Cli/Migrations/20251011113400_InitialCreate.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

#nullable disable
using Microsoft.EntityFrameworkCore.Migrations;

namespace Dns.Cli.Migrations
{
Expand Down
7 changes: 3 additions & 4 deletions Dns.Cli/Migrations/20251012125120_Rev1.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Dns.Cli/Migrations/20251012125120_Rev1.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

#nullable disable
using Microsoft.EntityFrameworkCore.Migrations;

namespace Dns.Cli.Migrations
{
Expand Down
7 changes: 3 additions & 4 deletions Dns.Cli/Migrations/20251012131553_Rev2.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Dns.Cli/Migrations/20251012131553_Rev2.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

#nullable disable
using Microsoft.EntityFrameworkCore.Migrations;

namespace Dns.Cli.Migrations
{
Expand Down
7 changes: 3 additions & 4 deletions Dns.Cli/Migrations/DnsServerDbContextModelSnapshot.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// <auto-generated />
using System;

#nullable disable

using Dns.Db.Contexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

#nullable disable

namespace Dns.Cli.Migrations
{
Expand Down
14 changes: 12 additions & 2 deletions Dns.Cli/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
Expand All @@ -21,10 +21,20 @@ public static Task Main(string[] args)
private static IHostBuilder CreateWebHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(
(_, config) =>
(context, config) =>
{
config.AddEnvironmentVariables();
config.AddCommandLine(args);
var tempConfig = config.Build();
var customPath = tempConfig["appsettings"];

if (!string.IsNullOrWhiteSpace(customPath))
{
if (File.Exists(customPath))
{
config.AddJsonFile(customPath, optional: false, reloadOnChange: true);
}
}
}
)
.ConfigureWebHostDefaults(webHost => webHost.UseStartup<Startup>());
Expand Down
4 changes: 1 addition & 3 deletions Dns.Cli/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Dns.Config;
using Dns.Contracts;
using Dns.Db.Configuration;
using Dns.Db.Contexts;
using Dns.Db.Extensions;
using Dns.Handlers;
using Dns.Services;
Expand All @@ -25,7 +24,6 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
Expand Down Expand Up @@ -227,4 +225,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}
2 changes: 1 addition & 1 deletion Dns.Db/Models/EntityFramework/Enums/ResourceClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ public enum ResourceClass : ushort
CS = 2,
CH = 3,
HS = 4,
}
}
3 changes: 1 addition & 2 deletions Dns.Db/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Globalization;
using Dns.Db.Contexts;
using Dns.Db.Contexts;
using Dns.Db.Models.EntityFramework;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
Expand Down
116 changes: 116 additions & 0 deletions Dns.UnitTests/BindZoneProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// // //-------------------------------------------------------------------------------------------------
// // // <copyright file="BindZoneProviderTests.cs" company="stephbu">
// // // Copyright (c) Steve Butler. All rights reserved.
// // // </copyright>
// // //-------------------------------------------------------------------------------------------------

using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dns.Db.Models.EntityFramework.Enums;
using Dns.UnitTests.Integration;
using Dns.ZoneProvider;
using Dns.ZoneProvider.Bind;
using Microsoft.Extensions.Logging.Testing;
using Xunit;

namespace Dns.UnitTests;

public class BindZoneProviderTests
{
[Fact]
public void GenerateZone_ReturnsZoneRecordsFromBindFile()
{
var zoneFile = Path.Combine(TestProjectPaths.TestDataDirectory, "Bind", "simple.zone");

using var provider = CreateProvider(zoneFile);
var zone = provider.GenerateZone();

Assert.NotNull(zone);
Assert.Equal("example.com", zone.Suffix);
Assert.Equal(0u, zone.Serial);

var filteredRecords = zone.Records.Where(record => record.Host == "www.example.com" && record.Type == ResourceType.A);

var wwwA = Assert.Single(filteredRecords);
Assert.Equal("192.0.2.10", Assert.Single(wwwA.Addresses));

filteredRecords = zone.Records.Where(record => record.Host == "www.example.com" && record.Type == ResourceType.AAAA);
var wwwAaaa = Assert.Single(filteredRecords);
Assert.Equal("2001:db8::10", Assert.Single(wwwAaaa.Addresses));

filteredRecords = zone.Records.Where(record => record.Host == "example.com" && record.Type == ResourceType.A);

var apex = Assert.Single(filteredRecords);
Assert.Contains("192.0.2.20", apex.Addresses);

filteredRecords = zone.Records.Where(record => record.Host == "api.example.com");
var api = Assert.Single(filteredRecords);
Assert.Equal("192.0.2.30", Assert.Single(api.Addresses));
}

[Fact]
public void GenerateZone_InvalidZoneReturnsNull()
{
var zoneFile = Path.Combine(TestProjectPaths.TestDataDirectory, "Bind", "invalid_missing_ttl.zone");

using var provider = CreateProvider(zoneFile);
var zone = provider.GenerateZone();

Assert.Null(zone);
}

[Fact]
public void GenerateZone_ReturnsNullWhenCNameConflictsWithAddress()
{
var tempZone = WriteTempZoneFile(
[
"$TTL 1h",
"$ORIGIN example.com.",
"@ IN SOA ns1.example.com. hostmaster.example.com. (",
" 2024010101",
" 7200",
" 3600",
" 1209600",
" 3600 )",
"@ IN NS ns1.example.com.",
"www IN CNAME api",
"www IN A 192.0.2.40",
"api IN A 192.0.2.50",
]
);

try
{
using var provider = CreateProvider(tempZone);
var zone = provider.GenerateZone();

Assert.Null(zone);
}
finally
{
File.Delete(tempZone);
}
}

private BindZoneProvider CreateProvider(string zoneFile)
{
var provider = new BindZoneProvider(new FakeLogger<BindZoneProvider>(), new SmartZoneResolver(new FakeLogger<SmartZoneResolver>()));
provider.Initialize(new()
{
Name = "example.com",
ProviderSettings = new FileWatcherZoneProviderSettings
{
FileName = zoneFile,
},
});
return provider;
}

private string WriteTempZoneFile(IEnumerable<string> lines)
{
var path = Path.GetTempFileName();
File.WriteAllLines(path, lines);
return path;
}
}
Loading