A port forwarder which the server sends the SYN packet to client.
A simple TCP port forwarder usually works by creating an outgoing connection to a destination server for each incoming local connection. In nutshell, a program in client connects to port forwarder client, port forwarder client connects to a port forward sever, server establishes the connection between itself and client and then creates a connection to another host. Now the traffic between the initializer and last host is proxied between port forwarder client/server.
One thing you have to note here is the fact that the client initiates the connection between itself and server. On some cases, you may want the server to start the connection with the client. Obviously this requires the client to be exposed to the internet, either by port forwarding or by not having NAT at all.
Next question is how does the server connect to the client if the client is the one which shall establish the connections and knows when it needs another stream? The answer: WE CHEAT
We don't want to make direct connection to our server, right? But it's fine for a CDN (like cloudflare) to make connection with our server. So the server program also listens for HTTP requests on another port and upon receiving a specific request, it will initiate a connection with the client.
Each connection has an ID which it's generated by the client. They serve no purpose other than logging. When the client asks for a connection (via CDN), it also specifies a random ID. In the first packet of server to client the ID is sent back to client.
We have two programs: jailed
and freedom
. jailed
is our client and freedom
is our server.
- A program starts a TCP connection with
jailed
. - Jailed creates a random ID and sends it via a simple POST request to
freedom
via a CDN like Cloudflare. Note thatjailed
is not establishing a connection tofreedom
; CDN is establishing it! freedom
establishes a connection withjailed
. The ID is given in the first packet. The first packet is obfuscated as a simple HTTP request.- While this connection is being established,
freedom
also establishes a connection with remote host which packets should be forwarded to. jailed
does the handshake (parsing the first packet and extracting the ID). The connection is rejected if the handshake fails.- Now we have a stream between
jailed
andfreedom
whichfreedom
has started it. - A connection is closed on both ends upon either side closing it.
Schematic of the connection can be illustrated in the diagram below:
Local Service Destination Host
↕ ↕
Jailed <------- Direct -------> Freedom
↧ ↥
┕ ----------- CDN ------------- ┙
Before anything, you must have something to run! Before starting, make sure that you have Go 1.20. Then simply clone
this repo and execute following commands to build both jailed
and freedom
.
go build ./cmd/jailed/
go build ./cmd/freedom/
To run the freedom
app, create a file called config.json
next to it. A sample config file exists in the examples
directory. Here is another sample config:
{
"control_listen": "127.0.0.1:53141",
"jailed_computer_address": "1.1.1.1:32521",
"destination_address": "127.0.0.1:51109"
}
All the keys are necessary and have the following meanings:
control_listen
: The address that the control messages (the ones relayed through the CDN) comes in.jailed_computer_address
: The address which should be dialed to connect to the jailed PC.destination_address
: Where the packets should go after being proxied.
A sample config JSON file looks like this:
{
"local_listen_address": "127.0.0.1:24519",
"freedom_listen_address": "0.0.0.0:32521",
"control_endpoint": "https://my-domain.com/",
"remote_wait_timeout": 5000
}
All keys except remote_wait_timeout
are necessary.
local_listen_address
: On which interface and port connections are being accepted from the local service.freedom_listen_address
: On which interface and port should we listen for direct connections fromfreedom
.control_endpoint
: What is the domain behind CDN which connects us tocontrol_listen
offreedom
. (Hint: You might want to use a reverse proxy like Caddy or Nginx)remote_wait_timeout
: How long should be waited before terminating a local connection because of the absence of remote connection. In other words,freedom
hasremote_wait_timeout
milliseconds to make a connection withjailed
, otherwise the local connection is dropped. The unit of this option is milliseconds and the default value is 10 seconds.