Just enough code to get a private beta of https://www.proxied.eu going.
🛜 Share your Minecraft or Factorio instance on the Internet. No hosting, your own computer! 🔒 Your privacy is protected: players cannot detect your IP. 🚅 No complicated configuration required. Just a tiny app configured here. 😁 Capped free trial, then 3€/month or 30€/year.
- On startup,
proxied:- Finds the address of
proxyingin$API, defaulting to https://proxied.eu. - Finds its control port in
$PORT, defaulting to 32123. - Starts a tiny HTTP server on
http://127.0.0.1:$PORT, with CORS for$CONTROLLERdefaulting toproxied.eu.GET /returns the startup ID when it is set, or 404. - Reads
~/.proxiedcontaining a startup ID in plain text. If it exists, it starts using it. If not, it discovers a new one from$API/startupand persists it in~/.proxied.
- Finds the address of
- Query
$API/session/$startupIDfor a session ID postcard, target IP and port (ConnectionInfopostcard). - Establishes a connection to the target over TCP.
proxiedtoproxying:PRX, session ID length over 1 byte, session ID.proxyingtoproxied: either:OK, then the session starts.KO, an error message, then a disconnect.
Each side streams self-delimiting frames over the TCP socket. Frames are at least 3 bytes. The first byte is the type.
proxying tells proxied to open a new TCPv4 socket to the target IP and port identified as the following 2 bytes, represented over the next 4+2 bytes.
proxying tells proxied to open a new TCPv6 socket to the target IP and port identified as the following 2 bytes, represented over the next 18+2 bytes.
proxying tells proxied that the connection identified by the following 2 bytes should be associated with a UDP socket exchanging with the target IP and port represented over the next 4+2 bytes.
Either side tells the other side that the socket identified by the following 2 bytes should be closed.
Either side tells the other side that it has data to send. The data is prefixed by the connectionn ID as the following 2 bytes then its length over 2 bytes.
proxying tells proxied to exit. An exit code is sent over the next 2 bytes.
- On startup, establishes a connection pool to Redis on
$REDIS_URL, defaulting toredis://127.0.0.1:6379. - Awaits connections on
$PORT, defaulting to 8080. - Upon connection, after receiving the session ID, it subscribes to
s:$sessionIDand looks it up as a key in Redis (SessionInfopostcard). - If the session doesn't exist, it sends
KOInvalid sessionto the client and closes the connection. Otherwise, it sendsOKto the client and starts the session. - From the start of the session, every minute, it updates
l:$sessionIDin Redis with the current timestamp. - Whenever the subscription receives a message, it reloads the session info from Redis.
- It binds and forwards data from clients to the target as required from the latest session info.
admin add $sessionID $proxyingIP:$proxyingPort:$targetIP:$targetPort …admin remove $sessionIDadmin list(includes last activity)admin show $sessionIDadmin offer $proxyingIP:$proxyingPort tcp:$startPort-$endPort udp:$startPort-$endPort …admin rescind $proxyingIP:$proxyingPortadmin offersadmin publish $os(windows,macos) $url
admin offer expects each range argument to be prefixed with either tcp: or udp: (for example tcp:1024-2047 or udp:9000). You may repeat protocols to define multiple disjoint ranges per host.
- Exposed over HTTP on
$PORT, defaulting to 443. - Keeps the whole mapping in memory.
- Loads it from Redis on startup by scanning
s:*ando:*keys. o:*keys are shaped likeo:$proxyingIP:$proxyingPortand hold protocol-specific port range pools that can be assigned to sessions.- Internally, maintains separate bitsets of TCP and UDP IP:ports in use.
All services share a single Redis instance. Keys and payloads are binary postcard blobs unless noted otherwise.
-
s:<session_id>— session definitions. The canonical payload is aStoredSessionInfostruct encoded with postcard:struct StoredSessionInfo { bindings: Vec<SessionBindingConfig>, } struct SessionBindingConfig { proxying_ip: IpAddr, proxying_port: u16, target_ip: IpAddr, target_port: u16, }
-
l:<session_id>— unsigned 64-bit UNIX timestamp (seconds) tracking the last activity of a running session.proxyingrefreshes this every 60 seconds while a tunnel is up. Keys are not automatically removed if the corresponding session is deleted. -
o:<proxying_ip>:<proxying_port>— offer definitions for a proxy host, stored as a postcard-encodedOfferPortRangesvalue with protocol-specific, inclusive port ranges:struct OfferPortRanges { tcp: Vec<PortRange>, udp: Vec<PortRange>, } struct PortRange { start: u16, end: u16, }
IPv6 proxy addresses use square brackets in the key (for example
o:[2001:db8::1]:32123). These entries are maintained viaadmin offer/admin rescind, and the control plane loads them at startup to determine available capacity. Only theOfferPortRangesencoding is accepted; at least one oftcporudpmust contain ranges. -
d:<identifier>— DNS view of a domain, in the form:struct Domain { user_id: UUID, services: BTreeMap<String, Service>, // eg _minecraft._tcp, _factorio._udp } struct Service { name: String, target_ip: IpAddr, target_port: u16, }