v0.6: DHCP tunnel transport with NOP heartbeat protocol

This commit is contained in:
2026-08-12 00:29:54 +00:00
parent 4a58b94f35
commit e1739059d9
12 changed files with 743 additions and 528 deletions
+105
View File
@@ -0,0 +1,105 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
// Args: [interface_ip] [interval_ms]
// Defaults: auto-detect LAN IP, 50ms
string localIp = args.Length > 0 ? args[0] : GetLanInterfaceIp() ?? "0.0.0.0";
int intervalMs = args.Length > 1 && int.TryParse(args[1], out int iv) ? iv : 50;
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
sock.EnableBroadcast = true;
var localEp = new IPEndPoint(IPAddress.Parse(localIp), 68);
sock.Bind(localEp);
Console.WriteLine($"Bound to {localEp} (SO_REUSEADDR)");
Console.WriteLine("Sending NOPs + listening. Press Ctrl+C to stop.");
Console.WriteLine();
var cts = new CancellationTokenSource();
var recvTask = Task.Run(() => ReceiveLoop(sock, cts.Token));
var destEp = new IPEndPoint(IPAddress.Broadcast, 67);
uint nonce = 0;
while (!cts.Token.IsCancellationRequested)
{
nonce++;
string msg = $"HKMSTR {nonce}\n";
byte[] payload = Encoding.UTF8.GetBytes(msg);
try
{
int sent = sock.SendTo(payload, destEp);
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] SENT n={nonce} {sent} bytes");
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] SEND FAILED: {ex.Message}");
}
try { Thread.Sleep(intervalMs); }
catch (OperationCanceledException) { break; }
}
cts.Cancel();
await recvTask;
static string? GetLanInterfaceIp()
{
foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
{
if (nic.OperationalStatus != OperationalStatus.Up)
continue;
if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback)
continue;
if (nic.Description.Contains("WireGuard", StringComparison.OrdinalIgnoreCase))
continue;
foreach (var addr in nic.GetIPProperties().UnicastAddresses)
{
if (addr.Address.AddressFamily == AddressFamily.InterNetwork)
{
var ip = addr.Address.ToString();
if (ip.StartsWith("192.168.") || ip.StartsWith("10.") || ip.StartsWith("172."))
return ip;
}
}
}
return null;
}
static void ReceiveLoop(Socket sock, CancellationToken ct)
{
byte[] buffer = new byte[4096];
EndPoint fromEp = new IPEndPoint(IPAddress.Any, 0);
while (!ct.IsCancellationRequested)
{
int received;
try
{
if (!sock.Poll(500_000, SelectMode.SelectRead))
continue;
received = sock.ReceiveFrom(buffer, ref fromEp);
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] RECV ERROR: {ex.Message}");
continue;
}
string text = Encoding.UTF8.GetString(buffer, 0, received).TrimEnd('\n', '\r');
if (!text.StartsWith("HKMSTR"))
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] RECV {received} bytes from {fromEp} (no magic)");
continue;
}
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] RECV {received} bytes from {fromEp}: {text}");
}
}
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>