How NoRouter works under the hood
How NoRouter works under the hood.
The main NoRouter process launches the remote subprocesses and transfer L3 packets using their stdio streams.
To translate unprivileged socket syscalls into L3 packets, TCP/IP is implemented in userspace using netstack from gVisor & Fuchsia.
Stdio packet protocol
uint8be Magic | 0x42
uint24be Len | Length of the packet in bytes, excluding Magic and Len itself
uint16be Type | 0x0001: L3, 0x0002: JSON (for configuration)
uint16be Reserved | 0x0000
[]byte L3OrJSON | L3 or JSON
JSON messages
JSON messages are used to configure the agent. There are 3 types of messages:
requestare sent from the manager to an agentresultmessages are sent from an agent to the manager as a response to arequesteventare messages sent from an agent to the manager to indicate an independent event.
Messages always have the following structure:
{
"type": "request|response|event",
"body": { ... }
}
The request body
The request body has the following structure:
{
"id": 1, //Unique ID for this request
"op": "operator", //Currently the only operator supported is "configure"
"args": { ... }
}
The configure message arguments
The configure message has the following arguments:
{
"me": "192.168.42.100",
"forwards": [
// See Forward below
],
"others": [
// See IPPortProto below
],
"hostnameMap": {
"hostname": "ip"
},
"http": {
// See HTTP below
},
"socks": {
// See "Socks" below
},
"loopback": {
// See "Loopback" below
},
"statedir": {
"path": "/home/user/.norouter/agent",
"disable": false,
},
"writeEtcHosts": true,
"routes": [
// See "Route" below
],
"nameServers": [
// See IPPortProto below
]
}
The Forward object
The forward object has the following fields:
{
"listen_port": 23
// ConnectIP can be either IP or hostname.
// Until NoRouter v0.4.0, it had to be IP.
// The field name stil contains "_ip" suffix for compatibility.
"connect_ip": "ip or hostname",
"connect_port": 23,
"proto": "tcp",
}
The IPPortProto object
The IPPortProto object contains the following fields:
{
"ip": "192.168.42.100",
"port": 23,
"proto": "tcp"
}
The HTTP object
{
"listen": "127.0.0.1:80"
}
The Socks object
{
"listen": "127.0.0.1:5000"
}
The Loopback object
{
"disable": true
}
The Route object
{
"toCIDR": "192.168.95.0/24",
"toHostnameGlob": "*.cloud1.example.com",
"via": "192.168.42.100"
}
The result body
{
"request_id": 1,
"op": "configure",
"error": {},
"data": {}
}
The configure result data
{
"features": [
// Listening on multiple loopback IPs such as 127.0.42.101, 127.0.42.102, ...
"loopback",
// TCPv4 stream
"tcp",
// HTTP proxy
"http",
//Disable loopback
"loopback.disable",
//SOCKS
"socks",
//Creating ~/.norouter/agent/hostaliases file
"hostaliases",
//hostaliases using xip.io
"hostaliases.\"xip.io\"",
//Writing /etc/hosts when possible
"etchosts",
// Drawing packets into a specific hosts. Only meaningful for HTTP and SOCKS proxy modes
"routes",
//Built-in DNS
"dns"
],
"version": "norouter version"
}
The event message
The event JSON looks like this:
{
"type": "event type",
"data": { ... }
}
The routeSuggestion event
This event suggests a route to the manager. The data field is as follows:
{
"ip": [
// List of IP addresses
],
"route": "192.168.42.100"
}
Further information:
Last modified December 17, 2020: Documented current protocol (9bb5ddb)