Skip to content

Latest commit

 

History

History
492 lines (307 loc) · 9.12 KB

API.md

File metadata and controls

492 lines (307 loc) · 9.12 KB

neosocksd API Reference

Version: dev

Index

RESTful API

  1. The RESTful API server runs HTTP/1.1.
  2. The content length limit for a single request is 4 MiB.

Healthy Check

Check server liveness.

  • Path: /healthy
  • Method: Any
  • Status: HTTP 200

Server Statistics

GET: Get the stateless server statistics.

POST: Calculate server statistics since the last request.

  • Path: /stats
  • Method: GET, POST
  • Status: HTTP 200
  • Response: Server statistics in plain text.

Ruleset Invoke

Run the posted script.

  • Path: /ruleset/invoke
  • Method: POST
  • Content: Lua script
  • Status: HTTP 200, HTTP 500

Ruleset RPCall

Internal API reserved for await.invoke.

  • Path: /ruleset/rpcall
  • Method: POST
  • Content: application/x-neosocksd-rpc
  • Status: HTTP 200, HTTP 500
  • Response: Invocation results.

Ruleset Update

Load the posted script and use it as follows:

  1. If module name is not specified, replace the ruleset.
  2. If module name is specified, replace the named Lua module.
  3. If the field _G.name refers to the named module, it will be updated too.
  • Path: /ruleset/update
  • Query: ?module=name (optional)
  • Method: POST
  • Content: Lua ruleset script or Lua module script
  • Status: HTTP 200, HTTP 500

Ruleset GC

Trigger the garbage collector to free some memory.

  • Path: /ruleset/gc
  • Method: POST
  • Content: None
  • Status: HTTP 200

Ruleset Callbacks

ruleset.resolve

Synopsis

function ruleset.resolve(domain)
    return "www.example.org:80", "http://203.0.113.1:8080", ..., "socks4a://[2001:DB8::1]:1080"
end

Description

Process a host name request. Specifically:

  • Any HTTP CONNECT
  • SOCKS5 with host name (a.k.a. "socks5h")
  • Any SOCKS4A

Params

  • domain: full qualified domain name and port, like "www.example.org:80"

Returns

  • addr: replace the request
  • addr, proxy: forward the request through another proxy
  • addr, proxyN, ..., proxy1: forward the request through proxy chain
  • nil: reject the request

The proxy addresses are specified in URI format, supported scheme:

  • socks4a://example.org:1080: SOCKS4A server. The implementation is SOCKS4 compatible when requesting IPv4 address.
  • socks5://example.org:1080: SOCKS5 server.
  • http://example.org:8080: HTTP/1.1 CONNECT server.

ruleset.route

Synopsis

function ruleset.route(addr)
    return "www.example.org:80", "http://203.0.113.1:8080", ..., "socks4a://[2001:DB8::1]:1080"
end

Description

Process an IPv4 request. Specifically:

  • SOCKS5 with IPv4 address
  • Any SOCKS4

Params

  • addr: address and port, like "203.0.113.1:80"

Returns

See ruleset.resolve

ruleset.route6

Synopsis

function ruleset.route6(addr)
    return "www.example.org:80", "http://203.0.113.1:8080", ..., "socks4a://[2001:DB8::1]:1080"
end

Description

Process an IPv6 request. Specifically:

  • SOCKS5 with IPv6 address

Params

  • addr: address and port, like "[2001:DB8::1]:80"

Returns

See ruleset.resolve

ruleset.tick

Synopsis

function ruleset.tick(now)
    -- ......
end

Description

Periodic timer callback.

Params

  • now: current timestamp in seconds

Returns

Ignored

ruleset.stats

Synopsis

function ruleset.stats(dt)
    local w = {}
    table.insert(w, string.format("dt = %.03f", dt))
    return table.concat(w, "\n")
end

Description

Generate custom information to be provided in the API /stats. See also stats.

Params

  • dt: seconds elapsed since last call

Returns

Custom information in a string.

Lua API

neosocksd.resolve

Synopsis

local addr = neosocksd.resolve("www.example.com")

Description

(Deprecated) consider using await.resolve instead.

Resolves a host name locally and blocks the whole server until resolution is finished or times out.

neosocksd.parse_ipv4

Synopsis

local subnet = neosocksd.parse_ipv4("169.254.0.0")
local mask = 0xFFFF0000 -- 169.254.0.0/16
local ip = neosocksd.parse_ipv4("203.0.113.1")
if (ip & mask) == subnet then
    -- ......
