by gonc | 2025-12-17 10:05:32+00:00
Hi HN,

I’ve been experimenting with a different take on netcat-style connectivity and wanted to share the design and trade-offs.

Traditional netcat-like tools assume one side listens on a reachable IP and port. This breaks down when both peers are behind NATs, firewalls, or dynamic networks. Often neither side knows the other’s IP or can expose an inbound port.

The goal is to enable an ad-hoc, netcat-like connection using only a shared secret, without requiring inbound connectivity.

Design goals:

A. Easy to use. No client/server role. Both peers run the same command, e.g. nc -p2p <passphrase>

B. Support nc -e style behavior for executing a program per connection

C. No listening port required

D. No prior knowledge of peer IP or domain

E. Works when both peers are behind NAT

F. Minimal setup and dependencies

G. Familiar stdin/stdout netcat-style usage

Core idea:

Both peers share a high-entropy passphrase. It is never transmitted, and is deterministically used to derive:

(1) A unique MQTT topic as a rendezvous channel (2) A TLS certificate and private key for mutual TLS identity verification

Connection flow:

(1) Each peer derives the MQTT topic and TLS identity from the shared passphrase

(2) Both peers independently establish outbound TCP connections to a public MQTT broker The broker cannot decrypt or interpret messages

(3) Each side performs local STUN probing to discover NAT-mapped addresses

(4) Candidate addresses are exchanged exclusively via the derived MQTT topic

(5) Peers first attempt direct TCP connections

(6) If TCP fails, peers automatically fall back to UDP hole punching As a last resort, a “birthday paradox” strategy is used: one side picks 600 random source ports, the other probes 600 random destination ports

(7) If direct P2P fails, an optional fallback uses an existing SOCKS5 UDP relay, without custom relay protocols

(8) Once a direct path is established, MQTT is no longer used; all traffic flows P2P over mutually authenticated TLS

At no point does either peer need to:

A. Listen on a fixed port

B. Expose or know the peer’s IP or domain

C. Trust the rendezvous infrastructure with plaintext identity or session data

The MQTT broker only observes opaque topic names and encrypted payloads. Without the passphrase, it cannot authenticate, impersonate, or correlate peers.

Security model:

The derived TLS certificate represents a deterministic peer identity, meaningful only to peers sharing the passphrase. Mutual TLS ensures peers cannot be impersonated even if candidate addresses are tampered with.

This is not intended to replace VPNs or general-purpose P2P frameworks. The goal is to explore how little coordination is needed to establish a direct, authenticated channel.

Usage example:

Think of this as a lightweight FRP-style alternative, without a public reverse-proxy or exposed ports.

To reach 10.0.0.1:22 inside a LAN, run on any host:

gonc -p2p <passphrase> -linkagent

Equivalent to:

gonc -p2p <passphrase> -e ":mux linkagent" -keep-open -mqtt-wait

Later, from home:

gonc -p2p <passphrase> -link 3080;3080

Both sides then get a local SOCKS5 proxy to access services on the peer LAN.

Implementation notes:

A. Written in Go

B. Single binary

C. Behaves like netcat (stdin/stdout piping)

D. MQTT is used only for rendezvous, never for data

I’m interested in feedback, especially around NAT edge cases, security assumptions, and alternative rendezvous mechanisms.