announce server hostname in MANIFEST, display in client

Server reads its hostname via gethostname(2) and includes it as a
length-prefixed UTF-8 string at the start of the MANIFEST payload.

Client displays 'Server: <hostname> — <MAC>' in a label above the
upstream list. Both sides updated for the new MANIFEST format:
  [hostname_len:1][hostname:N][entries...]

Protocol version unchanged (still 1); MANIFEST payload layout change
is backward-incompatible but both sides ship together.
This commit is contained in:
2026-08-13 08:08:05 +00:00
parent 27e15452ee
commit 3336a08543
7 changed files with 110 additions and 37 deletions
+27 -12
View File
@@ -40,7 +40,7 @@ readonly record struct UpstreamEntry(
abstract record Frame
{
internal record Discover : Frame;
internal record Manifest(UpstreamEntry[] Entries) : Frame;
internal record Manifest(string Hostname, UpstreamEntry[] Entries) : Frame;
internal record Open(byte UpstreamId) : Frame;
internal record OpenAck(uint SessionId, byte UpstreamId) : Frame;
internal record OpenNak(byte UpstreamId, byte Reason) : Frame;
@@ -74,7 +74,7 @@ static class FrameCodec
Frame.Discover =>
Build(Proto.TypeDiscover, 0, []),
Frame.Manifest manifest =>
Build(Proto.TypeManifest, 0, BuildManifestPayload(manifest.Entries)),
Build(Proto.TypeManifest, 0, BuildManifestPayload(manifest.Hostname, manifest.Entries)),
Frame.Open open =>
Build(Proto.TypeOpen, 0, [open.UpstreamId]),
Frame.OpenAck ack =>
@@ -90,9 +90,14 @@ static class FrameCodec
};
}
static byte[] BuildManifestPayload(UpstreamEntry[] entries)
static byte[] BuildManifestPayload(string hostname, UpstreamEntry[] entries)
{
using var ms = new MemoryStream();
var hnBytes = Encoding.UTF8.GetBytes(hostname);
var hnLen = (byte)Math.Min(hnBytes.Length, 255);
ms.WriteByte(hnLen);
if (hnLen > 0)
ms.Write(hnBytes, 0, hnLen);
foreach (var e in entries)
{
var labelBytes = Encoding.UTF8.GetBytes(e.Label ?? "");
@@ -141,25 +146,35 @@ static class FrameCodec
static Frame.Manifest? ParseManifest(ReadOnlySpan<byte> payload)
{
if (payload.Length < 1)
return null;
var hnLen = payload[0];
if (1 + hnLen > payload.Length)
return null;
var hostname = hnLen == 0
? ""
: Encoding.UTF8.GetString(payload[1..(1 + hnLen)]);
var rest = payload[(1 + hnLen)..];
var entries = new List<UpstreamEntry>();
int i = 0;
while (i < payload.Length)
while (i < rest.Length)
{
if (i + 5 > payload.Length)
if (i + 5 > rest.Length)
return null;
var id = payload[i];
var proto = payload[i + 1];
var port = (ushort)(payload[i + 2] << 8 | payload[i + 3]);
var labelLen = payload[i + 4];
var id = rest[i];
var proto = rest[i + 1];
var port = (ushort)(rest[i + 2] << 8 | rest[i + 3]);
var labelLen = rest[i + 4];
i += 5;
if (i + labelLen > payload.Length)
if (i + labelLen > rest.Length)
return null;
string? label = labelLen == 0
? null
: Encoding.UTF8.GetString(payload[i..(i + labelLen)]);
: Encoding.UTF8.GetString(rest[i..(i + labelLen)]);
i += labelLen;
entries.Add(new UpstreamEntry(id, proto, port, label));
}
return new Frame.Manifest(entries.ToArray());
return new Frame.Manifest(hostname, entries.ToArray());
}
}