Skip to content

Commit d99bb7f

Browse files
committed
js: docs
1 parent 6729fb3 commit d99bb7f

File tree

3 files changed

+114
-23
lines changed

3 files changed

+114
-23
lines changed

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Dumbest HTTP proxy ever.
2424
* Resilient to DPI (including active probing, see `hidden_domain` option for authentication providers)
2525
* Connecting via upstream HTTP(S)/SOCKS5 proxies (proxy chaining)
2626
* systemd socket activation
27+
* Scripting with JavaScript:
28+
* Access filter by JS function
29+
* Upstream proxy selection by JS function
2730

2831
## Installation
2932

@@ -175,6 +178,96 @@ Authentication parameters are passed as URI via `-auth` parameter. Scheme of URI
175178
* `blacklist` - location of file with list of serial numbers of blocked certificates, one per each line in form of hex-encoded colon-separated bytes. Example: `ab:01:02:03`. Empty lines and comments starting with `#` are ignored.
176179
* `reload` - interval for certificate blacklist file reload, if it was modified since last load. Use negative duration to disable autoreload. Default: `15s`.
177180

181+
## Scripting
182+
183+
With the dumbproxy, it is possible to modify request processing behaviour using simple scripts written in the JavaScript programming language.
184+
185+
### Access filter by JS script
186+
187+
It is possible to filter (allow or deny) requests with simple `access` JS function. Such function can be loaded with the `-js-access-filter` option. Option value must specify location of script file where `access` function is defined.
188+
189+
`access` function is invoked with following parameters:
190+
191+
1. **Request** *(Object)*. It contains following properties:
192+
* **method** *(String)* - HTTP method used in request (CONNECT, GET, POST, PUT, etc.).
193+
* **url** *(String)* - URL parsed from the URI supplied on the Request-Line.
194+
* **proto** *(String)* - the protocol version for incoming server requests.
195+
* **protoMajor** *(Number)* - numeric major protocol version.
196+
* **protoMinor** *(Number)* - numeric minor protocol version.
197+
* **header** *(Object)* - mapping of *String* headers (except Host) in canonical form to an *Array* of their *String* values.
198+
* **contentLength** *(Number)* - length of request body, if known.
199+
* **transferEncoding** *(Array)* - lists the request's transfer encodings from outermost to innermost.
200+
* **host** *(String)* - specifies the host on which the URL is sought. For HTTP/1 (per RFC 7230, section 5.4), this is either the value of the "Host" header or the host name given in the URL itself. For HTTP/2, it is the value of the ":authority" pseudo-header field.
201+
* **remoteAddr** *(String)* - client's IP:port.
202+
* **requestURI** *(String)* - the unmodified request-target of the Request-Line (RFC 7230, Section 3.1.1) as sent by the client to a server.
203+
2. **Destination address** *(Object)*. It's an address where actual connection is about to be created. It contains following properties:
204+
* **network** *(String)* - connection type. Should be `"tcp"` in most cases unless restricted to specific address family (`"tcp4"` or `"tcp6"`).
205+
* **originalHost** *(String)* - original hostname or IP address parsed from request.
206+
* **resolvedHost** *(String)* - resolved hostname from request.
207+
* **port** *(Number)* - port number.
208+
3. **Username** *(String)*. Name of the authenticated user or an empty string if there is no authentication.
209+
210+
`access` function must return boolean value, `true` allows request and `false` forbids it. Any exception will be reported to log and the corresponding request will be denied.
211+
212+
Also it is possible to use builtin `print` function to print arbitrary values into dumbproxy log for debugging purposes.
213+
214+
Example:
215+
216+
```js
217+
// Deny unsafe ports for HTTP and non-SSL ports for HTTPS.
218+
219+
const SSL_ports = [
220+
443,
221+
]
222+
const Safe_ports = [
223+
80, // http
224+
21, // ftp
225+
443, // https
226+
70, // gopher
227+
210, // wais
228+
280, // http-mgmt
229+
488, // gss-http
230+
591, // filemaker
231+
777, // multiling http
232+
]
233+
const highPortBase = 1025
234+
235+
function access(req, dst, username) {
236+
if (req.method == "CONNECT") {
237+
if (SSL_ports.includes(dst.port)) return true
238+
} else {
239+
if (dst.port >= highPortBase || Safe_ports.includes(dst.port)) return true
240+
}
241+
return false
242+
}
243+
```
244+
245+
### Upstream proxy selection by JS script
246+
247+
dumbproxy can select upstream proxy dynamically invoking `getProxy` JS function from file specified by `-js-proxy-router` option.
248+
249+
Note that this option can be repeated multiple times, same as `-proxy` option for chaining of proxies. These two options can be used together and order of chaining will be as they come in command line. For generalization purposes we can say that `-proxy` option is equivalent to `-js-proxy-router` option with script which returns just one static proxy.
250+
251+
`getProxy` function is invoked with the [same parameters](#access-filter-by-js-script) as the `access` function. But unlike `access` function it is expected to return proxy URL in string format *scheme://[user:password@]host:port* or empty string `""` if no additional upstream proxy needed (i.e. direct connection if there are no other proxy dialers defined in chain).
252+
253+
Supported proxy schemes are:
254+
* `http` - regular HTTP proxy with the CONNECT method support.
255+
* `https` - HTTP proxy over TLS connection.
256+
* `socks5`, `socks5h` - SOCKS5 proxy with hostname resolving via remote proxy.
257+
258+
Example:
259+
260+
```js
261+
// Redirect .onion hidden domains to Tor SOCKS5 proxy
262+
263+
function getProxy(req, dst, username) {
264+
if (dst.originalHost.replace(/\.$/, "").toLowerCase().endsWith(".onion")) {
265+
return "socks5://127.0.0.1:9050"
266+
}
267+
return ""
268+
}
269+
```
270+
178271
## Synopsis
179272

180273
```
@@ -224,6 +317,14 @@ Usage of /home/user/go/bin/dumbproxy:
224317
sign username with specified key for given validity period. Positional arguments are: hex-encoded HMAC key, username, validity duration.
225318
-ip-hints string
226319
a comma-separated list of source addresses to use on dial attempts. "$lAddr" gets expanded to local address of connection. Example: "10.0.0.1,fe80::2,$lAddr,0.0.0.0,::"
320+
-js-access-filter string
321+
path to JS script file with the "access" filter function
322+
-js-access-filter-instances int
323+
number of JS VM instances to handle access filter requests (default 4)
324+
-js-proxy-router value
325+
path to JS script file with the "getProxy" function
326+
-js-proxy-router-instances int
327+
number of JS VM instances to handle proxy router requests (default 4)
227328
-key string
228329
key for TLS certificate
229330
-list-ciphers

access/jsfilter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type JSFilterFunc = func(req *jsext.JSRequestInfo, dst *jsext.JSDstInfo, usernam
2121
// Wrap it with filter pool for that!
2222
type JSFilter struct {
2323
funcPool chan JSFilterFunc
24-
next Filter
24+
next Filter
2525
}
2626

2727
func NewJSFilter(filename string, instances int, logger *clog.CondLogger, next Filter) (*JSFilter, error) {
@@ -65,7 +65,7 @@ func NewJSFilter(filename string, instances int, logger *clog.CondLogger, next F
6565

6666
return &JSFilter{
6767
funcPool: pool,
68-
next: next,
68+
next: next,
6969
}, nil
7070
}
7171

jsext/dto.go

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,25 @@ package jsext
22

33
import (
44
"context"
5-
"mime/multipart"
65
"net"
76
"net/http"
8-
"net/url"
97
"strconv"
108

119
ddto "github.com/SenseUnit/dumbproxy/dialer/dto"
1210
)
1311

1412
type JSRequestInfo struct {
15-
Method string `json:"method"`
16-
URL string `json:"url"`
17-
Proto string `json:"proto"`
18-
ProtoMajor int `json:"protoMajor"`
19-
ProtoMinor int `json:"protoMinor"`
20-
Header http.Header `json:"header"`
21-
ContentLength int64 `json:"contentLength"`
22-
TransferEncoding []string `json:"transferEncoding"`
23-
Host string `json:"host"`
24-
Form url.Values `json:"form"`
25-
PostForm url.Values `json:"portForm"`
26-
MultipartForm *multipart.Form `json:"multipartForm"`
27-
Trailer http.Header `json:"trailer"`
28-
RemoteAddr string `json:"remoteAddr"`
29-
RequestURI string `json:"requestURI"`
13+
Method string `json:"method"`
14+
URL string `json:"url"`
15+
Proto string `json:"proto"`
16+
ProtoMajor int `json:"protoMajor"`
17+
ProtoMinor int `json:"protoMinor"`
18+
Header http.Header `json:"header"`
19+
ContentLength int64 `json:"contentLength"`
20+
TransferEncoding []string `json:"transferEncoding"`
21+
Host string `json:"host"`
22+
RemoteAddr string `json:"remoteAddr"`
23+
RequestURI string `json:"requestURI"`
3024
}
3125

3226
func JSRequestInfoFromRequest(req *http.Request) *JSRequestInfo {
@@ -40,10 +34,6 @@ func JSRequestInfoFromRequest(req *http.Request) *JSRequestInfo {
4034
ContentLength: req.ContentLength,
4135
TransferEncoding: req.TransferEncoding,
4236
Host: req.Host,
43-
Form: req.Form,
44-
PostForm: req.PostForm,
45-
MultipartForm: req.MultipartForm,
46-
Trailer: req.Trailer,
4737
RemoteAddr: req.RemoteAddr,
4838
RequestURI: req.RequestURI,
4939
}

0 commit comments

Comments
 (0)