Pattern · a few Firebug calls

DynaPort

A well-behaved listener port for a LAN HTTP/media server — so a busy port never strands a user. Pick it, persist it, authorize it.

The problem

If your app runs a small HTTP server to stream media to a device on the LAN, you have to pick a port for it. Hardcode 8080 and one day something else is already on 8080; the server won't start and the user is stuck editing config or filing a bug. Pick a different fixed number and you've only moved the collision.

The port doesn't need to be fixed. Many network media receivers are handed the full media URL — host and port — by the sender and simply fetch it; they never discover or guess the port. So the listener port is your own business, and the right answer is whatever's free right now.

Which leaves two jobs on Windows: (1) pick a free port, and (2) authorize it so a non-admin process can bind a LAN-reachable address and peers can reach it (a URL-ACL reservation + a firewall rule). Both are small, both are fiddly, and both are easy to get subtly wrong. That's what Firebug is for.

Firebug — the library this uses

Firebug is a small, dependency-free (.NET BCL only, net48) Windows utility that does exactly those two jobs. It consolidates the port / firewall / URL-ACL patterns that otherwise get re-hand-rolled in every project into one tried-and-true place, as a referenceable library and a CLI. Apache-2.0 — reference it, or vendor the pieces you need.

Apache-2.0 Get it:  github.com/halrad-com/firebug Firebug.dll (library) · firebug.exe (CLI, self-elevating)

What it provides

TypeWhat it does
PortPickerChoose a free listener port — probe one, pick the first free at/above a preferred value, or the first free adjacent pair; reuse a saved port when it's still free.
FirebugManagerAuthorize a port — add/remove/check firewall rules (TCP & UDP) and URL-ACL reservations; test elevation and firewall state; generate a manual .bat; open the Firewall UI.
PortPicker.IsFree(port) · Pick(preferred) · PickPair(preferred) · Resolve(savedPort, preferred)
Probe a port; find the first free one; find the first free adjacent pair (server + side-channel); or reuse the saved port, falling back to a fresh pick.
FirebugManager.AddUrlAcl(port) · AddTcpInboundRule(name, port) · AddUdpInboundRule(name, port)
Reserve the URL ACL so a non-admin process may bind a LAN prefix, and open the firewall so peers can reach it. (Both need elevation.)
HasRule(name) · HasUrlAcl(port) · IsElevated() · IsFirewallEnabled()
Check what's already in place before touching anything.
RemoveRule(name) · RemoveUrlAcl(port) · GenerateScript(name, port) · OpenFirewallSettings()
Tear it down on uninstall/port-change, emit a manual .bat for users who prefer that, or just open the Windows Firewall UI.

Prefer to stay out of process? The same thing from an elevated shell:

firebug add --name "MyApp" --port 8000 --urlacl
firebug check --name "MyApp" --port 8000
firebug remove --name "MyApp" --port 8000

Putting it together — the calls

var fb = new Firebug.FirebugManager();

// 1. Pick -- reuse the saved port if still free, else ladder up from 8000.
int port = Firebug.PortPicker.Resolve(settings.Port, preferred: 8000, log: Log);

// 2. Persist -- keeps the URL ACL / firewall rule / bookmarks aligned to one number.
settings.Port = port;
settings.Save();

// 3. Authorize that exact port (needs elevation -- see note).
fb.AddUrlAcl(port);                       // non-admin process may then bind a LAN prefix
fb.AddTcpInboundRule("MyApp", port);      // peers on the LAN can reach it

// 4. Bind, then hand the receiver the URL. It never guesses the port.
listener.Prefixes.Add($"http://*:{port}/");
string mediaUrl = $"http://{GetLocalIP()}:{port}/";

Two things to get right

Windows footnote

Serving a receiver on the LAN means binding a non-loopback prefix (localhost won't reach another device). That needs a URL-ACL reservation (or an elevated process) plus a firewall rule — exactly what AddUrlAcl + AddTcpInboundRule do. Run them from a small elevated helper (ProcessStartInfo { Verb = "runas" }) or via firebug.exe (which self-elevates), not from inside a non-elevated host process.