end

Description

Parses an IPv4 address into integers.

neosocksd.parse_ipv6

Synopsis

-- with 64-bit Lua integers
local subnet1, subnet2 = neosocksd.parse_ipv6("FE80::")
local mask1 = 0xFFC0000000000000 -- fe80::/10
local ip1, ip2 = neosocksd.parse_ipv6("2001:DB8::1")
if (ip1 & mask1) == subnet1 then
    -- ......
end

Description

Parses an IPv6 address into integers.

neosocksd.setinterval

Synopsis

neosocksd.setinterval(1.5)

Description

Set tick interval in seconds, see also ruleset.tick.

The valid interval range is [1e-3, 1e+9], use setinterval(0) to stop the timer tick.

neosocksd.invoke

Synopsis

-- neosocksd.invoke(code, host, proxyN, ..., proxy1)
neosocksd.invoke([[log("test rpc")]], "api.neosocksd.lan:80", "socks4a://127.0.0.1:1080")

Description

Run Lua code on another neosocksd. This function returns immediately. In case of failure, the invocation is lost.

neosocksd.stats

Synopsis

local t = neosocksd.stats()

Description

Return a table of raw statistics.

neosocksd.now

Synopsis

local now = neosocksd.now()

Description

Formally, get the timestamp of the latest event in seconds.

  • Any ruleset callback must be invoked by an event.
  • Any asynchronous routine must be resumed by an event.

regex.compile

Synopsis

local reg = regex.compile([[\.example\.(com|org)$]])
local s, e = reg:find(host)
if s then
    -- ......
end
local m = reg:match(host)
if m then
    -- ......
end

Description

Lua interface for POSIX Extended Regular Expressions.

zlib.compress

Synopsis

local z = zlib.compress(s)
local s1 = zlib.uncompress(z)
assert(s == s1)

Description

Data compression interface for zlib format (as declared in RFC 1950 and RFC 1951).

Tip: Using compressed data for remote invocations is not recommended because neosocksd.invoke and await.invoke may compress long content internally.

_G.NDEBUG

Synopsis

logf("some debug log: %d", 123)

Description

True if the log level doesn't allow printing debug logs. The log level depends on command line argument --loglevel.

In the default implementation of libruleset.lua, this value controls whether log/logf writes to standard output.

_G.marshal

Synopsis

local s = marshal("a", {"b", ["c"] = "d"})
log(s) -- "a",{"b",["c"]="d"}

Description

Marshal all parameters in Lua syntax.

To be symmetric, there is also _G.unmarshal(s) in libruleset.lua.

_G.async

Synopsis

async(function(...)
    -- routine
end, ...)

Description

Start an asynchronous routine. Asynchronous routines are supported by Lua coroutines. Therefore, they run concurrently, but not in parallel. See await.resolve for a full example.

Notice: The await.* functions should only be called in asynchronous routines.

await.resolve

Synopsis

async(function()
    local addr = await.resolve("www.example.com")
    if addr then
        -- ......
    end
end)

Description

Resolves a host name asynchronously. If asynchronous name resolution is not configured, await.resolve behaves the same as neosocksd.resolve.

IPv4/IPv6 preference depends on command line argument -4/-6.

Tip: To reduce delays caused by name resolution. It's recommended to set up a local DNS cache, such as systemd-resolved or dnsmasq.

await.invoke

Synopsis

async(function(addr)
    local begin = neosocksd.now()
    local ok, ret = await.invoke([[return "echo"]], addr)
    if not ok then
        error("await.invoke: " .. ret)
    end
    assert(ret == "echo")
    local rtt = neosocksd.now() - begin
    logf("ping %s: %dms", addr, math.ceil(rtt * 1e+3))
end, "127.0.1.1:9080")

Description

Run Lua code on another neosocksd and take the results back. await.invoke is designed to work on the control plane.

Tip: If you want to implement RPC, please refer to await.rpcall in libruleset.lua.

await.sleep

Synopsis

async(function()
    await.sleep(1.5)
end)

Description

Pause an asynchronous routine for at least specified interval in seconds.

The interval is clamped to [1e-3, 1e+9].

await.idle

Synopsis

async(function()
    while ruleset.running do
        await.sleep(10)
        local tasks = _G.tasks
        _G.tasks = {}
        for i, v in ipairs(tasks) do
            await.idle()
            -- ......
        end
    end
end)

Description

Pause an asynchronous routine until there is nothing better to do. For example, this can be used to choose a good time to start some non-urgent tasks in the background.