mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-26 07:28:45 -07:00
Localtunnel hosting + admin panel + AppData db
- Default hosting mode is now Localtunnel — public *.loca.lt URL, no account, auto-fetched on first run. Replaces the prototyped Tailscale mode. - Tailscale code purged from launcher + CLI; docs swapped to Localtunnel throughout (easy-setup, joining-a-server, server-cli, troubleshooting, faq, READMEs). - Launcher sidebar gains an Open admin panel button — opens https://admin.<apex> in the browser once the server is live. - SQLite db moves to %APPDATA%\DorkNet\dorknet.db (server reads Database__SqlitePath env var, launcher passes it). Existing dbs under <serverDir>\data\ are migrated on first run. - CLI Program.cs: --mode default flips from tailscale to localtunnel; csproj links in LocaltunnelTunnel + LocaltunnelInstaller. - New Backend files: LauncherNameServer, LocaltunnelInstaller, LocaltunnelTunnel, SingleOriginServiceMap. - .gitignore: exclude installer/out/.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace DorkNet.Launcher.Backend;
|
||||
|
||||
/// <summary>Minimal localhost HTTP name-server for the patched client.
|
||||
/// The patcher points the hardcoded RecNet bootstrap URL at
|
||||
/// http://127.0.0.1:80; this listener answers that request with the
|
||||
/// service URL map for the active Easy Launcher session.</summary>
|
||||
public sealed class LauncherNameServer : IAsyncDisposable
|
||||
{
|
||||
private TcpListener? _listener;
|
||||
private CancellationTokenSource? _cts;
|
||||
private Task? _loop;
|
||||
private string _publicBaseUrl = "";
|
||||
|
||||
public void Start(string publicBaseUrl)
|
||||
{
|
||||
_publicBaseUrl = SingleOriginServiceMap.NormalizeBaseUrl(publicBaseUrl);
|
||||
if (_listener is not null) return;
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
_listener = new TcpListener(IPAddress.Loopback, 80);
|
||||
_listener.Start();
|
||||
_loop = Task.Run(() => AcceptLoopAsync(_cts.Token));
|
||||
}
|
||||
|
||||
public void Update(string publicBaseUrl)
|
||||
=> _publicBaseUrl = SingleOriginServiceMap.NormalizeBaseUrl(publicBaseUrl);
|
||||
|
||||
private async Task AcceptLoopAsync(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested && _listener is not null)
|
||||
{
|
||||
TcpClient client;
|
||||
try { client = await _listener.AcceptTcpClientAsync(ct); }
|
||||
catch (OperationCanceledException) { break; }
|
||||
catch { if (ct.IsCancellationRequested) break; else continue; }
|
||||
_ = Task.Run(() => HandleAsync(client, ct), ct);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleAsync(TcpClient client, CancellationToken ct)
|
||||
{
|
||||
using var clientHandle = client;
|
||||
try
|
||||
{
|
||||
await using var stream = client.GetStream();
|
||||
var buffer = new byte[2048];
|
||||
_ = await stream.ReadAsync(buffer, ct);
|
||||
|
||||
var body = SingleOriginServiceMap.ToJson(_publicBaseUrl);
|
||||
var bodyBytes = Encoding.UTF8.GetBytes(body);
|
||||
var header =
|
||||
"HTTP/1.1 200 OK\r\n" +
|
||||
"Content-Type: application/json; charset=utf-8\r\n" +
|
||||
$"Content-Length: {bodyBytes.Length}\r\n" +
|
||||
"Cache-Control: no-store\r\n" +
|
||||
"Connection: close\r\n\r\n";
|
||||
var headerBytes = Encoding.ASCII.GetBytes(header);
|
||||
await stream.WriteAsync(headerBytes, ct);
|
||||
await stream.WriteAsync(bodyBytes, ct);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
try { _cts?.Cancel(); } catch { }
|
||||
try { _listener?.Stop(); } catch { }
|
||||
if (_loop is not null)
|
||||
{
|
||||
try { await _loop; } catch { }
|
||||
}
|
||||
_cts?.Dispose();
|
||||
_listener = null;
|
||||
_cts = null;
|
||||
_loop = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user