Merge branch 'master' into feature-http2-transport
* master: (39 commits) docs: update package: bump to v3.3.1 bin: should delete more items in simple mode docs: add examples transports: set handshake timeout of ws to 10s lib: regen config: fix new URL() default port issue lint: fix package: upgrade eslint to v5.2.0 docs: add "Test Using curl" docs: update src: add client side https proxy support package: upgrade winston to v3.0.0 build: keep comments in lib/*.js ci: change NEXT_VERSION to 3.3.1 docs: update lib: regen package: bump to v3.3.0 docs: update transports,test: add "wss" transport ... # Conflicts: # src/core/config.js
This commit is contained in:
commit
9c3cf648e8
@ -5,7 +5,7 @@ node_js:
|
||||
- "node"
|
||||
|
||||
before_deploy:
|
||||
- export NEXT_VERSION=3.2.1
|
||||
- export NEXT_VERSION=3.3.1
|
||||
- export COMMIT_HASH=$(git log --format=%h -1)
|
||||
- export DIST_PATH=build
|
||||
- export PUBLISH_REPO=blinksocks/blinksocks-nightly-releases
|
||||
|
229
CHANGELOG.md
229
CHANGELOG.md
@ -1,31 +1,138 @@
|
||||
# Change Log
|
||||
|
||||
## 3.3.1 (2018-06-15)
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- package: upgrade winston to v3.0.0.
|
||||
- proxies: add `https` support.
|
||||
- transport: set handshake timeout of ws to 10s.
|
||||
- docs: add `Test Using curl`.
|
||||
- docs: add a batch of [examples](./docs/examples).
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- config: fix default port issue.
|
||||
|
||||
### Migrating from 3.3.0 to 3.3.1
|
||||
|
||||
```
|
||||
$ npm install -g blinksocks@3.3.1
|
||||
```
|
||||
|
||||
## 3.3.0 (2018-06-12)
|
||||
|
||||
### :exclamation: Notable Changes
|
||||
|
||||
- **transports**: add `wss`([WebSocket/TLS](docs/config#blinksocks-over-websockettls)).
|
||||
|
||||
If you encounter the following warning, and the certificate is `self-signed`, please add `"tls_cert_self_signed": true` to client configuration and provide server certificate in `"tls_cert"` as well:
|
||||
|
||||
```
|
||||
warn: [xxx:outbound] [x.x.x.x:xxxxx] self signed certificate
|
||||
```
|
||||
|
||||
```diff
|
||||
--- a/blinksocks.client.old.json
|
||||
+++ b/blinksocks.client.new.json
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tls://localhost:1082",
|
||||
"key": "AuM3R$]Pnj^Cqg^9",
|
||||
"presets": [
|
||||
@@ -18,11 +17,10 @@
|
||||
"mux": false,
|
||||
"mux_concurrency": 10,
|
||||
"tls_cert": "cert.pem",
|
||||
+ "tls_cert_self_signed": true
|
||||
},
|
||||
"dns": [],
|
||||
"dns_expire": 3600,
|
||||
"timeout": 300,
|
||||
```
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- **config**: add `tls_cert_self_signed` option.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- **transports/tls**: require `"tls_cert_self_signed": true` if use `self-singed` certificate.
|
||||
|
||||
### Migrating from 3.2.2 to 3.3.0
|
||||
|
||||
```
|
||||
$ npm install -g blinksocks@3.3.0
|
||||
```
|
||||
|
||||
## 3.2.2 (2018-06-10)
|
||||
|
||||
### :exclamation: Notable Changes
|
||||
|
||||
> For security reason, **executables** are no longer uploaded in the following releases, we only publish npm packages.
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- **deploy**: add `install-run-debian.sh`.
|
||||
- **transports**: add `pathname` support for websocket.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- **proxies**: fix a security bug of authorization bypass when use `socks` or `http` protocol.
|
||||
|
||||
### Migrating from 3.2.1 to 3.2.2
|
||||
|
||||
```
|
||||
$ npm install -g blinksocks@3.2.2
|
||||
```
|
||||
|
||||
## 3.2.1 (2018-05-12)
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- **benchmark**: add test for chacha20-ietf.
|
||||
- **hub**: set a timer to prune udp relays.
|
||||
- **src**: move constants from `presets/actions.js` to `src/constants.js`.
|
||||
- **src**: remove `preset.js`, implement `_write()` in `IPreset`.
|
||||
- **test**: add udp test for `obfs-tls1.2-ticket`.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- **core**: add `speed-tester.js`, fix speed measurement.
|
||||
|
||||
### Migrating from 3.2.0 to 3.2.1
|
||||
|
||||
```
|
||||
$ npm install -g blinksocks@3.2.1
|
||||
```
|
||||
|
||||
## 3.2.0 (2018-04-28)
|
||||
|
||||
> Node.js 10 is supported in this version!
|
||||
|
||||
### :exclamation: Notable Changes
|
||||
|
||||
- src: support socks4(a), socks5, http `basic authorization`(username/password).
|
||||
- presets: add `IPresetAddressing::onInitTargetAddress()` and `IPresetAddressing::resolveTargetAddress()`.
|
||||
- presets: add `chacha20-ietf` method for `ss-stream-cipher`, but require Node.js 10.x.
|
||||
- presets: deprecated `IPreset::onNotified()`.
|
||||
- presets: deprecated `auto-conf` preset.
|
||||
- **src**: support socks4(a), socks5, http `basic authorization`(username/password).
|
||||
- **presets**: add `IPresetAddressing::onInitTargetAddress()` and `IPresetAddressing::resolveTargetAddress()`.
|
||||
- **presets**: add `chacha20-ietf` method for `ss-stream-cipher`, but require Node.js 10.x.
|
||||
- **presets**: deprecated `IPreset::onNotified()`.
|
||||
- **presets**: deprecated `auto-conf` preset.
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- core: add `Pipe::initTargetAddress()`.
|
||||
- utils: add `uint64ToBuffer()`, `incrementLE()` and `incrementBE()`.
|
||||
- package: upgrade ws from v3.3.3 to v5.1.1.
|
||||
- package: use WHATWG URL API instead of legacy qs api.
|
||||
- package: add package-lock.json.
|
||||
- package: remove unused dependencies.
|
||||
- ci: add test on the latest stable Node.js.
|
||||
- **core**: add `Pipe::initTargetAddress()`.
|
||||
- **utils**: add `uint64ToBuffer()`, `incrementLE()` and `incrementBE()`.
|
||||
- **package**: upgrade ws from v3.3.3 to v5.1.1.
|
||||
- **package**: use WHATWG URL API instead of legacy qs api.
|
||||
- **package**: add package-lock.json.
|
||||
- **package**: remove unused dependencies.
|
||||
- **ci**: add test on the latest stable Node.js.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- hub: fix sequence between calling `relay::on()` and `relay.init()`.
|
||||
- hub: patch `server.getConnections()` for ws.
|
||||
- **hub**: fix sequence between calling `relay::on()` and `relay.init()`.
|
||||
- **hub**: patch `server.getConnections()` for ws.
|
||||
|
||||
### Migrating from 3.1.1 to 3.2.0
|
||||
|
||||
@ -37,13 +144,13 @@ $ npm install -g blinksocks@3.2.0
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- api: remove Hub::getPerformance().
|
||||
- api: add Hub::getUploadSpeed(), Hub::getDownloadSpeed() and Hub::getConnStatuses().
|
||||
- benchmark: enlarge wait time before kill iperf server.
|
||||
- package: upgrade winston logger to v3.
|
||||
- package: remove husky and precommit hook, it's annoying.
|
||||
- test: add udp tests for multiplexing.
|
||||
- docs: add usage for systemd.
|
||||
- **api**: remove Hub::getPerformance().
|
||||
- **api**: add Hub::getUploadSpeed(), Hub::getDownloadSpeed() and Hub::getConnStatuses().
|
||||
- **benchmark**: enlarge wait time before kill iperf server.
|
||||
- **package**: upgrade winston logger to v3.
|
||||
- **package**: remove husky and precommit hook, it's annoying.
|
||||
- **test**: add udp tests for multiplexing.
|
||||
- **docs**: add usage for systemd.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
@ -61,18 +168,18 @@ $ npm install -g blinksocks@3.1.1
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- lib: compiled to Node.js 8.
|
||||
- core: start udp server only when use socks protocol on client.
|
||||
- core: add performance.js to collect upload/download speed.
|
||||
- core: add Hub::getConnections(), Hub::getTotalRead(), Hub::getTotalWritten() and Hub::getPerformance().
|
||||
- test: add tests for udp relay.
|
||||
- test: add tests for multiplexing over ws and tls.
|
||||
- **lib**: compiled to Node.js 8.
|
||||
- **core**: start udp server only when use socks protocol on client.
|
||||
- **core**: add performance.js to collect upload/download speed.
|
||||
- **core**: add Hub::getConnections(), Hub::getTotalRead(), Hub::getTotalWritten() and Hub::getPerformance().
|
||||
- **test**: add tests for udp relay.
|
||||
- **test**: add tests for multiplexing over ws and tls.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- src: reduce error rate when enable multiplexing on server.
|
||||
- core: include target address in tracker's log when enable multiplexing.
|
||||
- core: avoid putting duplicate target address in tracker's log.
|
||||
- **src**: reduce error rate when enable multiplexing on server.
|
||||
- **core**: include target address in tracker's log when enable multiplexing.
|
||||
- **core**: avoid putting duplicate target address in tracker's log.
|
||||
|
||||
### Migrating from 3.0.0 to 3.1.0
|
||||
|
||||
@ -84,30 +191,30 @@ $ npm install -g blinksocks@3.1.0
|
||||
|
||||
### :boom: Breaking Changes:
|
||||
|
||||
- bin/init: remove "workers".
|
||||
- bin/init: replace "servers: []" to "server: {}".
|
||||
- core: remove balancer.
|
||||
- presets: remove `stats` preset.
|
||||
- presets: remove `tracker` preset, re-implement in core.
|
||||
- presets: remove `access-control` preset, re-implement in core.
|
||||
- **bin/init**: remove "workers".
|
||||
- **bin/init**: replace "servers: []" to "server: {}".
|
||||
- **core**: remove balancer.
|
||||
- **presets**: remove `stats` preset.
|
||||
- **presets**: remove `tracker` preset, re-implement in core.
|
||||
- **presets**: remove `access-control` preset, re-implement in core.
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- bin/init: add "acl" and "acl_conf" on server side.
|
||||
- src: refactor src/dns-cache.js and move it to utils/.
|
||||
- src: move config items from global to local.
|
||||
- core: expose Config::getLogFilePath().
|
||||
- benchmark: use json output of iperf.
|
||||
- presets/mux: discretize cid and other improvements.
|
||||
- ci: add scripts to do nightly release automatically.
|
||||
- test: add e2e tests.
|
||||
- **bin/init**: add "acl" and "acl_conf" on server side.
|
||||
- **src**: refactor src/dns-cache.js and move it to utils/.
|
||||
- **src**: move config items from global to local.
|
||||
- **core**: expose Config::getLogFilePath().
|
||||
- **benchmark**: use json output of iperf.
|
||||
- **presets/mux**: discretize cid and other improvements.
|
||||
- **ci**: add scripts to do nightly release automatically.
|
||||
- **test**: add e2e tests.
|
||||
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- src: fix "send() is not a function" when using mux though http proxy.
|
||||
- core: fix "cannot read property 'remoteInfo' of null" of mux-relay.js.
|
||||
- core: fix sub connection id collision.
|
||||
- core: handle listen "error" event.
|
||||
- **src**: fix "send() is not a function" when using mux though http proxy.
|
||||
- **core**: fix "cannot read property 'remoteInfo' of null" of mux-relay.js.
|
||||
- **core**: fix sub connection id collision.
|
||||
- **core**: handle listen "error" event.
|
||||
|
||||
### Committers: 2
|
||||
|
||||
@ -181,21 +288,21 @@ After v3, blinksocks no longer support multiple servers and cluster mode, so you
|
||||
|
||||
### :rocket: Features & Improvements
|
||||
|
||||
- core: add mux-relay.
|
||||
- benchmark: archive reports of 2017.
|
||||
- package: upgrade pkg to v4.3.0.
|
||||
- package: compile before running benchmark.
|
||||
- presets: add mux preset.
|
||||
- transports: refactor and optimize websocket transport.
|
||||
- transports: add this.ctx.
|
||||
- transports: add mux transport.
|
||||
- utils: add a faster version of crypto.randomBytes().
|
||||
|
||||
- **core**: add mux-relay.
|
||||
- **benchmark**: archive reports of 2017.
|
||||
- **package**: upgrade pkg to v4.3.0.
|
||||
- **package**: compile before running benchmark.
|
||||
- **presets**: add mux preset.
|
||||
- **transports**: refactor and optimize websocket transport.
|
||||
- **transports**: add this.ctx.
|
||||
- **transports**: add mux transport.
|
||||
- **utils**: add a faster version of crypto.randomBytes().
|
||||
**
|
||||
### :bug: Bug Fixes:
|
||||
|
||||
- proxies: fix crash when client reset the socks connection later.
|
||||
- utils: fix getRandomInt().
|
||||
- utils: remove generateMutexId().
|
||||
- **proxies**: fix crash when client reset the socks connection later.
|
||||
- **utils**: fix getRandomInt().
|
||||
- **utils**: remove generateMutexId().
|
||||
|
||||
### Upgrade from 2.8.5 to 2.9.0
|
||||
|
||||
|
27
README.md
27
README.md
@ -20,7 +20,7 @@
|
||||
|
||||
* Cross-platform: running on Linux, Windows and macOS.
|
||||
* Lightweight proxy interfaces: Socks5/Socks4/Socks4a and HTTP.
|
||||
* Multiple Transport Layers: TCP, UDP, [TLS] and [WebSocket].
|
||||
* Multiple Transport Layers: TCP, UDP, [TLS], [WebSocket] and [WebSocket/TLS].
|
||||
* TLS/TLS/WebSocket [multiplexing].
|
||||
* Convenient protocol [customization].
|
||||
* Access Control List([ACL]) support.
|
||||
@ -51,21 +51,10 @@ Please check out [blinksocks-nightly-releases](https://github.com/blinksocks/bli
|
||||
|
||||
## Run blinksocks
|
||||
|
||||
**npm version(require Node.js, recommended)**
|
||||
|
||||
```
|
||||
$ blinksocks --help
|
||||
```
|
||||
|
||||
**executable version(~~Node.js~~, not GUI)**
|
||||
|
||||
Tips: You can [download](https://github.com/blinksocks/blinksocks/releases) precompiled executables for different platforms and launch it directly without having Node.js installed.
|
||||
|
||||
```
|
||||
$ ./blinksocks --help // Linux and macOS
|
||||
$ blinksocks.exe --help // Windows
|
||||
```
|
||||
|
||||
For configuring blinksocks, please refer to [Configuration](docs/config).
|
||||
|
||||
## Documents
|
||||
@ -75,6 +64,7 @@ For configuring blinksocks, please refer to [Configuration](docs/config).
|
||||
1. [Usage](docs/usage)
|
||||
2. [Configuration](docs/config)
|
||||
3. [Presets](docs/presets)
|
||||
4. [Examples](docs/examples)
|
||||
|
||||
### For Developers
|
||||
|
||||
@ -91,11 +81,12 @@ See [contributors](https://github.com/blinksocks/blinksocks/graphs/contributors)
|
||||
|
||||
Apache License 2.0
|
||||
|
||||
[TLS]: docs/config#blinksocks-over-tls
|
||||
[WebSocket]: docs/config#blinksocks-over-websocket
|
||||
[multiplexing]: docs/config#multiplexing
|
||||
[customization]: docs/development/api
|
||||
[ACL]: docs/config#access-control-list
|
||||
[shadowsocks]: docs/presets/RECOMMENDATIONS.md#work-with-shadowsocks
|
||||
[shadowsocksR]: docs/presets/RECOMMENDATIONS.md#work-with-shadowsocksr
|
||||
[v2ray vmess]: docs/presets/RECOMMENDATIONS.md#work-with-v2ray-vmess
|
||||
[TLS]: docs/examples/tls
|
||||
[WebSocket]: docs/examples/websocket
|
||||
[WebSocket/TLS]: docs/examples/websocket-tls
|
||||
[multiplexing]: docs/examples/multiplexing
|
||||
[shadowsocks]: docs/examples/shadowsocks
|
||||
[shadowsocksR]: docs/examples/shadowsocksr
|
||||
[v2ray vmess]: docs/examples/v2ray-vmess
|
||||
|
@ -101,12 +101,8 @@ function main() {
|
||||
}
|
||||
|
||||
if (hasOption('--list-presets')) {
|
||||
const { presets, legacyPresets } = modules;
|
||||
console.log(chalk.bold.underline('[Built-In]'));
|
||||
console.log(presets.join(os.EOL));
|
||||
console.log('');
|
||||
console.log(chalk.bold.underline('[Deprecated]'));
|
||||
console.log(legacyPresets ? legacyPresets.map((name) => `${chalk.gray(name)}`).join(os.EOL) : '-');
|
||||
const { builtInPresetMap } = modules;
|
||||
console.log(Object.keys(builtInPresetMap).join(os.EOL));
|
||||
return;
|
||||
}
|
||||
|
||||
|
16
bin/init.js
16
bin/init.js
@ -38,24 +38,30 @@ module.exports = function init({ isMinimal, isOverwrite, isDryRun = false }) {
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } }
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
'tls_cert': 'cert.pem',
|
||||
'tls_cert_self_signed': false,
|
||||
'mux': false,
|
||||
'mux_concurrency': 10
|
||||
'mux_concurrency': 10,
|
||||
},
|
||||
'https_key': 'https_key.pem',
|
||||
'https_cert': 'https_cert.pem',
|
||||
'dns': [],
|
||||
'dns_expire': 3600,
|
||||
'timeout': timeout,
|
||||
'log_path': 'bs-client.log',
|
||||
'log_level': 'info',
|
||||
'log_max_days': 30
|
||||
'log_max_days': 30,
|
||||
};
|
||||
|
||||
if (isMinimal) {
|
||||
delete clientJson.server.tls_cert;
|
||||
delete clientJson.server.tls_cert_self_signed;
|
||||
delete clientJson.server.mux;
|
||||
delete clientJson.server.mux_concurrency;
|
||||
delete clientJson.https_key;
|
||||
delete clientJson.https_cert;
|
||||
delete clientJson.dns;
|
||||
delete clientJson.dns_expire;
|
||||
delete clientJson.timeout;
|
||||
@ -70,7 +76,7 @@ module.exports = function init({ isMinimal, isOverwrite, isDryRun = false }) {
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } }
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
'tls_key': 'key.pem',
|
||||
'tls_cert': 'cert.pem',
|
||||
@ -83,7 +89,7 @@ module.exports = function init({ isMinimal, isOverwrite, isDryRun = false }) {
|
||||
'redirect': '',
|
||||
'log_path': 'bs-server.log',
|
||||
'log_level': 'info',
|
||||
'log_max_days': 30
|
||||
'log_max_days': 30,
|
||||
};
|
||||
|
||||
if (isMinimal) {
|
||||
|
25
deploy/install-run-debian.sh
Executable file
25
deploy/install-run-debian.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
npm install --global pm2 blinksocks
|
||||
|
||||
echo "$ node --version"
|
||||
node --version
|
||||
|
||||
echo "$ npm --version"
|
||||
npm --version
|
||||
|
||||
echo "$ blinksocks --version"
|
||||
blinksocks --version
|
||||
|
||||
echo "$ blinksocks init"
|
||||
blinksocks init
|
||||
|
||||
echo "$ pm2 start blinksocks -- --config blinksocks.server.json"
|
||||
pm2 start blinksocks -- --config blinksocks.server.json
|
||||
|
||||
echo "$ cat blinksocks.server.json"
|
||||
cat blinksocks.server.json
|
||||
echo ""
|
@ -5,6 +5,7 @@
|
||||
1. [Usage](usage)
|
||||
2. [Configuration](config)
|
||||
3. [Presets](presets)
|
||||
4. [Examples](examples)
|
||||
|
||||
## For Developers
|
||||
|
||||
|
@ -31,9 +31,12 @@ $ blinksocks init
|
||||
}
|
||||
],
|
||||
"tls_cert": "cert.pem",
|
||||
"tls_cert_self_signed": false,
|
||||
"mux": false,
|
||||
"mux_concurrency": 10
|
||||
},
|
||||
"https_key": "https_key.pem",
|
||||
"https_cert": "https_cert.pem",
|
||||
"dns": [],
|
||||
"dns_expire": 3600,
|
||||
"timeout": 221,
|
||||
@ -76,28 +79,31 @@ $ blinksocks init
|
||||
}
|
||||
```
|
||||
|
||||
| KEY | DESCRIPTION | DEFAULT | REMARKS |
|
||||
| :---------------- | :----------------------------------------------------------- | :-------------- | :---------------------------------------------------------- |
|
||||
| service | local service address | - | a [WHATWG URL] e.g, "socks://127.0.0.1:1080" |
|
||||
| server | remote server config | - | **CLIENT ONLY** |
|
||||
| server.service | remote service address | - | `<protocol>://<host>:<port>` |
|
||||
| server.key | remote server master key | - | - |
|
||||
| presets | an ordered list of presets to build a protocol stack | - | see [presets] |
|
||||
| presets[i].name | preset name | - | - |
|
||||
| presets[i].params | preset params | - | - |
|
||||
| tls_key | private key for TLS | - | required on server if `<protocol>` is "tls" |
|
||||
| tls_cert | certificate for TLS | - | required on both client and server if `<protocol>` is "tls" |
|
||||
| acl | enable access control list or not | false | **SERVER ONLY** |
|
||||
| acl_conf | access control list configuration file | - | **SERVER ONLY**, see below |
|
||||
| timeout | timeout for each connection | 600 | in seconds |
|
||||
| mux | enable multiplexing or not | false | - |
|
||||
| mux_concurrency | the max mux connection established between client and server | 10 | **CLIENT ONLY** |
|
||||
| redirect | target address to redirect when preset fail to process | "" | **SERVER ONLY** `<host>:<port>` |
|
||||
| dns | a list of DNS server IPs | [] | - |
|
||||
| dns_expire | in-memory DNS cache expiration time | 3600 | in seconds |
|
||||
| log_path | log file path | "bs-[type].log" | a relative/absolute directory or a file to put logs in |
|
||||
| log_level | log level | "info" | ['error', 'warn', 'info', 'verbose', 'debug', 'silly'] |
|
||||
| log_max_days | the max of days a log file will be saved | 30 | remove this option if you want to keep all log files |
|
||||
| KEY | DESCRIPTION | DEFAULT | REMARKS |
|
||||
| :------------------- | :----------------------------------------------------------- | :-------------- | :---------------------------------------------------------- |
|
||||
| service | local service address | - | [WHATWG URL] e.g, "socks://127.0.0.1:1080" |
|
||||
| server | remote server config | - | **CLIENT ONLY** |
|
||||
| server.service | remote service address | - | [WHATWG URL] e.g, "tls://example.com:443" |
|
||||
| server.key | remote server master key | - | - |
|
||||
| presets | an ordered list of presets to build a protocol stack | - | see [presets] |
|
||||
| presets[i].name | preset name | - | - |
|
||||
| presets[i].params | preset params | - | - |
|
||||
| tls_key | private key path for TLS | - | required on server if `<protocol>` is "tls" |
|
||||
| tls_cert | certificate path for TLS | - | required on both client and server if `<protocol>` is "tls" |
|
||||
| tls_cert_self_signed | whether "tls_cert" is `self-signed` or not | false | **CLIENT ONLY** |
|
||||
| https_key | private key path for HTTPS | - | **CLIENT ONLY** |
|
||||
| https_cert | certificate path for HTTPS | - | **CLIENT ONLY** |
|
||||
| acl | enable access control list or not | false | **SERVER ONLY** |
|
||||
| acl_conf | access control list configuration file | - | **SERVER ONLY**, see below |
|
||||
| timeout | timeout for each connection | 600 | in seconds |
|
||||
| mux | enable multiplexing or not | false | - |
|
||||
| mux_concurrency | the max mux connection established between client and server | 10 | **CLIENT ONLY** |
|
||||
| redirect | target address to redirect when preset fail to process | "" | **SERVER ONLY** `<host>:<port>` |
|
||||
| dns | a list of DNS server IPs | [] | - |
|
||||
| dns_expire | in-memory DNS cache expiration time | 3600 | in seconds |
|
||||
| log_path | log file path | "bs-[type].log" | a relative/absolute file path to put logs in |
|
||||
| log_level | log level | "info" | ['error', 'warn', 'info', 'verbose', 'debug', 'silly'] |
|
||||
| log_max_days | the max of days a log file will be saved | 30 | remove this option if you want to keep all log files |
|
||||
|
||||
### Service
|
||||
|
||||
@ -105,12 +111,12 @@ $ blinksocks init
|
||||
|
||||
The `<protocol>` should be:
|
||||
|
||||
* On client side: `tcp`, `socks`/`socks5`/`socks4`/`socks4a` or `http`/`https`.
|
||||
* On server side: `tcp`, `tls` or `ws`.
|
||||
* On client side: `tcp`, `socks`/`socks5`/`socks4`/`socks4a`, `http` or `https`.
|
||||
* On server side: `tcp`, `tls`, `ws` or `wss`.
|
||||
|
||||
#### Service Authentication
|
||||
|
||||
* Create a **http** service with [Basic Authentication](https://www.iana.org/go/rfc7617).
|
||||
* Create a **http/https** service with [Basic Authentication](https://www.iana.org/go/rfc7617).
|
||||
|
||||
```
|
||||
// blinksocks.client.json
|
||||
@ -177,77 +183,6 @@ In this case, it uses [iperf](https://en.wikipedia.org/wiki/Iperf) to test netwo
|
||||
|
||||
For more information about presets, please check out [presets].
|
||||
|
||||
### blinksocks over TLS
|
||||
|
||||
By default, blinksocks use "tcp" as transport, but you can take advantage of TLS technology to protect your data well.
|
||||
|
||||
To enable blinksocks over TLS, you should:
|
||||
|
||||
1. Generate `key.pem` and `cert.pem` on server
|
||||
|
||||
```
|
||||
// self-signed
|
||||
$ openssl req -x509 -newkey rsa:4096 -nodes -sha256 -subj '/CN=localhost' \
|
||||
-keyout key.pem -out cert.pem
|
||||
```
|
||||
|
||||
> NOTE: Remember the **Common Name(CN)** you typed in the command line.
|
||||
|
||||
2. Server config
|
||||
|
||||
Change `tcp://` to `tls://`, then provide `tls_key` and `tls_cert`:
|
||||
|
||||
```
|
||||
{
|
||||
"service": "tls://<host>:<port>",
|
||||
"tls_key": "key.pem",
|
||||
"tls_cert": "cert.pem",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
3. Client config
|
||||
|
||||
Change server's `tcp://` to `tls://`, then provide `tls_cert`:
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"server": {
|
||||
"service": "tls://<Common Name>:<port>", // take care of <Common Name>
|
||||
"tls_cert": "cert.pem",
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### blinksocks over WebSocket
|
||||
|
||||
Like blinksocks over TLS, it's much easier to setup a websocket tunnel:
|
||||
|
||||
1. Server config
|
||||
|
||||
```
|
||||
{
|
||||
"service": "ws://<host>:<port>",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
2. Client config
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"server": {
|
||||
"service": "ws://<host>:<port>",
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Access Control List
|
||||
|
||||
You can enable ACL on **server** by setting **acl: true** and provide a acl configuration file in **acl_conf**:
|
||||
@ -281,36 +216,6 @@ Rules in **acl.txt** has a priority from lower to higher.
|
||||
|
||||
> NOTE: acl requires a restart each time you updated **acl_conf**.
|
||||
|
||||
### Multiplexing
|
||||
|
||||
Since blinksocks v2.9.0, blinksocks supports TCP/TLS/WS multiplexing.
|
||||
|
||||
You can enable this feature easily by setting `mux: true` on both client and server, and set `mux_concurrency: <number>` on client.
|
||||
|
||||
1. Server config
|
||||
|
||||
```
|
||||
{
|
||||
"mux": true,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
2. Client config
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"server": {
|
||||
...
|
||||
"mux": true,
|
||||
"mux_concurrency": 10
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Log Path
|
||||
|
||||
Specify a relative or absolute path to store log file, if no `log_path` provided, log file named `bs-[type].log` will be stored in the working directory.
|
||||
@ -352,4 +257,4 @@ apps <--SOCKS5--> [blinksocks client] <--UDP--> [blinksocks server] <--UDP--> de
|
||||
[WHATWG URL]: https://nodejs.org/dist/latest/docs/api/url.html#url_url_strings_and_url_objects
|
||||
[presets]: ../presets
|
||||
[winston]: https://github.com/winstonjs/winston
|
||||
[UDP ASSOCIATE]: https://tools.ietf.org/html/rfc1928#section-4
|
||||
[UDP ASSOCIATE]: https://tools.ietf.org/html/rfc1928#section-4
|
||||
|
@ -65,13 +65,3 @@ After compile, we can change version in `package.json` then publish a package to
|
||||
```
|
||||
$ npm publish
|
||||
```
|
||||
|
||||
## Package
|
||||
|
||||
For users don't have Node.js installed, we use [zeit/pkg](https://github.com/zeit/pkg) to prepare compiled executables:
|
||||
|
||||
```
|
||||
$ npm run pkg
|
||||
```
|
||||
|
||||
This will generate compressed executables for different platforms named `blinksocks-{platform}-${arch}-${version}.gz`. And can be distribute to target platform at once.
|
||||
|
3
docs/examples/README.md
Normal file
3
docs/examples/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Examples
|
||||
|
||||
Here I post some particle configuration of blinksocks, please enter each directory for more details.
|
31
docs/examples/multiplexing/README.md
Normal file
31
docs/examples/multiplexing/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Multiplexing
|
||||
|
||||
**Minimal Version Required: v2.9.x**
|
||||
|
||||
blinksocks supports TCP/TLS/WS multiplexing.
|
||||
|
||||
You can enable this feature easily by setting `"mux": true` on both client and server, and set `"mux_concurrency": <number>` on client.
|
||||
|
||||
1. Client config
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"server": {
|
||||
...
|
||||
"mux": true,
|
||||
"mux_concurrency": 10
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
2. Server config
|
||||
|
||||
```
|
||||
{
|
||||
"mux": true,
|
||||
...
|
||||
}
|
||||
```
|
23
docs/examples/multiplexing/blinksocks.client.json
Normal file
23
docs/examples/multiplexing/blinksocks.client.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tcp://example.com:17720",
|
||||
"key": "#{N-^!6{SExKTQ|b",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mux": true,
|
||||
"mux_concurrency": 10
|
||||
}
|
||||
}
|
19
docs/examples/multiplexing/blinksocks.server.json
Normal file
19
docs/examples/multiplexing/blinksocks.server.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"service": "tcp://0.0.0.0:17720",
|
||||
"key": "#{N-^!6{SExKTQ|b",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
],
|
||||
"mux": true
|
||||
}
|
23
docs/examples/obfs-random-padding/README.md
Normal file
23
docs/examples/obfs-random-padding/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# obfs-random-padding
|
||||
|
||||
`obfs-random-padding` provides ability to prevent traffic analysis(based on sequence of round trip packet length between client and server):
|
||||
|
||||
## To prevent traffic analysis
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "obfs-random-padding"},
|
||||
{"name": "ss-stream-cipher","params": {"method": "aes-128-ctr"}}
|
||||
]
|
||||
```
|
||||
|
||||
## To prevent traffic analysis and ensure integrity as well
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "obfs-random-padding"},
|
||||
{"name": "ss-aead-cipher","params": {"method": "aes-128-gcm"}}
|
||||
]
|
||||
```
|
21
docs/examples/obfs-random-padding/blinksocks.client.json
Normal file
21
docs/examples/obfs-random-padding/blinksocks.client.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tcp://example.com:50102",
|
||||
"key": "&8[j;(>fjLGm,db]",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
18
docs/examples/obfs-random-padding/blinksocks.server.json
Normal file
18
docs/examples/obfs-random-padding/blinksocks.server.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"service": "tcp://0.0.0.0:50102",
|
||||
"key": "&8[j;(>fjLGm,db]",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
7
docs/examples/obfs-tls-session-ticket/README.md
Normal file
7
docs/examples/obfs-tls-session-ticket/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# obfs-tls-session-ticket
|
||||
|
||||
**Minimal Version Required: v2.x**
|
||||
|
||||
You can append a **http** or **tls** obfuscator to preset list to avoid bad [QoS], **obfs-tls1.2-ticket** is recommended.
|
||||
|
||||
[QoS]: https://en.wikipedia.org/wiki/Quality_of_service
|
26
docs/examples/obfs-tls-session-ticket/blinksocks.client.json
Normal file
26
docs/examples/obfs-tls-session-ticket/blinksocks.client.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tcp://example.com:14938",
|
||||
"key": "had2a8#(kVKA2&gx",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ss-aead-cipher",
|
||||
"params": {
|
||||
"method": "aes-256-gcm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "obfs-tls1.2-ticket",
|
||||
"params": {
|
||||
"sni": [
|
||||
"example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
23
docs/examples/obfs-tls-session-ticket/blinksocks.server.json
Normal file
23
docs/examples/obfs-tls-session-ticket/blinksocks.server.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"service": "tcp://0.0.0.0:14938",
|
||||
"key": "had2a8#(kVKA2&gx",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ss-aead-cipher",
|
||||
"params": {
|
||||
"method": "aes-256-gcm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "obfs-tls1.2-ticket",
|
||||
"params": {
|
||||
"sni": [
|
||||
"example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
23
docs/examples/shadowsocks/README.md
Normal file
23
docs/examples/shadowsocks/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# shadowsocks
|
||||
|
||||
**Minimal Version Required: v1.x**
|
||||
|
||||
To work with **shadowsocks**, you can just add two presets:
|
||||
|
||||
**AEAD Ciphers(Newer Versions), Recommend**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ss-aead-cipher", "params": {"method": "aes-256-gcm"}}
|
||||
]
|
||||
```
|
||||
|
||||
**Steam Ciphers(Older Versions)**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ss-stream-cipher", "params": {"method": "aes-256-cfb"}}
|
||||
]
|
||||
```
|
18
docs/examples/shadowsocks/blinksocks.client.json
Normal file
18
docs/examples/shadowsocks/blinksocks.client.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tcp://example.com:49849",
|
||||
"key": "XyDw&JndtwhV?m<6",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ss-aead-cipher",
|
||||
"params": {
|
||||
"method": "aes-256-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
15
docs/examples/shadowsocks/blinksocks.server.json
Normal file
15
docs/examples/shadowsocks/blinksocks.server.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"service": "tcp://0.0.0.0:49849",
|
||||
"key": "XyDw&JndtwhV?m<6",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-256-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
42
docs/examples/shadowsocksr/README.md
Normal file
42
docs/examples/shadowsocksr/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# shadowsocksr
|
||||
|
||||
**Minimal Version Required: v2.x**
|
||||
|
||||
> NOTE: To work with shadowsocksR, you must add both "ss-base" and "ss-stream-cipher".
|
||||
|
||||
<details>
|
||||
<summary>Notice in shadowsocksR config</summary>
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"method": "aes-128-ctr",
|
||||
"protocol": "auth_aes128_md5",
|
||||
"protocol_param": "", // protocol_param must be empty
|
||||
"obfs": "plain", // obfs must be "plain"
|
||||
"obfs_param": "",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**auth_aes128_md5 / auth_aes128_sha1**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ssr-auth-aes128-md5"},
|
||||
{"name": "ss-stream-cipher", "params": {"method": "aes-256-ctr"}}
|
||||
]
|
||||
```
|
||||
|
||||
**auth_chain_a / auth_chain_b**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ssr-auth-chain-a"},
|
||||
{"name": "ss-stream-cipher", "params": {"method": "none"}}
|
||||
]
|
||||
```
|
21
docs/examples/shadowsocksr/blinksocks.client.json
Normal file
21
docs/examples/shadowsocksr/blinksocks.client.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tcp://example.com:24045",
|
||||
"key": "?4H}vrE_[WQj9[>F",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ssr-auth-chain-a"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "none"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
18
docs/examples/shadowsocksr/blinksocks.server.json
Normal file
18
docs/examples/shadowsocksr/blinksocks.server.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"service": "tcp://0.0.0.0:24045",
|
||||
"key": "?4H}vrE_[WQj9[>F",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ssr-auth-chain-a"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "none"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
40
docs/examples/tls/README.md
Normal file
40
docs/examples/tls/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# tls
|
||||
|
||||
**Minimal Version Required: v2.x**
|
||||
|
||||
blinksocks can transfer data using `tls`:
|
||||
|
||||
```
|
||||
+-------------+ +-------------+ +------------+
|
||||
| | tls://site.com/path | | tcp:// | |
|
||||
| bs-client <-----------------------> bs-server <-----------> Target |
|
||||
| | | | | |
|
||||
+-------------+ +-------------+ +------------+
|
||||
```
|
||||
|
||||
When use `tls://` as transport, make sure both `tls_cert` and `tls_key` is provided to `bs-server`.
|
||||
|
||||
> If your are using self-signed certificate on server, please also provide the same `tls_cert` on client and also set `"tls_cert_self_signed": true`.
|
||||
|
||||
Make sure you provide **Common Name** of certificate NOT IP in client config:
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"server": {
|
||||
"service": "tls://<Common Name>:<port>",
|
||||
"tls_cert": "cert.pem",
|
||||
"tls_cert_self_signed": true
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Generate key.pem and cert.pem
|
||||
|
||||
```
|
||||
// self-signed certificate
|
||||
$ openssl req -x509 -newkey rsa:4096 -nodes -sha256 -subj '/CN=example.com' \
|
||||
-keyout key.pem -out cert.pem
|
||||
```
|
17
docs/examples/tls/blinksocks.client.json
Normal file
17
docs/examples/tls/blinksocks.client.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tls://example.com:11902",
|
||||
"key": "NdFCdXFK/TTP2GdU",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
}
|
||||
],
|
||||
"tls_cert": "cert.pem",
|
||||
"tls_cert_self_signed": true
|
||||
}
|
||||
}
|
14
docs/examples/tls/blinksocks.server.json
Normal file
14
docs/examples/tls/blinksocks.server.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"service": "tls://0.0.0.0:11902",
|
||||
"key": "NdFCdXFK/TTP2GdU",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
}
|
||||
],
|
||||
"tls_key": "key.pem",
|
||||
"tls_cert": "cert.pem"
|
||||
}
|
67
docs/examples/v2ray-vmess/README.md
Normal file
67
docs/examples/v2ray-vmess/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# v2ray-vmess
|
||||
|
||||
**Minimal Version Required: v2.x**
|
||||
|
||||
> NOTE: To work with v2ray vmess, you should only provide "v2ray-vmess" in preset list.
|
||||
|
||||
<details>
|
||||
<summary>v2ray client</summary>
|
||||
|
||||
```
|
||||
"outbound": {
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"port": 10086,
|
||||
"users": [
|
||||
{
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm",
|
||||
"alterId": 0 // [must be the default value: 0]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"mux": {
|
||||
"enabled": false // [must be false]
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>v2ray server</summary>
|
||||
|
||||
```
|
||||
"inbound": {
|
||||
"port": 10086,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"level": 1,
|
||||
"alterId": 0 // [must be the default value: 0]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{
|
||||
"name": "v2ray-vmess",
|
||||
"params": {
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
16
docs/examples/v2ray-vmess/blinksocks.client.json
Normal file
16
docs/examples/v2ray-vmess/blinksocks.client.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "tcp://example.com:65282",
|
||||
"key": "z{]5AWaxEFCMTKA,",
|
||||
"presets": [
|
||||
{
|
||||
"name": "v2ray-vmess",
|
||||
"params": {
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
13
docs/examples/v2ray-vmess/blinksocks.server.json
Normal file
13
docs/examples/v2ray-vmess/blinksocks.server.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"service": "tcp://0.0.0.0:65282",
|
||||
"key": "z{]5AWaxEFCMTKA,",
|
||||
"presets": [
|
||||
{
|
||||
"name": "v2ray-vmess",
|
||||
"params": {
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
9
docs/examples/websocket-caddy-tls/Caddyfile
Normal file
9
docs/examples/websocket-caddy-tls/Caddyfile
Normal file
@ -0,0 +1,9 @@
|
||||
example.com {
|
||||
proxy /<your-custom-path> 127.0.0.1:59463 {
|
||||
websocket
|
||||
header_upstream Host {host}
|
||||
header_upstream X-Real-IP {remote}
|
||||
header_upstream X-Forwarded-For {remote}
|
||||
header_upstream X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
20
docs/examples/websocket-caddy-tls/README.md
Normal file
20
docs/examples/websocket-caddy-tls/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# websocket-caddy-tls
|
||||
|
||||
**Minimal Version Required: v3.3.1**
|
||||
|
||||
blinksocks can transfer data through [caddy] proxy server using [WebSocket/TLS]:
|
||||
|
||||
```
|
||||
+--------------------------------------------------+
|
||||
| Caddy Server |
|
||||
+-------------+ | +-----------+ | +------------+
|
||||
| | wss://site.com/path | :433 ws://127.0.0.1:1234 | | | tcp:// | |
|
||||
| bs-client <-----------------------> proxy /path +--------------------> bs-server <-------------> Target |
|
||||
| | (encrypted) | (encrypted) | | | (raw) | |
|
||||
+-------------+ | +-----------+ | +------------+
|
||||
| |
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
[caddy]: https://caddyserver.com
|
||||
[WebSocket/TLS]: ../websocket-tls
|
15
docs/examples/websocket-caddy-tls/blinksocks.client.json
Normal file
15
docs/examples/websocket-caddy-tls/blinksocks.client.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "wss://example.com:443/your-custom-path",
|
||||
"key": "8;:2%]zTbPc[2g-%",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
12
docs/examples/websocket-caddy-tls/blinksocks.server.json
Normal file
12
docs/examples/websocket-caddy-tls/blinksocks.server.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"service": "ws://0.0.0.0:59463/your-custom-path",
|
||||
"key": "8;:2%]zTbPc[2g-%",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
}
|
||||
]
|
||||
}
|
20
docs/examples/websocket-tls/README.md
Normal file
20
docs/examples/websocket-tls/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# websocket-tls
|
||||
|
||||
**Minimal Version Required: v3.3.1**
|
||||
|
||||
blinksocks can transfer data using [WebSocket/TLS]:
|
||||
|
||||
|
||||
```
|
||||
+-------------+ +-------------+ +------------+
|
||||
| | wss://site.com/path | | tcp:// | |
|
||||
| bs-client <-----------------------> bs-server <-----------> Target |
|
||||
| | (encrypted) | | (raw) | |
|
||||
+-------------+ +-------------+ +------------+
|
||||
```
|
||||
|
||||
When use `wss://` as transport, make sure both `tls_cert` and `tls_key` is provided to `bs-server`.
|
||||
|
||||
> If your are using self-signed certificate on server, please also provide the same `tls_cert` on client and set `"tls_cert_self_signed": true`.
|
||||
|
||||
[WebSocket/TLS]: ../websocket-tls
|
17
docs/examples/websocket-tls/blinksocks.client.json
Normal file
17
docs/examples/websocket-tls/blinksocks.client.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "wss://example.com:48079",
|
||||
"key": "98{U64+Z4bX#d,Ra",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
}
|
||||
],
|
||||
"tls_cert": "cert.pem",
|
||||
"tls_cert_self_signed": true
|
||||
}
|
||||
}
|
14
docs/examples/websocket-tls/blinksocks.server.json
Normal file
14
docs/examples/websocket-tls/blinksocks.server.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"service": "wss://0.0.0.0:48079",
|
||||
"key": "98{U64+Z4bX#d,Ra",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
}
|
||||
],
|
||||
"tls_key": "key.pem",
|
||||
"tls_cert": "cert.pem"
|
||||
}
|
13
docs/examples/websocket/README.md
Normal file
13
docs/examples/websocket/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# websocket
|
||||
|
||||
**Minimal Version Required: v2.6.2**
|
||||
|
||||
blinksocks can transfer data using `websocket`:
|
||||
|
||||
```
|
||||
+-------------+ +-------------+ +------------+
|
||||
| | ws://site.com/path | | tcp:// | |
|
||||
| bs-client <----------------------> bs-server <-----------> Target |
|
||||
| | | | | |
|
||||
+-------------+ +-------------+ +------------+
|
||||
```
|
21
docs/examples/websocket/blinksocks.client.json
Normal file
21
docs/examples/websocket/blinksocks.client.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"service": "socks5://127.0.0.1:1080",
|
||||
"server": {
|
||||
"service": "ws://127.0.0.1:6336",
|
||||
"key": "?B4-y[tsFQCV/zK%",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
18
docs/examples/websocket/blinksocks.server.json
Normal file
18
docs/examples/websocket/blinksocks.server.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"service": "ws://0.0.0.0:6336",
|
||||
"key": "?B4-y[tsFQCV/zK%",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -326,7 +326,7 @@ all features from **ss-aead-cipher** and prevent server from being detected by p
|
||||
|
||||
## Have trouble in choosing presets?
|
||||
|
||||
Here is a [list](./RECOMMENDATIONS.md) of recommended conbinations.
|
||||
Here is a [list](../examples) of recommended conbinations.
|
||||
|
||||
[base-auth]: ../../src/presets/base-auth.js
|
||||
[ss-base]: ../../src/presets/ss-base.js
|
||||
|
@ -1,167 +0,0 @@
|
||||
# Recommended Combinations
|
||||
|
||||
## Work with shadowsocks
|
||||
|
||||
To work with **shadowsocks**, please choose one of the following configurations:
|
||||
|
||||
**Steam Ciphers(Older Versions)**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ss-stream-cipher", "params": {"method": "aes-256-cfb"}}
|
||||
]
|
||||
```
|
||||
|
||||
**AEAD Ciphers(Newer Versions)**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ss-aead-cipher", "params": {"method": "aes-256-gcm"}}
|
||||
]
|
||||
```
|
||||
|
||||
## Work with shadowsocksR
|
||||
|
||||
> NOTE: To work with shadowsocksR, you must add both "ss-base" and "ss-stream-cipher".
|
||||
|
||||
<details>
|
||||
<summary>Notice in shadowsocksR config</summary>
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"method": "aes-128-ctr",
|
||||
"protocol": "auth_aes128_md5",
|
||||
"protocol_param": "", // protocol_param must be empty
|
||||
"obfs": "plain", // obfs must be "plain"
|
||||
"obfs_param": "",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**auth_aes128_md5 / auth_aes128_sha1**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ssr-auth-aes128-md5"},
|
||||
{"name": "ss-stream-cipher", "params": {"method": "aes-256-ctr"}}
|
||||
]
|
||||
```
|
||||
|
||||
**auth_chain_a / auth_chain_b**
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ssr-auth-chain-a"},
|
||||
{"name": "ss-stream-cipher", "params": {"method": "none"}}
|
||||
]
|
||||
```
|
||||
|
||||
## Work with v2ray vmess
|
||||
|
||||
> Notice in v2ray configs:
|
||||
|
||||
<details>
|
||||
<summary>v2ray client</summary>
|
||||
|
||||
```
|
||||
"outbound": {
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"port": 10086,
|
||||
"users": [
|
||||
{
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm",
|
||||
"alterId": 0 // [must be the default value: 0]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"mux": {
|
||||
"enabled": false // [must be false]
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>v2ray server</summary>
|
||||
|
||||
```
|
||||
"inbound": {
|
||||
"port": 10086,
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"level": 1,
|
||||
"alterId": 0 // [must be the default value: 0]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{
|
||||
"name": "v2ray-vmess",
|
||||
"params": {
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Avoid Bad QoS
|
||||
|
||||
You can use **http** or **tls** obfuscator to avoid bad [QoS], **tls** is recommended.
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "ss-aead-cipher", "params": {"method": "aes-256-gcm"}},
|
||||
{"name": "obfs-tls1.2-ticket", "params": {"sni": ["example.com"]}}
|
||||
]
|
||||
```
|
||||
|
||||
## To prevent traffic analysis
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "obfs-random-padding"},
|
||||
{"name": "ss-stream-cipher","params": {"method": "aes-128-ctr"}}
|
||||
]
|
||||
```
|
||||
|
||||
## To prevent traffic analysis and ensure integrity as well
|
||||
|
||||
```
|
||||
"presets": [
|
||||
{"name": "ss-base"},
|
||||
{"name": "obfs-random-padding"},
|
||||
{"name": "ss-aead-cipher","params": {"method": "aes-128-gcm"}}
|
||||
]
|
||||
```
|
||||
|
||||
> You can also check out [benchmark] to choose a combination you prefer.
|
||||
|
||||
[QoS]: https://en.wikipedia.org/wiki/Quality_of_service
|
||||
[benchmark]: ../benchmark/README.md
|
@ -52,6 +52,23 @@ After init, you should edit `blinksocks.client.json` to tell blinksocks client w
|
||||
|
||||
You can also check out [Configuration](../config) for explanation of every option.
|
||||
|
||||
## Test Using curl
|
||||
|
||||
[curl](https://curl.haxx.se/) is an useful tool which can send requests via a variety of proxy protocols:
|
||||
|
||||
```
|
||||
# SOCKS
|
||||
$ curl -Lx socks5://localhost:1080 www.google.com
|
||||
$ curl -Lx socks4://localhost:1080 www.google.com
|
||||
$ curl -Lx socks4a://localhost:1080 www.google.com
|
||||
|
||||
# HTTP
|
||||
$ curl -Lx http://localhost:1080 www.google.com
|
||||
|
||||
# HTTPS
|
||||
$ curl --proxy-insecure -Lx https://localhost:1080 www.google.com
|
||||
```
|
||||
|
||||
## Run in production
|
||||
|
||||
### Using pm2
|
||||
@ -117,25 +134,6 @@ WantedBy=multi-user.target
|
||||
# journalctl -u blinksocks.service
|
||||
```
|
||||
|
||||
### Using executables
|
||||
|
||||
```
|
||||
// download archive from releases page
|
||||
$ wget https://github.com/blinksocks/blinksocks/releases/download/v2.5.3/blinksocks-linux-x64-v2.5.3.gz
|
||||
|
||||
// you'd better check sha256sum listed in sha256sum.txt
|
||||
$ wget https://github.com/blinksocks/blinksocks/releases/download/v2.5.3/sha256sum.txt
|
||||
|
||||
// decompress
|
||||
$ gunzip blinksocks-linux-x64-v2.5.3.gz
|
||||
|
||||
// grant executable permission
|
||||
$ chmod +x blinksocks-linux-x64-v2.5.3
|
||||
|
||||
// run directly
|
||||
$ ./blinksocks-linux-x64-v2.5.3 --help
|
||||
```
|
||||
|
||||
## Work with browsers
|
||||
|
||||
Most of the time, you are surfing the Internet via web browsers such as Firefox or Google Chrome.
|
||||
|
@ -3,7 +3,7 @@
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.MUX_CLOSE_CONN = exports.MUX_DATA_FRAME = exports.MUX_NEW_CONN = exports.CONNECTED_TO_REMOTE = exports.CONNECT_TO_REMOTE = exports.PRESET_FAILED = exports.MAX_BUFFERED_SIZE = exports.PIPE_DECODE = exports.PIPE_ENCODE = exports.APP_ID = undefined;
|
||||
exports.PROTOCOL_DEFAULT_PORTS = exports.MUX_CLOSE_CONN = exports.MUX_DATA_FRAME = exports.MUX_NEW_CONN = exports.CONNECTED_TO_REMOTE = exports.CONNECT_TO_REMOTE = exports.PRESET_FAILED = exports.MAX_BUFFERED_SIZE = exports.PIPE_DECODE = exports.PIPE_ENCODE = exports.APP_ID = undefined;
|
||||
|
||||
var _crypto = require('./utils/crypto');
|
||||
|
||||
@ -15,4 +15,13 @@ const CONNECT_TO_REMOTE = exports.CONNECT_TO_REMOTE = 'CONNECT_TO_REMOTE';
|
||||
const CONNECTED_TO_REMOTE = exports.CONNECTED_TO_REMOTE = 'CONNECTED_TO_REMOTE';
|
||||
const MUX_NEW_CONN = exports.MUX_NEW_CONN = 'MUX_NEW_CONN';
|
||||
const MUX_DATA_FRAME = exports.MUX_DATA_FRAME = 'MUX_DATA_FRAME';
|
||||
const MUX_CLOSE_CONN = exports.MUX_CLOSE_CONN = 'MUX_CLOSE_CONN';
|
||||
const MUX_CLOSE_CONN = exports.MUX_CLOSE_CONN = 'MUX_CLOSE_CONN';
|
||||
|
||||
const PROTOCOL_DEFAULT_PORTS = exports.PROTOCOL_DEFAULT_PORTS = {
|
||||
'ftp:': 21,
|
||||
'gopher:': 70,
|
||||
'http:': 80,
|
||||
'https:': 443,
|
||||
'ws:': 80,
|
||||
'wss:': 443
|
||||
};
|
@ -41,6 +41,8 @@ var _lodash2 = _interopRequireDefault(_lodash);
|
||||
|
||||
var _acl = require('./acl');
|
||||
|
||||
var _constants = require('../constants');
|
||||
|
||||
var _presets = require('../presets');
|
||||
|
||||
var _defs = require('../presets/defs');
|
||||
@ -62,17 +64,22 @@ class Config {
|
||||
this.local_search_params = null;
|
||||
this.local_host = null;
|
||||
this.local_port = null;
|
||||
this.local_pathname = null;
|
||||
this.server = null;
|
||||
this.is_client = null;
|
||||
this.is_server = null;
|
||||
this.https_key = null;
|
||||
this.https_cert = null;
|
||||
this.timeout = null;
|
||||
this.redirect = null;
|
||||
this.dns_expire = null;
|
||||
this.dns = null;
|
||||
this.transport = null;
|
||||
this.server_protocol = null;
|
||||
this.server_host = null;
|
||||
this.server_port = null;
|
||||
this.server_pathname = null;
|
||||
this.tls_cert = null;
|
||||
this.tls_cert_self_signed = false;
|
||||
this.tls_key = null;
|
||||
this.key = null;
|
||||
this.acl = false;
|
||||
@ -81,20 +88,21 @@ class Config {
|
||||
this.acl_tries = {};
|
||||
this.presets = null;
|
||||
this.udp_presets = null;
|
||||
this.mux = null;
|
||||
this.mux = false;
|
||||
this.mux_concurrency = null;
|
||||
this.log_path = null;
|
||||
this.log_level = null;
|
||||
this.log_max_days = null;
|
||||
this.stores = [];
|
||||
|
||||
const { protocol, hostname, port, searchParams, username, password } = new _url.URL(json.service);
|
||||
const { protocol, hostname, port, pathname, searchParams, username, password } = new _url.URL(json.service);
|
||||
this.local_protocol = protocol.slice(0, -1);
|
||||
this.local_username = username;
|
||||
this.local_password = password;
|
||||
this.local_search_params = searchParams;
|
||||
this.local_host = hostname;
|
||||
this.local_port = +port;
|
||||
this.local_port = +port || +_constants.PROTOCOL_DEFAULT_PORTS[protocol];
|
||||
this.local_pathname = pathname;
|
||||
|
||||
let server;
|
||||
|
||||
@ -121,6 +129,13 @@ class Config {
|
||||
this._initServer(server);
|
||||
}
|
||||
|
||||
if (this.is_client && this.local_protocol === 'https') {
|
||||
_utils.logger.info(`[config] loading ${json.https_cert}`);
|
||||
this.https_cert = loadFileSync(json.https_cert);
|
||||
_utils.logger.info(`[config] loading ${json.https_key}`);
|
||||
this.https_key = loadFileSync(json.https_key);
|
||||
}
|
||||
|
||||
this.timeout = json.timeout !== undefined ? json.timeout * 1e3 : 600 * 1e3;
|
||||
this.dns_expire = json.dns_expire !== undefined ? json.dns_expire * 1e3 : _utils.DNS_DEFAULT_EXPIRE;
|
||||
|
||||
@ -133,14 +148,20 @@ class Config {
|
||||
}
|
||||
|
||||
_initServer(server) {
|
||||
const { protocol, hostname, port } = new _url.URL(server.service);
|
||||
this.transport = protocol.slice(0, -1);
|
||||
const { protocol, hostname, port, pathname } = new _url.URL(server.service);
|
||||
this.server_protocol = protocol.slice(0, -1);
|
||||
this.server_host = hostname;
|
||||
this.server_port = +port;
|
||||
this.server_port = +port || +_constants.PROTOCOL_DEFAULT_PORTS[protocol];
|
||||
this.server_pathname = pathname;
|
||||
|
||||
if (this.transport === 'tls') {
|
||||
_utils.logger.info(`[config] loading ${server.tls_cert}`);
|
||||
this.tls_cert = loadFileSync(server.tls_cert);
|
||||
if (this.server_protocol === 'tls' || this.server_protocol === 'wss') {
|
||||
if (this.is_client) {
|
||||
this.tls_cert_self_signed = !!server.tls_cert_self_signed;
|
||||
}
|
||||
if (this.tls_cert_self_signed || this.is_server) {
|
||||
_utils.logger.info(`[config] loading ${server.tls_cert}`);
|
||||
this.tls_cert = loadFileSync(server.tls_cert);
|
||||
}
|
||||
if (this.is_server) {
|
||||
_utils.logger.info(`[config] loading ${server.tls_key}`);
|
||||
this.tls_key = loadFileSync(server.tls_key);
|
||||
@ -164,7 +185,7 @@ class Config {
|
||||
this.redirect = server.redirect;
|
||||
}
|
||||
|
||||
this.mux = !!server.mux;
|
||||
this.mux = server.mux === true;
|
||||
if (this.is_client) {
|
||||
this.mux_concurrency = server.mux_concurrency || 10;
|
||||
}
|
||||
@ -226,15 +247,15 @@ class Config {
|
||||
throw Error('"service" must be provided as "<protocol>://<host>:<port>[?params]"');
|
||||
}
|
||||
|
||||
const { protocol: _protocol, hostname, port, searchParams } = new _url.URL(json.service);
|
||||
const { protocol, hostname, port: _port, searchParams } = new _url.URL(json.service);
|
||||
|
||||
if (typeof _protocol !== 'string') {
|
||||
if (typeof protocol !== 'string') {
|
||||
throw Error('service.protocol is invalid');
|
||||
}
|
||||
|
||||
const protocol = _protocol.slice(0, -1);
|
||||
const proto = protocol.slice(0, -1);
|
||||
const available_client_protocols = ['tcp', 'http', 'https', 'socks', 'socks5', 'socks4', 'socks4a'];
|
||||
if (!available_client_protocols.includes(protocol)) {
|
||||
if (!available_client_protocols.includes(proto)) {
|
||||
throw Error(`service.protocol must be: ${available_client_protocols.join(', ')}`);
|
||||
}
|
||||
|
||||
@ -242,11 +263,12 @@ class Config {
|
||||
throw Error('service.host is invalid');
|
||||
}
|
||||
|
||||
const port = _port || _constants.PROTOCOL_DEFAULT_PORTS[protocol] || '';
|
||||
if (!(0, _utils.isValidPort)(+port)) {
|
||||
throw Error('service.port is invalid');
|
||||
}
|
||||
|
||||
if (protocol === 'tcp') {
|
||||
if (proto === 'tcp') {
|
||||
const forward = searchParams.get('forward');
|
||||
|
||||
if (!forward) {
|
||||
@ -262,6 +284,15 @@ class Config {
|
||||
}
|
||||
}
|
||||
|
||||
if (proto === 'https') {
|
||||
if (typeof json.https_cert !== 'string' || json.https_cert === '') {
|
||||
throw Error('"https_cert" must be provided');
|
||||
}
|
||||
if (typeof json.https_key !== 'string' || json.https_key === '') {
|
||||
throw Error('"https_key" must be provided');
|
||||
}
|
||||
}
|
||||
|
||||
let server;
|
||||
|
||||
if (json.servers) {
|
||||
@ -308,23 +339,29 @@ class Config {
|
||||
throw Error('"service" must be provided as "<protocol>://<host>:<port>[?params]"');
|
||||
}
|
||||
|
||||
const { protocol: _protocol, hostname, port } = new _url.URL(server.service);
|
||||
const { protocol, hostname, port: _port } = new _url.URL(server.service);
|
||||
|
||||
if (typeof _protocol !== 'string') {
|
||||
if (typeof protocol !== 'string') {
|
||||
throw Error('service.protocol is invalid');
|
||||
}
|
||||
|
||||
const protocol = _protocol.slice(0, -1);
|
||||
const available_server_protocols = ['tcp', 'ws', 'tls'];
|
||||
if (!available_server_protocols.includes(protocol)) {
|
||||
const proto = protocol.slice(0, -1);
|
||||
const available_server_protocols = ['tcp', 'ws', 'wss', 'tls'];
|
||||
if (!available_server_protocols.includes(proto)) {
|
||||
throw Error(`service.protocol must be: ${available_server_protocols.join(', ')}`);
|
||||
}
|
||||
|
||||
if (protocol === 'tls') {
|
||||
if (typeof server.tls_cert !== 'string' || server.tls_cert === '') {
|
||||
throw Error('"tls_cert" must be provided');
|
||||
if (proto === 'tls' || proto === 'wss') {
|
||||
if (from_client && server.tls_cert_self_signed) {
|
||||
if (typeof server.tls_cert !== 'string' || server.tls_cert === '') {
|
||||
throw Error('"tls_cert" must be provided when "tls_cert_self_signed" is set');
|
||||
}
|
||||
}
|
||||
|
||||
if (!from_client) {
|
||||
if (typeof server.tls_cert !== 'string' || server.tls_cert === '') {
|
||||
throw Error('"tls_cert" must be provided');
|
||||
}
|
||||
if (typeof server.tls_key !== 'string' || server.tls_key === '') {
|
||||
throw Error('"tls_key" must be provided');
|
||||
}
|
||||
@ -335,6 +372,7 @@ class Config {
|
||||
throw Error('service.host is invalid');
|
||||
}
|
||||
|
||||
const port = _port || _constants.PROTOCOL_DEFAULT_PORTS[protocol] || '';
|
||||
if (!(0, _utils.isValidPort)(+port)) {
|
||||
throw Error('service.port is invalid');
|
||||
}
|
||||
|
@ -19,6 +19,14 @@ var _net = require('net');
|
||||
|
||||
var _net2 = _interopRequireDefault(_net);
|
||||
|
||||
var _http = require('http');
|
||||
|
||||
var _http2 = _interopRequireDefault(_http);
|
||||
|
||||
var _https = require('https');
|
||||
|
||||
var _https2 = _interopRequireDefault(_https);
|
||||
|
||||
var _url = require('url');
|
||||
|
||||
var _tls = require('tls');
|
||||
@ -43,6 +51,8 @@ var _relay = require('./relay');
|
||||
|
||||
var _muxRelay = require('./mux-relay');
|
||||
|
||||
var _speedTester = require('./speed-tester');
|
||||
|
||||
var _utils = require('../utils');
|
||||
|
||||
var _proxies = require('../proxies');
|
||||
@ -67,14 +77,23 @@ class Hub {
|
||||
this._tcpRelays = new Map();
|
||||
this._muxRelays = new Map();
|
||||
this._udpRelays = null;
|
||||
this._prevHrtime = process.hrtime();
|
||||
this._upSpeedTester = null;
|
||||
this._dlSpeedTester = null;
|
||||
this._totalRead = 0;
|
||||
this._totalWritten = 0;
|
||||
this._prevTotalRead = 0;
|
||||
this._prevTotalWritten = 0;
|
||||
this._connQueue = [];
|
||||
this._udpCleanerTimer = null;
|
||||
|
||||
this._onRead = size => {
|
||||
this._totalRead += size;
|
||||
this._dlSpeedTester.feed(size);
|
||||
};
|
||||
|
||||
this._onWrite = size => {
|
||||
this._totalWritten += size;
|
||||
this._upSpeedTester.feed(size);
|
||||
};
|
||||
|
||||
this._onConnection = (socket, proxyRequest = null) => {
|
||||
_utils.logger.verbose(`[hub] [${socket.remoteAddress}:${socket.remotePort}] connected`);
|
||||
|
||||
@ -117,8 +136,8 @@ class Hub {
|
||||
|
||||
relay.on('_error', err => updateConnStatus('error', err.message));
|
||||
relay.on('_connect', targetAddress => updateConnStatus('target', targetAddress));
|
||||
relay.on('_read', size => this._totalRead += size);
|
||||
relay.on('_write', size => this._totalWritten += size);
|
||||
relay.on('_read', this._onRead);
|
||||
relay.on('_write', this._onWrite);
|
||||
relay.on('close', () => {
|
||||
updateConnStatus('close');
|
||||
this._tcpRelays.delete(relay.id);
|
||||
@ -130,6 +149,8 @@ class Hub {
|
||||
|
||||
this._config = new _config.Config(config);
|
||||
this._udpRelays = (0, _lruCache2.default)({ max: 500, maxAge: 1e5, dispose: (_, relay) => relay.destroy() });
|
||||
this._upSpeedTester = new _speedTester.SpeedTester();
|
||||
this._dlSpeedTester = new _speedTester.SpeedTester();
|
||||
}
|
||||
|
||||
async run() {
|
||||
@ -190,21 +211,11 @@ class Hub {
|
||||
}
|
||||
|
||||
getUploadSpeed() {
|
||||
const [sec, nano] = process.hrtime(this._prevHrtime);
|
||||
const totalWritten = this._totalWritten;
|
||||
const diff = totalWritten - this._prevTotalWritten;
|
||||
const speed = Math.ceil(diff / (sec + nano / 1e9));
|
||||
this._prevTotalWritten = totalWritten;
|
||||
return speed;
|
||||
return this._upSpeedTester.getSpeed();
|
||||
}
|
||||
|
||||
getDownloadSpeed() {
|
||||
const [sec, nano] = process.hrtime(this._prevHrtime);
|
||||
const totalRead = this._totalRead;
|
||||
const diff = totalRead - this._prevTotalRead;
|
||||
const speed = Math.ceil(diff / (sec + nano / 1e9));
|
||||
this._prevTotalRead = totalRead;
|
||||
return speed;
|
||||
return this._dlSpeedTester.getSpeed();
|
||||
}
|
||||
|
||||
getConnStatuses() {
|
||||
@ -227,6 +238,7 @@ class Hub {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { local_protocol, local_search_params, local_host, local_port } = this._config;
|
||||
const { local_username: username, local_password: password } = this._config;
|
||||
const { https_key, https_cert } = this._config;
|
||||
let server = null;
|
||||
switch (local_protocol) {
|
||||
case 'tcp':
|
||||
@ -242,11 +254,22 @@ class Hub {
|
||||
case 'socks5':
|
||||
case 'socks4':
|
||||
case 'socks4a':
|
||||
server = _proxies.socks.createServer({ bindAddress: local_host, bindPort: local_port, username, password });
|
||||
server = _proxies.socks.createServer({
|
||||
bindAddress: local_host,
|
||||
bindPort: local_port,
|
||||
username,
|
||||
password
|
||||
});
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
server = _proxies.http.createServer({ username, password });
|
||||
server = _proxies.http.createServer({
|
||||
secure: local_protocol === 'https',
|
||||
https_key,
|
||||
https_cert,
|
||||
username,
|
||||
password
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return reject(Error(`unsupported protocol: "${local_protocol}"`));
|
||||
@ -266,18 +289,19 @@ class Hub {
|
||||
}
|
||||
|
||||
async _createServerOnServer() {
|
||||
const { local_protocol, local_host, local_port, local_pathname, tls_key, tls_cert } = this._config;
|
||||
return new Promise((resolve, reject) => {
|
||||
const address = {
|
||||
host: this._config.local_host,
|
||||
port: this._config.local_port
|
||||
host: local_host,
|
||||
port: local_port
|
||||
};
|
||||
const onListening = server => {
|
||||
const service = `${this._config.local_protocol}://${this._config.local_host}:${this._config.local_port}`;
|
||||
const service = `${local_protocol}://${local_host}:${local_port}` + (local_pathname ? local_pathname : '');
|
||||
_utils.logger.info(`[hub] blinksocks server is running at ${service}`);
|
||||
resolve(server);
|
||||
};
|
||||
let server = null;
|
||||
switch (this._config.local_protocol) {
|
||||
switch (local_protocol) {
|
||||
case 'tcp':
|
||||
{
|
||||
server = _net2.default.createServer();
|
||||
@ -285,29 +309,38 @@ class Hub {
|
||||
server.listen(address, () => onListening(server));
|
||||
break;
|
||||
}
|
||||
case 'wss':
|
||||
case 'ws':
|
||||
{
|
||||
server = new _ws2.default.Server(_extends({}, address, {
|
||||
let http_s_server = null;
|
||||
if (local_protocol === 'wss') {
|
||||
http_s_server = _https2.default.createServer({ key: tls_key, cert: tls_cert });
|
||||
} else {
|
||||
http_s_server = _http2.default.createServer();
|
||||
}
|
||||
server = new _ws2.default.Server({
|
||||
server: http_s_server,
|
||||
path: local_pathname,
|
||||
perMessageDeflate: false
|
||||
}));
|
||||
});
|
||||
server.getConnections = server._server.getConnections.bind(server._server);
|
||||
server.on('connection', (ws, req) => {
|
||||
ws.remoteAddress = req.connection.remoteAddress;
|
||||
ws.remotePort = req.connection.remotePort;
|
||||
this._onConnection(ws);
|
||||
});
|
||||
server.on('listening', () => onListening(server));
|
||||
http_s_server.listen(address, () => onListening(http_s_server));
|
||||
break;
|
||||
}
|
||||
case 'tls':
|
||||
{
|
||||
server = _tls2.default.createServer({ key: [this._config.tls_key], cert: [this._config.tls_cert] });
|
||||
server = _tls2.default.createServer({ key: tls_key, cert: tls_cert });
|
||||
server.on('secureConnection', this._onConnection);
|
||||
server.listen(address, () => onListening(server));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return reject(Error(`unsupported protocol: "${this._config.local_protocol}"`));
|
||||
return reject(Error(`unsupported protocol: "${local_protocol}"`));
|
||||
}
|
||||
server.on('error', reject);
|
||||
});
|
||||
@ -385,8 +418,8 @@ class Hub {
|
||||
muxRelay = this._createRelay(context, true);
|
||||
muxRelay.on('_error', err => updateConnStatus('error', err.message));
|
||||
muxRelay.on('_connect', targetAddress => updateConnStatus('target', targetAddress));
|
||||
muxRelay.on('_read', size => this._totalRead += size);
|
||||
muxRelay.on('_write', size => this._totalWritten += size);
|
||||
muxRelay.on('_read', this._onRead);
|
||||
muxRelay.on('_write', this._onWrite);
|
||||
muxRelay.on('close', () => {
|
||||
updateConnStatus('close');
|
||||
this._muxRelays.delete(muxRelay.id);
|
||||
@ -411,9 +444,9 @@ class Hub {
|
||||
|
||||
_createRelay(context, isMux = false) {
|
||||
const props = {
|
||||
config: this._config,
|
||||
context: context,
|
||||
transport: this._config.transport,
|
||||
config: this._config,
|
||||
transport: this._config.server_protocol,
|
||||
presets: this._config.presets
|
||||
};
|
||||
if (isMux) {
|
||||
|
@ -26,40 +26,4 @@ Object.keys(_hub).forEach(function (key) {
|
||||
return _hub[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var _pipe = require('./pipe');
|
||||
|
||||
Object.keys(_pipe).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _pipe[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var _relay = require('./relay');
|
||||
|
||||
Object.keys(_relay).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _relay[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var _muxRelay = require('./mux-relay');
|
||||
|
||||
Object.keys(_muxRelay).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _muxRelay[key];
|
||||
}
|
||||
});
|
||||
});
|
@ -144,6 +144,7 @@ class Relay extends _events2.default {
|
||||
'udp': [_transports.UdpInbound, _transports.UdpOutbound],
|
||||
'tls': [_transports.TlsInbound, _transports.TlsOutbound],
|
||||
'ws': [_transports.WsInbound, _transports.WsOutbound],
|
||||
'wss': [_transports.WssInbound, _transports.WssOutbound],
|
||||
'mux': [_transports.MuxInbound, _transports.MuxOutbound]
|
||||
};
|
||||
let Inbound = null,
|
||||
|
26
lib/core/speed-tester.js
Normal file
26
lib/core/speed-tester.js
Normal file
@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
class SpeedTester {
|
||||
constructor() {
|
||||
this.totalBytes = 0;
|
||||
this.lastTime = Date.now();
|
||||
}
|
||||
|
||||
feed(bytes) {
|
||||
this.totalBytes += bytes;
|
||||
}
|
||||
|
||||
getSpeed() {
|
||||
const now = Date.now();
|
||||
const timeDiff = now - this.lastTime;
|
||||
const speed = this.totalBytes / (timeDiff / 1e3);
|
||||
this.lastTime = now;
|
||||
this.totalBytes = 0;
|
||||
return speed;
|
||||
}
|
||||
|
||||
}
|
||||
exports.SpeedTester = SpeedTester;
|
@ -3,7 +3,7 @@
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.presets = undefined;
|
||||
exports.builtInPresetMap = undefined;
|
||||
exports.getPresetClassByName = getPresetClassByName;
|
||||
|
||||
var _mux = require('./_mux');
|
||||
@ -64,7 +64,20 @@ var _aeadRandomCipher2 = _interopRequireDefault(_aeadRandomCipher);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
const presetMap = {
|
||||
function checkPresetClass(clazz) {
|
||||
if (typeof clazz !== 'function') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const requiredMethods = ['onDestroy', 'onInit', 'beforeOut', 'beforeIn', 'clientOut', 'serverIn', 'serverOut', 'clientIn', 'beforeOutUdp', 'beforeInUdp', 'clientOutUdp', 'serverInUdp', 'serverOutUdp', 'clientInUdp'];
|
||||
if (requiredMethods.some(method => typeof clazz.prototype[method] !== 'function')) {
|
||||
return false;
|
||||
}
|
||||
const requiredStaticMethods = ['onCheckParams', 'onCache'];
|
||||
return !requiredStaticMethods.some(method => typeof clazz[method] !== 'function');
|
||||
}
|
||||
|
||||
const builtInPresetMap = exports.builtInPresetMap = {
|
||||
'mux': _mux2.default,
|
||||
|
||||
'base-auth': _baseAuth2.default,
|
||||
@ -87,24 +100,8 @@ const presetMap = {
|
||||
'aead-random-cipher': _aeadRandomCipher2.default
|
||||
};
|
||||
|
||||
function checkPresetClass(clazz) {
|
||||
if (typeof clazz !== 'function') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const requiredMethods = ['onDestroy', 'onInit', 'beforeOut', 'beforeIn', 'clientOut', 'serverIn', 'serverOut', 'clientIn', 'beforeOutUdp', 'beforeInUdp', 'clientOutUdp', 'serverInUdp', 'serverOutUdp', 'clientInUdp'];
|
||||
if (requiredMethods.some(method => typeof clazz.prototype[method] !== 'function')) {
|
||||
return false;
|
||||
}
|
||||
const requiredStaticMethods = ['onCheckParams', 'onCache'];
|
||||
if (requiredStaticMethods.some(method => typeof clazz[method] !== 'function')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getPresetClassByName(name, allowPrivate = false) {
|
||||
let clazz = presetMap[name];
|
||||
let clazz = builtInPresetMap[name];
|
||||
if (clazz === undefined) {
|
||||
try {
|
||||
clazz = require(name);
|
||||
@ -119,6 +116,4 @@ function getPresetClassByName(name, allowPrivate = false) {
|
||||
throw Error(`cannot load private preset "${name}"`);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
const presets = exports.presets = Object.keys(presetMap);
|
||||
}
|
@ -11,6 +11,10 @@ var _http = require('http');
|
||||
|
||||
var _http2 = _interopRequireDefault(_http);
|
||||
|
||||
var _https = require('https');
|
||||
|
||||
var _https2 = _interopRequireDefault(_https);
|
||||
|
||||
var _utils = require('../utils');
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
@ -27,8 +31,16 @@ function checkBasicAuthorization(credentials, { username, password }) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function createServer({ username, password }) {
|
||||
const server = _http2.default.createServer();
|
||||
function createServer({ secure, https_key, https_cert, username, password }) {
|
||||
const name = secure ? 'https' : 'http';
|
||||
let server = null;
|
||||
if (secure) {
|
||||
server = _https2.default.createServer({ key: https_key, cert: https_cert });
|
||||
} else {
|
||||
server = _http2.default.createServer();
|
||||
}
|
||||
|
||||
const isAuthRequired = username !== '' && password !== '';
|
||||
|
||||
server.on('request', (req, res) => {
|
||||
const { hostname, port, pathname } = new _url.URL(req.url);
|
||||
@ -39,15 +51,15 @@ function createServer({ username, password }) {
|
||||
|
||||
if (hostname === null || !(0, _utils.isValidPort)(_port)) {
|
||||
const remote = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
_utils.logger.warn(`[http] drop invalid http request sent from ${remote}`);
|
||||
_utils.logger.warn(`[${name}] drop invalid http request sent from ${remote}`);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
const proxyAuth = headers['proxy-authorization'];
|
||||
if (proxyAuth && username !== '' && password !== '') {
|
||||
if (isAuthRequired) {
|
||||
const proxyAuth = headers['proxy-authorization'] || '';
|
||||
const [type, credentials] = proxyAuth.split(' ');
|
||||
if (type !== 'Basic' || !checkBasicAuthorization(credentials, { username, password })) {
|
||||
_utils.logger.error(`[http] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
_utils.logger.error(`[${name}] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
return res.end('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
}
|
||||
}
|
||||
@ -82,15 +94,15 @@ function createServer({ username, password }) {
|
||||
|
||||
if (hostname === null || !(0, _utils.isValidPort)(port)) {
|
||||
const remote = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
_utils.logger.warn(`[http] [${appAddress}] drop invalid http CONNECT request sent from ${remote}`);
|
||||
_utils.logger.warn(`[${name}] [${appAddress}] drop invalid http CONNECT request sent from ${remote}`);
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
const proxyAuth = req.headers['proxy-authorization'];
|
||||
if (proxyAuth && username !== '' && password !== '') {
|
||||
if (isAuthRequired) {
|
||||
const proxyAuth = req.headers['proxy-authorization'] || '';
|
||||
const [type, credentials] = proxyAuth.split(' ');
|
||||
if (type !== 'Basic' || !checkBasicAuthorization(credentials, { username, password })) {
|
||||
_utils.logger.error(`[http] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
_utils.logger.error(`[${name}] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
return socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
}
|
||||
}
|
||||
@ -105,8 +117,8 @@ function createServer({ username, password }) {
|
||||
});
|
||||
|
||||
server.on('clientError', (err, socket) => {
|
||||
const appAddress = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
_utils.logger.error(`[http] [${appAddress}] invalid http request: ${err.message}`);
|
||||
const appAddress = `${socket.remoteAddress || ''}:${socket.remotePort || ''}`;
|
||||
_utils.logger.error(`[${name}] [${appAddress}] invalid http request: ${err.message}`);
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
|
@ -220,6 +220,7 @@ const STAGE_DONE = 3;
|
||||
|
||||
function createServer({ bindAddress, bindPort, username, password }) {
|
||||
const server = _net2.default.createServer();
|
||||
const isAuthRequired = username !== '' && password !== '';
|
||||
|
||||
server.on('connection', socket => {
|
||||
const appAddress = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
@ -240,6 +241,11 @@ function createServer({ bindAddress, bindPort, username, password }) {
|
||||
const { method } = request;
|
||||
switch (method) {
|
||||
case METHOD_NO_AUTH:
|
||||
if (isAuthRequired) {
|
||||
_utils.logger.error(`[socks] [${appAddress}] server requires authorization but got METHOD_NO_AUTH`);
|
||||
socket.end(Buffer.from([SOCKS_VERSION_V5, METHOD_NOT_ACCEPTABLE]));
|
||||
break;
|
||||
}
|
||||
stage = STAGE_SOCKS5_REQUEST_MESSAGE;
|
||||
|
||||
socket.write(Buffer.from([SOCKS_VERSION_V5, METHOD_NO_AUTH]));
|
||||
@ -276,7 +282,7 @@ function createServer({ bindAddress, bindPort, username, password }) {
|
||||
} else if (stage === STAGE_SOCKS5_INITIAL_NEGOTIATION_MESSAGE) {
|
||||
request = parseSocks5InitialNegotiation(buffer);
|
||||
if (request !== null) {
|
||||
if (username !== '' && password !== '') {
|
||||
if (isAuthRequired) {
|
||||
if (username !== request.username || password !== request.password) {
|
||||
_utils.logger.error(`[socks] [${appAddress}] invalid socks5 authorization, username=${request.username} password=${request.password}`);
|
||||
socket.end(Buffer.from([SOCKS_VERSION_V5, 0x01]));
|
||||
|
@ -52,6 +52,18 @@ Object.keys(_ws).forEach(function (key) {
|
||||
});
|
||||
});
|
||||
|
||||
var _wss = require('./wss');
|
||||
|
||||
Object.keys(_wss).forEach(function (key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _wss[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var _mux = require('./mux');
|
||||
|
||||
Object.keys(_mux).forEach(function (key) {
|
||||
|
@ -332,7 +332,11 @@ class TcpOutbound extends _defs.Outbound {
|
||||
await this.connect({ host, port });
|
||||
}
|
||||
if (this._config.is_client) {
|
||||
await this.connect({ host: this._config.server_host, port: this._config.server_port });
|
||||
await this.connect({
|
||||
host: this._config.server_host,
|
||||
port: this._config.server_port,
|
||||
pathname: this._config.server_pathname
|
||||
});
|
||||
}
|
||||
this._socket.on('connect', () => {
|
||||
if (typeof onConnected === 'function') {
|
||||
@ -360,11 +364,11 @@ class TcpOutbound extends _defs.Outbound {
|
||||
}
|
||||
}
|
||||
|
||||
async connect({ host, port }) {
|
||||
async connect(target) {
|
||||
if (this._socket && !this._socket.destroyed) {
|
||||
this._socket.destroy();
|
||||
}
|
||||
this._socket = await this._connect({ host, port });
|
||||
this._socket = await this._connect(target);
|
||||
this._socket.on('error', this.onError);
|
||||
this._socket.on('end', this.onHalfClose);
|
||||
this._socket.on('close', this.onClose);
|
||||
|
@ -40,7 +40,11 @@ class TlsOutbound extends _tcp.TcpOutbound {
|
||||
|
||||
async _connect({ host, port }) {
|
||||
_utils.logger.info(`[tls:outbound] [${this.remote}] connecting to tls://${host}:${port}`);
|
||||
return _tls2.default.connect({ host, port, ca: [this._config.tls_cert] });
|
||||
const options = { host, port };
|
||||
if (this._config.tls_cert_self_signed) {
|
||||
options.ca = [this._config.tls_cert];
|
||||
}
|
||||
return _tls2.default.connect(options);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -68,13 +68,25 @@ class WsOutbound extends _tcp.TcpOutbound {
|
||||
return this._socket && this._socket.readyState === _ws2.default.OPEN;
|
||||
}
|
||||
|
||||
async _connect({ host, port }) {
|
||||
_utils.logger.info(`[${this.name}] [${this.remote}] connecting to ws://${host}:${port}`);
|
||||
const socket = new _ws2.default(`ws://${host}:${port}`, { perMessageDeflate: false });
|
||||
async _connect(target) {
|
||||
const address = this.getConnAddress(target);
|
||||
_utils.logger.info(`[${this.name}] [${this.remote}] connecting to ${address}`);
|
||||
const socket = new _ws2.default(address, this.getConnOptions({
|
||||
handshakeTimeout: 1e4,
|
||||
perMessageDeflate: false
|
||||
}));
|
||||
socket.on('message', this.onReceive);
|
||||
socket.on('close', () => socket.destroyed = true);
|
||||
return patchWebsocket.call(this, socket);
|
||||
}
|
||||
|
||||
getConnAddress({ host, port, pathname }) {
|
||||
return `ws://${host}:${port}` + (pathname ? pathname : '');
|
||||
}
|
||||
|
||||
getConnOptions(options) {
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
exports.WsOutbound = WsOutbound;
|
40
lib/transports/wss.js
Normal file
40
lib/transports/wss.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WssOutbound = exports.WssInbound = undefined;
|
||||
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
var _ws = require('./ws');
|
||||
|
||||
class WssInbound extends _ws.WsInbound {
|
||||
|
||||
get name() {
|
||||
return 'wss:inbound';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.WssInbound = WssInbound;
|
||||
class WssOutbound extends _ws.WsOutbound {
|
||||
|
||||
get name() {
|
||||
return 'wss:outbound';
|
||||
}
|
||||
|
||||
getConnAddress({ host, port, pathname }) {
|
||||
return `wss://${host}:${port}` + (pathname ? pathname : '');
|
||||
}
|
||||
|
||||
getConnOptions(options) {
|
||||
const _options = _extends({}, options);
|
||||
if (this._config.tls_cert_self_signed) {
|
||||
_options.ca = [this._config.tls_cert];
|
||||
}
|
||||
return _options;
|
||||
}
|
||||
|
||||
}
|
||||
exports.WssOutbound = WssOutbound;
|
4429
package-lock.json
generated
4429
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "blinksocks",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.1",
|
||||
"description": "A framework for building composable proxy protocol stack",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
@ -13,17 +13,15 @@
|
||||
"scripts": {
|
||||
"test": "npm run lint && npm run test:coverage",
|
||||
"test:coverage": "jest --runInBand --silent --coverage --bail",
|
||||
"lint": "eslint bin src test pkg",
|
||||
"lint": "eslint bin src test",
|
||||
"compile": "cross-env NODE_ENV=production babel src --out-dir lib --ignore __tests__,__mocks__",
|
||||
"prepkg": "npm run compile && rimraf pkg/blinksocks-* pkg/sha256sum.txt",
|
||||
"pkg": "pkg --out-path pkg/ --targets node8.9.0-linux-x64,node8.9.0-macos-x64,node8.9.0-win-x64 .",
|
||||
"postpkg": "node pkg/postpkg.js",
|
||||
"debug:client": "cross-env NODE_ENV=development node --inspect --inspect-port=9200 bin/cli.js blinksocks.client.json",
|
||||
"debug:server": "cross-env NODE_ENV=development node --inspect --inspect-port=9300 bin/cli.js blinksocks.server.json",
|
||||
"client": "cross-env NODE_ENV=production node bin/cli.js blinksocks.client.json",
|
||||
"server": "cross-env NODE_ENV=production node bin/cli.js blinksocks.server.json",
|
||||
"prebenchmark": "npm run compile",
|
||||
"benchmark": "node benchmark/bootstrap.js"
|
||||
"benchmark": "node benchmark/bootstrap.js",
|
||||
"prepublishOnly": "npm run compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.1",
|
||||
@ -34,30 +32,28 @@
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.uniqueid": "^4.0.1",
|
||||
"long": "^4.0.0",
|
||||
"lru-cache": "^4.1.2",
|
||||
"winston": "^3.0.0-rc4",
|
||||
"winston-daily-rotate-file": "^3.1.3",
|
||||
"ws": "^5.1.1"
|
||||
"lru-cache": "^4.1.3",
|
||||
"winston": "^3.0.0",
|
||||
"winston-daily-rotate-file": "^3.2.1",
|
||||
"ws": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-jest": "^22.4.3",
|
||||
"babel-jest": "^23.0.1",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-register": "^6.26.0",
|
||||
"cross-env": "^5.1.4",
|
||||
"cross-env": "^5.2.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-babel": "^7.0.2",
|
||||
"eslint-plugin-babel": "^5.1.0",
|
||||
"eslint-plugin-flowtype": "^2.46.3",
|
||||
"jest": "^22.4.3",
|
||||
"eslint-plugin-flowtype": "^2.49.3",
|
||||
"jest": "^23.1.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pkg": "^4.3.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"socks": "^2.2.0"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -1,46 +0,0 @@
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const utils = require('util');
|
||||
const zlib = require('zlib');
|
||||
|
||||
const { version } = require('../package.json');
|
||||
|
||||
const readdir = utils.promisify(fs.readdir);
|
||||
const remove = utils.promisify(fs.unlink);
|
||||
const appendFile = utils.promisify(fs.appendFile);
|
||||
const readFile = utils.promisify(fs.readFile);
|
||||
|
||||
async function sha256sum(file) {
|
||||
const sha256 = crypto.createHash('sha256');
|
||||
const input = await readFile(file);
|
||||
return sha256.update(input).digest('hex');
|
||||
}
|
||||
|
||||
(async function main() {
|
||||
try {
|
||||
let files = await readdir(path.resolve(__dirname));
|
||||
files = files.filter((f) => f.startsWith('blinksocks-'));
|
||||
for (const file of files) {
|
||||
const name = path.basename(file, '.exe');
|
||||
const ext = path.extname(file);
|
||||
const newName = `${name}-x64-v${version}${ext}.gz`;
|
||||
|
||||
const input = path.join(__dirname, file);
|
||||
const output = path.join(__dirname, newName);
|
||||
const hashFile = path.join(__dirname, 'sha256sum.txt');
|
||||
|
||||
// compress into .gz
|
||||
const stream = fs.createReadStream(input).pipe(zlib.createGzip()).pipe(fs.createWriteStream(output));
|
||||
|
||||
stream.on('finish', async () => {
|
||||
// calc sha256sum
|
||||
await appendFile(hashFile, `${path.basename(output)} ${await sha256sum(output)}\n`);
|
||||
// remove original
|
||||
await remove(input);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
}
|
||||
})();
|
@ -11,3 +11,13 @@ export const CONNECTED_TO_REMOTE = 'CONNECTED_TO_REMOTE';
|
||||
export const MUX_NEW_CONN = 'MUX_NEW_CONN';
|
||||
export const MUX_DATA_FRAME = 'MUX_DATA_FRAME';
|
||||
export const MUX_CLOSE_CONN = 'MUX_CLOSE_CONN';
|
||||
|
||||
// https://url.spec.whatwg.org/#url-miscellaneous
|
||||
export const PROTOCOL_DEFAULT_PORTS = {
|
||||
'ftp:': 21,
|
||||
'gopher:': 70,
|
||||
'http:': 80,
|
||||
'https:': 443,
|
||||
'ws:': 80,
|
||||
'wss:': 443,
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ import winston from 'winston';
|
||||
import WinstonDailyRotateFile from 'winston-daily-rotate-file';
|
||||
import isPlainObject from 'lodash.isplainobject';
|
||||
import { ACL } from './acl';
|
||||
import { PROTOCOL_DEFAULT_PORTS } from '../constants';
|
||||
import { getPresetClassByName } from '../presets';
|
||||
import { IPresetAddressing } from '../presets/defs';
|
||||
import { DNSCache, isValidHostname, isValidPort, logger, DNS_DEFAULT_EXPIRE } from '../utils';
|
||||
@ -24,21 +25,28 @@ export class Config {
|
||||
local_search_params = null;
|
||||
local_host = null;
|
||||
local_port = null;
|
||||
local_pathname = null;
|
||||
|
||||
server = null;
|
||||
is_client = null;
|
||||
is_server = null;
|
||||
|
||||
https_key = null;
|
||||
https_cert = null;
|
||||
|
||||
timeout = null;
|
||||
redirect = null;
|
||||
|
||||
dns_expire = null;
|
||||
dns = null;
|
||||
|
||||
transport = null;
|
||||
server_protocol = null;
|
||||
server_host = null;
|
||||
server_port = null;
|
||||
server_pathname = null;
|
||||
|
||||
tls_cert = null;
|
||||
tls_cert_self_signed = false;
|
||||
tls_key = null;
|
||||
key = null;
|
||||
|
||||
@ -62,14 +70,17 @@ export class Config {
|
||||
stores = [];
|
||||
|
||||
constructor(json) {
|
||||
const { protocol, hostname, port, searchParams, username, password } = new URL(json.service);
|
||||
// service
|
||||
const { protocol, hostname, port, pathname, searchParams, username, password } = new URL(json.service);
|
||||
this.local_protocol = protocol.slice(0, -1);
|
||||
this.local_username = username;
|
||||
this.local_password = password;
|
||||
this.local_search_params = searchParams;
|
||||
this.local_host = hostname;
|
||||
this.local_port = +port;
|
||||
this.local_port = +port || +PROTOCOL_DEFAULT_PORTS[protocol];
|
||||
this.local_pathname = pathname;
|
||||
|
||||
// server
|
||||
let server;
|
||||
// TODO(remove in next version): make backwards compatibility to "json.servers"
|
||||
if (json.servers !== undefined) {
|
||||
@ -78,7 +89,7 @@ export class Config {
|
||||
chalk.bgYellowBright('WARN'),
|
||||
'"servers" will be deprecated in the next version,' +
|
||||
' please configure only one server in "server: {...}",' +
|
||||
' for migration guide please refer to CHANGELOG.md.'
|
||||
' for migration guide please refer to CHANGELOG.md.',
|
||||
);
|
||||
} else {
|
||||
server = json.server;
|
||||
@ -100,8 +111,15 @@ export class Config {
|
||||
this._initServer(server);
|
||||
}
|
||||
|
||||
// common
|
||||
// https_cert, https_key
|
||||
if (this.is_client && this.local_protocol === 'https') {
|
||||
logger.info(`[config] loading ${json.https_cert}`);
|
||||
this.https_cert = loadFileSync(json.https_cert);
|
||||
logger.info(`[config] loading ${json.https_key}`);
|
||||
this.https_key = loadFileSync(json.https_key);
|
||||
}
|
||||
|
||||
// common
|
||||
this.timeout = (json.timeout !== undefined) ? json.timeout * 1e3 : 600 * 1e3;
|
||||
this.dns_expire = (json.dns_expire !== undefined) ? json.dns_expire * 1e3 : DNS_DEFAULT_EXPIRE;
|
||||
|
||||
@ -117,18 +135,22 @@ export class Config {
|
||||
|
||||
_initServer(server) {
|
||||
// service
|
||||
const { protocol, hostname, port } = new URL(server.service);
|
||||
this.transport = protocol.slice(0, -1);
|
||||
const { protocol, hostname, port, pathname } = new URL(server.service);
|
||||
this.server_protocol = protocol.slice(0, -1);
|
||||
this.server_host = hostname;
|
||||
this.server_port = +port;
|
||||
this.server_port = +port || +PROTOCOL_DEFAULT_PORTS[protocol];
|
||||
this.server_pathname = pathname;
|
||||
|
||||
// preload tls_cert and tls_key
|
||||
if (this.transport === 'tls' || this.transport === 'h2') {
|
||||
if (server.tls_cert) {
|
||||
// preload tls_cert or tls_key
|
||||
if (['tls', 'wss', 'h2'].includes(this.server_protocol)) {
|
||||
if (this.is_client) {
|
||||
this.tls_cert_self_signed = !!server.tls_cert_self_signed;
|
||||
}
|
||||
if (this.tls_cert_self_signed || this.is_server) {
|
||||
logger.info(`[config] loading ${server.tls_cert}`);
|
||||
this.tls_cert = loadFileSync(server.tls_cert);
|
||||
}
|
||||
if (this.is_server && server.tls_key) {
|
||||
if (this.is_server) {
|
||||
logger.info(`[config] loading ${server.tls_key}`);
|
||||
this.tls_key = loadFileSync(server.tls_key);
|
||||
}
|
||||
@ -164,7 +186,7 @@ export class Config {
|
||||
// remove unnecessary presets
|
||||
if (this.mux) {
|
||||
this.presets = this.presets.filter(
|
||||
({ name }) => !IPresetAddressing.isPrototypeOf(getPresetClassByName(name))
|
||||
({ name }) => !IPresetAddressing.isPrototypeOf(getPresetClassByName(name)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -208,7 +230,7 @@ export class Config {
|
||||
// TODO: Enable coloring. Currently we have to prevent dumping color characters in log files.
|
||||
// colorize(),
|
||||
prettyPrint(),
|
||||
printf((info) => `${info.timestamp} - ${info.level}: ${info.message}`)
|
||||
printf((info) => `${info.timestamp} - ${info.level}: ${info.message}`),
|
||||
),
|
||||
transports: trans,
|
||||
});
|
||||
@ -233,19 +255,18 @@ export class Config {
|
||||
throw Error('"service" must be provided as "<protocol>://<host>:<port>[?params]"');
|
||||
}
|
||||
|
||||
const { protocol: _protocol, hostname, port, searchParams } = new URL(json.service);
|
||||
const { protocol, hostname, port: _port, searchParams } = new URL(json.service);
|
||||
|
||||
// service.protocol
|
||||
if (typeof _protocol !== 'string') {
|
||||
if (typeof protocol !== 'string') {
|
||||
throw Error('service.protocol is invalid');
|
||||
}
|
||||
|
||||
const protocol = _protocol.slice(0, -1);
|
||||
const proto = protocol.slice(0, -1);
|
||||
const available_client_protocols = [
|
||||
'tcp', 'http', 'https',
|
||||
'socks', 'socks5', 'socks4', 'socks4a'
|
||||
'tcp', 'http', 'https', 'socks', 'socks5', 'socks4', 'socks4a',
|
||||
];
|
||||
if (!available_client_protocols.includes(protocol)) {
|
||||
if (!available_client_protocols.includes(proto)) {
|
||||
throw Error(`service.protocol must be: ${available_client_protocols.join(', ')}`);
|
||||
}
|
||||
|
||||
@ -255,12 +276,13 @@ export class Config {
|
||||
}
|
||||
|
||||
// service.port
|
||||
const port = _port || PROTOCOL_DEFAULT_PORTS[protocol] || '';
|
||||
if (!isValidPort(+port)) {
|
||||
throw Error('service.port is invalid');
|
||||
}
|
||||
|
||||
// service.query
|
||||
if (protocol === 'tcp') {
|
||||
if (proto === 'tcp') {
|
||||
const forward = searchParams.get('forward');
|
||||
|
||||
// ?forward
|
||||
@ -277,6 +299,16 @@ export class Config {
|
||||
}
|
||||
}
|
||||
|
||||
// https_cert, https_key
|
||||
if (proto === 'https') {
|
||||
if (typeof json.https_cert !== 'string' || json.https_cert === '') {
|
||||
throw Error('"https_cert" must be provided');
|
||||
}
|
||||
if (typeof json.https_key !== 'string' || json.https_key === '') {
|
||||
throw Error('"https_key" must be provided');
|
||||
}
|
||||
}
|
||||
|
||||
// server
|
||||
let server;
|
||||
// TODO(remove in next version): make backwards compatibility to "json.servers"
|
||||
@ -329,27 +361,33 @@ export class Config {
|
||||
throw Error('"service" must be provided as "<protocol>://<host>:<port>[?params]"');
|
||||
}
|
||||
|
||||
const { protocol: _protocol, hostname, port } = new URL(server.service);
|
||||
const { protocol, hostname, port: _port } = new URL(server.service);
|
||||
|
||||
// service.protocol
|
||||
if (typeof _protocol !== 'string') {
|
||||
if (typeof protocol !== 'string') {
|
||||
throw Error('service.protocol is invalid');
|
||||
}
|
||||
|
||||
const protocol = _protocol.slice(0, -1);
|
||||
const proto = protocol.slice(0, -1);
|
||||
const available_server_protocols = [
|
||||
'tcp', 'ws', 'tls', 'h2'
|
||||
'tcp', 'ws', 'wss', 'tls', 'h2'
|
||||
];
|
||||
if (!available_server_protocols.includes(protocol)) {
|
||||
if (!available_server_protocols.includes(proto)) {
|
||||
throw Error(`service.protocol must be: ${available_server_protocols.join(', ')}`);
|
||||
}
|
||||
|
||||
// tls_cert, tls_key
|
||||
if (protocol === 'tls' || protocol === 'h2') {
|
||||
if (typeof server.tls_cert !== 'string' || server.tls_cert === '') {
|
||||
throw Error('"tls_cert" must be provided');
|
||||
if (['tls', 'wss', 'h2'].includes(proto)) {
|
||||
if (from_client && server.tls_cert_self_signed) {
|
||||
if (typeof server.tls_cert !== 'string' || server.tls_cert === '') {
|
||||
throw Error('"tls_cert" must be provided when "tls_cert_self_signed" is set');
|
||||
}
|
||||
}
|
||||
// on server, both tls_cert and tls_key must be set
|
||||
if (!from_client) {
|
||||
if (typeof server.tls_cert !== 'string' || server.tls_cert === '') {
|
||||
throw Error('"tls_cert" must be provided');
|
||||
}
|
||||
if (typeof server.tls_key !== 'string' || server.tls_key === '') {
|
||||
throw Error('"tls_key" must be provided');
|
||||
}
|
||||
@ -362,6 +400,7 @@ export class Config {
|
||||
}
|
||||
|
||||
// service.port
|
||||
const port = _port || PROTOCOL_DEFAULT_PORTS[protocol] || '';
|
||||
if (!isValidPort(+port)) {
|
||||
throw Error('service.port is invalid');
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import _sodium from 'libsodium-wrappers';
|
||||
import dgram from 'dgram';
|
||||
import net from 'net';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { URL } from 'url';
|
||||
import http2 from 'http2';
|
||||
import tls from 'tls';
|
||||
@ -12,7 +14,7 @@ import { Relay } from './relay';
|
||||
import { MuxRelay } from './mux-relay';
|
||||
import { SpeedTester } from './speed-tester';
|
||||
import { dumpHex, getRandomInt, hash, logger } from '../utils';
|
||||
import { http, socks, tcp } from '../proxies';
|
||||
import { http as httpProxy, socks, tcp } from '../proxies';
|
||||
import { APP_ID } from '../constants';
|
||||
|
||||
export const MAX_CONNECTIONS = 50;
|
||||
@ -149,6 +151,7 @@ export class Hub {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { local_protocol, local_search_params, local_host, local_port } = this._config;
|
||||
const { local_username: username, local_password: password } = this._config;
|
||||
const { https_key, https_cert } = this._config;
|
||||
let server = null;
|
||||
switch (local_protocol) {
|
||||
case 'tcp': {
|
||||
@ -163,11 +166,22 @@ export class Hub {
|
||||
case 'socks5':
|
||||
case 'socks4':
|
||||
case 'socks4a':
|
||||
server = socks.createServer({ bindAddress: local_host, bindPort: local_port, username, password });
|
||||
server = socks.createServer({
|
||||
bindAddress: local_host,
|
||||
bindPort: local_port,
|
||||
username,
|
||||
password,
|
||||
});
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
server = http.createServer({ username, password });
|
||||
server = httpProxy.createServer({
|
||||
secure: local_protocol === 'https',
|
||||
https_key,
|
||||
https_cert,
|
||||
username,
|
||||
password,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return reject(Error(`unsupported protocol: "${local_protocol}"`));
|
||||
@ -187,14 +201,14 @@ export class Hub {
|
||||
}
|
||||
|
||||
async _createServerOnServer() {
|
||||
const { local_protocol, local_host, local_port, tls_key, tls_cert } = this._config;
|
||||
const { local_protocol, local_host, local_port, local_pathname, tls_key, tls_cert } = this._config;
|
||||
return new Promise((resolve, reject) => {
|
||||
const address = {
|
||||
host: local_host,
|
||||
port: local_port,
|
||||
};
|
||||
const onListening = (server) => {
|
||||
const service = `${local_protocol}://${local_host}:${local_port}`;
|
||||
const service = `${local_protocol}://${local_host}:${local_port}` + (local_pathname ? local_pathname : '');
|
||||
logger.info(`[hub] blinksocks server is running at ${service}`);
|
||||
resolve(server);
|
||||
};
|
||||
@ -206,9 +220,17 @@ export class Hub {
|
||||
server.listen(address, () => onListening(server));
|
||||
break;
|
||||
}
|
||||
case 'wss':
|
||||
case 'ws': {
|
||||
let http_s_server = null;
|
||||
if (local_protocol === 'wss') {
|
||||
http_s_server = https.createServer({ key: tls_key, cert: tls_cert });
|
||||
} else {
|
||||
http_s_server = http.createServer();
|
||||
}
|
||||
server = new ws.Server({
|
||||
...address,
|
||||
server: http_s_server,
|
||||
path: local_pathname,
|
||||
perMessageDeflate: false,
|
||||
});
|
||||
server.getConnections = server._server.getConnections.bind(server._server);
|
||||
@ -217,7 +239,7 @@ export class Hub {
|
||||
ws.remotePort = req.connection.remotePort;
|
||||
this._onConnection(ws);
|
||||
});
|
||||
server.on('listening', () => onListening(server));
|
||||
http_s_server.listen(address, () => onListening(http_s_server));
|
||||
break;
|
||||
}
|
||||
case 'tls': {
|
||||
@ -423,9 +445,9 @@ export class Hub {
|
||||
|
||||
_createRelay(context, isMux = false) {
|
||||
const props = {
|
||||
config: this._config,
|
||||
context: context,
|
||||
transport: this._config.transport,
|
||||
config: this._config,
|
||||
transport: this._config.server_protocol,
|
||||
presets: this._config.presets,
|
||||
};
|
||||
if (isMux) {
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
TlsInbound, TlsOutbound,
|
||||
Http2Inbound, Http2Outbound,
|
||||
WsInbound, WsOutbound,
|
||||
WssInbound, WssOutbound,
|
||||
MuxInbound, MuxOutbound,
|
||||
} from '../transports';
|
||||
|
||||
@ -134,6 +135,7 @@ export class Relay extends EventEmitter {
|
||||
'tls': [TlsInbound, TlsOutbound],
|
||||
'h2': [Http2Inbound, Http2Outbound],
|
||||
'ws': [WsInbound, WsOutbound],
|
||||
'wss': [WssInbound, WssOutbound],
|
||||
'mux': [MuxInbound, MuxOutbound],
|
||||
};
|
||||
let Inbound = null, Outbound = null;
|
||||
|
@ -26,7 +26,29 @@ import ObfsTls12TicketPreset from './obfs-tls1.2-ticket';
|
||||
// others
|
||||
import AeadRandomCipherPreset from './aead-random-cipher';
|
||||
|
||||
const presetMap = {
|
||||
/**
|
||||
* check if a class is a valid preset class
|
||||
* @param clazz
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkPresetClass(clazz) {
|
||||
if (typeof clazz !== 'function') {
|
||||
return false;
|
||||
}
|
||||
// check require hooks
|
||||
const requiredMethods = [
|
||||
'onDestroy', 'onInit',
|
||||
'beforeOut', 'beforeIn', 'clientOut', 'serverIn', 'serverOut', 'clientIn',
|
||||
'beforeOutUdp', 'beforeInUdp', 'clientOutUdp', 'serverInUdp', 'serverOutUdp', 'clientInUdp'
|
||||
];
|
||||
if (requiredMethods.some((method) => typeof clazz.prototype[method] !== 'function')) {
|
||||
return false;
|
||||
}
|
||||
const requiredStaticMethods = ['onCheckParams', 'onCache'];
|
||||
return !requiredStaticMethods.some((method) => typeof clazz[method] !== 'function');
|
||||
}
|
||||
|
||||
export const builtInPresetMap = {
|
||||
// functional
|
||||
'mux': MuxPreset,
|
||||
|
||||
@ -56,35 +78,12 @@ const presetMap = {
|
||||
'aead-random-cipher': AeadRandomCipherPreset
|
||||
};
|
||||
|
||||
/**
|
||||
* check if a class is a valid preset class
|
||||
* @param clazz
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkPresetClass(clazz) {
|
||||
if (typeof clazz !== 'function') {
|
||||
return false;
|
||||
}
|
||||
// check require hooks
|
||||
const requiredMethods = [
|
||||
'onDestroy', 'onInit',
|
||||
'beforeOut', 'beforeIn', 'clientOut', 'serverIn', 'serverOut', 'clientIn',
|
||||
'beforeOutUdp', 'beforeInUdp', 'clientOutUdp', 'serverInUdp', 'serverOutUdp', 'clientInUdp'
|
||||
];
|
||||
if (requiredMethods.some((method) => typeof clazz.prototype[method] !== 'function')) {
|
||||
return false;
|
||||
}
|
||||
const requiredStaticMethods = ['onCheckParams', 'onCache'];
|
||||
if (requiredStaticMethods.some((method) => typeof clazz[method] !== 'function')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getPresetClassByName(name, allowPrivate = false) {
|
||||
let clazz = presetMap[name];
|
||||
// load from built-in
|
||||
let clazz = builtInPresetMap[name];
|
||||
if (clazz === undefined) {
|
||||
try {
|
||||
// load from external
|
||||
clazz = require(name);
|
||||
} catch (err) {
|
||||
throw Error(`cannot load preset "${name}" from built-in modules or external`);
|
||||
@ -98,5 +97,3 @@ export function getPresetClassByName(name, allowPrivate = false) {
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
export const presets = Object.keys(presetMap);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { URL } from 'url';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { logger, isValidPort } from '../utils';
|
||||
|
||||
function checkBasicAuthorization(credentials, { username, password }) {
|
||||
@ -14,8 +15,16 @@ function checkBasicAuthorization(credentials, { username, password }) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function createServer({ username, password }) {
|
||||
const server = http.createServer();
|
||||
export function createServer({ secure, https_key, https_cert, username, password }) {
|
||||
const name = secure ? 'https' : 'http';
|
||||
let server = null;
|
||||
if (secure) {
|
||||
server = https.createServer({ key: https_key, cert: https_cert });
|
||||
} else {
|
||||
server = http.createServer();
|
||||
}
|
||||
|
||||
const isAuthRequired = username !== '' && password !== '';
|
||||
|
||||
// Simple HTTP Proxy
|
||||
server.on('request', (req, res) => {
|
||||
@ -27,16 +36,16 @@ export function createServer({ username, password }) {
|
||||
|
||||
if (hostname === null || !isValidPort(_port)) {
|
||||
const remote = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
logger.warn(`[http] drop invalid http request sent from ${remote}`);
|
||||
logger.warn(`[${name}] drop invalid http request sent from ${remote}`);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
// Basic Authorization
|
||||
const proxyAuth = headers['proxy-authorization'];
|
||||
if (proxyAuth && username !== '' && password !== '') {
|
||||
if (isAuthRequired) {
|
||||
const proxyAuth = headers['proxy-authorization'] || '';
|
||||
const [type, credentials] = proxyAuth.split(' ');
|
||||
if (type !== 'Basic' || !checkBasicAuthorization(credentials, { username, password })) {
|
||||
logger.error(`[http] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
logger.error(`[${name}] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
return res.end('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
}
|
||||
}
|
||||
@ -63,7 +72,7 @@ export function createServer({ username, password }) {
|
||||
|
||||
// free to recv from application now
|
||||
socket.resume();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -76,16 +85,16 @@ export function createServer({ username, password }) {
|
||||
|
||||
if (hostname === null || !isValidPort(port)) {
|
||||
const remote = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
logger.warn(`[http] [${appAddress}] drop invalid http CONNECT request sent from ${remote}`);
|
||||
logger.warn(`[${name}] [${appAddress}] drop invalid http CONNECT request sent from ${remote}`);
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
// Basic Authorization
|
||||
const proxyAuth = req.headers['proxy-authorization'];
|
||||
if (proxyAuth && username !== '' && password !== '') {
|
||||
if (isAuthRequired) {
|
||||
const proxyAuth = req.headers['proxy-authorization'] || '';
|
||||
const [type, credentials] = proxyAuth.split(' ');
|
||||
if (type !== 'Basic' || !checkBasicAuthorization(credentials, { username, password })) {
|
||||
logger.error(`[http] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
logger.error(`[${name}] [${appAddress}] authorization failed, type=${type} credentials=${credentials}`);
|
||||
return socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
}
|
||||
}
|
||||
@ -95,14 +104,14 @@ export function createServer({ username, password }) {
|
||||
port: port,
|
||||
onConnected: () => {
|
||||
socket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// errors
|
||||
server.on('clientError', (err, socket) => {
|
||||
const appAddress = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
logger.error(`[http] [${appAddress}] invalid http request: ${err.message}`);
|
||||
const appAddress = `${socket.remoteAddress || ''}:${socket.remotePort || ''}`;
|
||||
logger.error(`[${name}] [${appAddress}] invalid http request: ${err.message}`);
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
|
@ -287,6 +287,7 @@ const STAGE_DONE = 3;
|
||||
|
||||
export function createServer({ bindAddress, bindPort, username, password }) {
|
||||
const server = net.createServer();
|
||||
const isAuthRequired = username !== '' && password !== '';
|
||||
|
||||
server.on('connection', (socket) => {
|
||||
const appAddress = `${socket.remoteAddress}:${socket.remotePort}`;
|
||||
@ -308,6 +309,11 @@ export function createServer({ bindAddress, bindPort, username, password }) {
|
||||
const { method } = request;
|
||||
switch (method) {
|
||||
case METHOD_NO_AUTH:
|
||||
if (isAuthRequired) {
|
||||
logger.error(`[socks] [${appAddress}] server requires authorization but got METHOD_NO_AUTH`);
|
||||
socket.end(Buffer.from([SOCKS_VERSION_V5, METHOD_NOT_ACCEPTABLE]));
|
||||
break;
|
||||
}
|
||||
stage = STAGE_SOCKS5_REQUEST_MESSAGE;
|
||||
// Socks5 Select Message
|
||||
socket.write(Buffer.from([SOCKS_VERSION_V5, METHOD_NO_AUTH]));
|
||||
@ -347,7 +353,7 @@ export function createServer({ bindAddress, bindPort, username, password }) {
|
||||
request = parseSocks5InitialNegotiation(buffer);
|
||||
if (request !== null) {
|
||||
// Username/Password Authentication
|
||||
if (username !== '' && password !== '') {
|
||||
if (isAuthRequired) {
|
||||
if (username !== request.username || password !== request.password) {
|
||||
logger.error(`[socks] [${appAddress}] invalid socks5 authorization, username=${request.username} password=${request.password}`);
|
||||
socket.end(Buffer.from([SOCKS_VERSION_V5, 0x01]));
|
||||
|
@ -3,4 +3,5 @@ export * from './udp';
|
||||
export * from './tls';
|
||||
export * from './h2';
|
||||
export * from './ws';
|
||||
export * from './wss';
|
||||
export * from './mux';
|
||||
|
@ -343,7 +343,11 @@ export class TcpOutbound extends Outbound {
|
||||
await this.connect({ host, port });
|
||||
}
|
||||
if (this._config.is_client) {
|
||||
await this.connect({ host: this._config.server_host, port: this._config.server_port });
|
||||
await this.connect({
|
||||
host: this._config.server_host,
|
||||
port: this._config.server_port,
|
||||
pathname: this._config.server_pathname,
|
||||
});
|
||||
}
|
||||
this._socket.on('connect', () => {
|
||||
if (typeof onConnected === 'function') {
|
||||
@ -371,12 +375,12 @@ export class TcpOutbound extends Outbound {
|
||||
}
|
||||
}
|
||||
|
||||
async connect({ host, port }) {
|
||||
async connect(target) {
|
||||
// close alive connection before create a new one
|
||||
if (this._socket && !this._socket.destroyed) {
|
||||
this._socket.destroy();
|
||||
}
|
||||
this._socket = await this._connect({ host, port });
|
||||
this._socket = await this._connect(target);
|
||||
this._socket.on('error', this.onError);
|
||||
this._socket.on('end', this.onHalfClose);
|
||||
this._socket.on('close', this.onClose);
|
||||
|
@ -29,7 +29,11 @@ export class TlsOutbound extends TcpOutbound {
|
||||
// overwrite _connect of tcp outbound using tls.connect()
|
||||
async _connect({ host, port }) {
|
||||
logger.info(`[tls:outbound] [${this.remote}] connecting to tls://${host}:${port}`);
|
||||
return tls.connect({ host, port, ca: [this._config.tls_cert] });
|
||||
const options = { host, port };
|
||||
if (this._config.tls_cert_self_signed) {
|
||||
options.ca = [this._config.tls_cert];
|
||||
}
|
||||
return tls.connect(options);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ function patchWebsocket(ws) {
|
||||
ws.write = (buffer) => ws.send(buffer, {
|
||||
compress: false,
|
||||
mask: false,
|
||||
fin: true // send data out immediately
|
||||
fin: true, // send data out immediately
|
||||
}, () => this.emit('drain'));
|
||||
ws.end = () => ws.close();
|
||||
ws.destroy = () => ws.close();
|
||||
@ -57,12 +57,24 @@ export class WsOutbound extends TcpOutbound {
|
||||
return this._socket && this._socket.readyState === WebSocket.OPEN;
|
||||
}
|
||||
|
||||
async _connect({ host, port }) {
|
||||
logger.info(`[${this.name}] [${this.remote}] connecting to ws://${host}:${port}`);
|
||||
const socket = new WebSocket(`ws://${host}:${port}`, { perMessageDeflate: false });
|
||||
async _connect(target) {
|
||||
const address = this.getConnAddress(target);
|
||||
logger.info(`[${this.name}] [${this.remote}] connecting to ${address}`);
|
||||
const socket = new WebSocket(address, this.getConnOptions({
|
||||
handshakeTimeout: 1e4, // 10s
|
||||
perMessageDeflate: false,
|
||||
}));
|
||||
socket.on('message', this.onReceive);
|
||||
socket.on('close', () => socket.destroyed = true);
|
||||
return patchWebsocket.call(this, socket);
|
||||
}
|
||||
|
||||
getConnAddress({ host, port, pathname }) {
|
||||
return `ws://${host}:${port}` + (pathname ? pathname : '');
|
||||
}
|
||||
|
||||
getConnOptions(options) {
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
|
29
src/transports/wss.js
Normal file
29
src/transports/wss.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { WsInbound, WsOutbound } from './ws';
|
||||
|
||||
export class WssInbound extends WsInbound {
|
||||
|
||||
get name() {
|
||||
return 'wss:inbound';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class WssOutbound extends WsOutbound {
|
||||
|
||||
get name() {
|
||||
return 'wss:outbound';
|
||||
}
|
||||
|
||||
getConnAddress({ host, port, pathname }) {
|
||||
return `wss://${host}:${port}` + (pathname ? pathname : '');
|
||||
}
|
||||
|
||||
getConnOptions(options) {
|
||||
const _options = { ...options };
|
||||
if (this._config.tls_cert_self_signed) {
|
||||
_options.ca = [this._config.tls_cert];
|
||||
}
|
||||
return _options;
|
||||
}
|
||||
|
||||
}
|
@ -9,10 +9,10 @@ const clientJson = {
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } }
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
},
|
||||
'log_level': 'debug'
|
||||
'log_level': 'debug',
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
@ -21,11 +21,11 @@ const serverJson = {
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } }
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
'acl': true,
|
||||
'acl_conf': path.resolve(__dirname, 'resources', 'acl.txt'),
|
||||
'log_level': 'debug'
|
||||
'log_level': 'debug',
|
||||
};
|
||||
|
||||
test('acl', async () => await run({ clientJson, serverJson, not: true }));
|
||||
|
@ -1,24 +1,24 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "aead-random-cipher", "params": { "method": "aes-256-gcm" } },
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'aead-random-cipher', 'params': { 'method': 'aes-256-gcm' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "aead-random-cipher", "params": { "method": "aes-256-gcm" } },
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'aead-random-cipher', 'params': { 'method': 'aes-256-gcm' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('aead-random-cipher', async () => await run({ clientJson, serverJson }));
|
||||
|
@ -1,22 +1,22 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "base-auth", "params": { "method": "sha1" } },
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'base-auth', 'params': { 'method': 'sha1' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "base-auth", "params": { "method": "sha1" } },
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'base-auth', 'params': { 'method': 'sha1' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('base-auth', async () => await run({ clientJson, serverJson }));
|
||||
|
@ -1,26 +1,26 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('init-configs-minimal, tcp', async () => {
|
||||
|
@ -1,45 +1,46 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "2/tS:7|.-ec.7cxk",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '2/tS:7|.-ec.7cxk',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
"tls_cert": "cert.pem",
|
||||
"mux": false,
|
||||
"mux_concurrency": 10
|
||||
'tls_cert': 'cert.pem',
|
||||
'tls_cert_self_signed': false,
|
||||
'mux': false,
|
||||
'mux_concurrency': 10,
|
||||
},
|
||||
"dns": [],
|
||||
"dns_expire": 3600,
|
||||
"timeout": 610,
|
||||
"log_path": "bs-client.log",
|
||||
"log_level": "info",
|
||||
"log_max_days": 30
|
||||
'dns': [],
|
||||
'dns_expire': 3600,
|
||||
'timeout': 610,
|
||||
'log_path': 'bs-client.log',
|
||||
'log_level': 'info',
|
||||
'log_max_days': 30,
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "2/tS:7|.-ec.7cxk",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '2/tS:7|.-ec.7cxk',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
"tls_key": "key.pem",
|
||||
"tls_cert": "cert.pem",
|
||||
"mux": false,
|
||||
"dns": [],
|
||||
"dns_expire": 3600,
|
||||
"timeout": 610,
|
||||
"redirect": "",
|
||||
"log_path": "bs-server.log",
|
||||
"log_level": "info",
|
||||
"log_max_days": 30
|
||||
'tls_key': 'key.pem',
|
||||
'tls_cert': 'cert.pem',
|
||||
'mux': false,
|
||||
'dns': [],
|
||||
'dns_expire': 3600,
|
||||
'timeout': 610,
|
||||
'redirect': '',
|
||||
'log_path': 'bs-server.log',
|
||||
'log_level': 'info',
|
||||
'log_max_days': 30,
|
||||
};
|
||||
|
||||
test('init-configs', async () => {
|
||||
|
@ -6,34 +6,35 @@ const tlsKey = path.resolve(__dirname, 'resources', 'key.pem');
|
||||
const tlsCert = path.resolve(__dirname, 'resources', 'cert.pem');
|
||||
|
||||
const client = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
// "service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
"mux": true,
|
||||
"mux_concurrency": 5,
|
||||
"tls_cert": tlsCert,
|
||||
'mux': true,
|
||||
'mux_concurrency': 5,
|
||||
'tls_cert': tlsCert,
|
||||
'tls_cert_self_signed': true,
|
||||
},
|
||||
"log_level": "debug"
|
||||
'log_level': 'debug',
|
||||
};
|
||||
|
||||
const server = {
|
||||
// "service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
"mux": true,
|
||||
"tls_cert": tlsCert,
|
||||
"tls_key": tlsKey,
|
||||
"log_level": "debug"
|
||||
'mux': true,
|
||||
'tls_cert': tlsCert,
|
||||
'tls_key': tlsKey,
|
||||
'log_level': 'debug',
|
||||
};
|
||||
|
||||
test('multiplexing over tcp', async () => {
|
||||
|
@ -4,24 +4,24 @@ import run from '../common/run-e2e';
|
||||
const mockfile = path.resolve(__dirname, 'resources', 'http-mock.txt');
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-http", "params": { "file": mockfile } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-http', 'params': { 'file': mockfile } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-http", "params": { "file": mockfile } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-http', 'params': { 'file': mockfile } },
|
||||
],
|
||||
};
|
||||
|
||||
test('obfs-http', async () => await run({ clientJson, serverJson, repeat: 5 }));
|
||||
|
@ -1,24 +1,24 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-tls1.2-ticket", "params": { "sni": ["test.com", "example.com"] } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-tls1.2-ticket', 'params': { 'sni': ['test.com', 'example.com'] } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-tls1.2-ticket" }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-tls1.2-ticket' },
|
||||
],
|
||||
};
|
||||
|
||||
test('obfs-tls1.2-ticket', async () => {
|
||||
|
@ -2,25 +2,25 @@ import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
// "service": "",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
]
|
||||
}
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('http-proxy', async () => await run({
|
||||
|
@ -2,26 +2,26 @@ import clone from 'lodash.clonedeep';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const client = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const server = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
],
|
||||
};
|
||||
|
||||
test('ss-aead-cipher, aes-128-gcm', async () => {
|
||||
const cipher = { "name": "ss-aead-cipher", "params": { "method": "aes-128-gcm" } };
|
||||
const cipher = { 'name': 'ss-aead-cipher', 'params': { 'method': 'aes-128-gcm' } };
|
||||
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
@ -34,7 +34,7 @@ test('ss-aead-cipher, aes-128-gcm', async () => {
|
||||
});
|
||||
|
||||
test('ss-aead-cipher, chacha20-ietf-poly1305', async () => {
|
||||
const cipher = { "name": "ss-aead-cipher", "params": { "method": "chacha20-ietf-poly1305" } };
|
||||
const cipher = { 'name': 'ss-aead-cipher', 'params': { 'method': 'chacha20-ietf-poly1305' } };
|
||||
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
@ -47,7 +47,7 @@ test('ss-aead-cipher, chacha20-ietf-poly1305', async () => {
|
||||
});
|
||||
|
||||
test('ss-aead-cipher, xchacha20-ietf-poly1305', async () => {
|
||||
const cipher = { "name": "ss-aead-cipher", "params": { "method": "xchacha20-ietf-poly1305" } };
|
||||
const cipher = { 'name': 'ss-aead-cipher', 'params': { 'method': 'xchacha20-ietf-poly1305' } };
|
||||
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
|
@ -2,26 +2,26 @@ import clone from 'lodash.clonedeep';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const client = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const server = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
],
|
||||
};
|
||||
|
||||
test('ss-stream-cipher, aes-256-cfb', async () => {
|
||||
const cipher = { "name": "ss-stream-cipher", "params": { "method": "aes-256-cfb" } };
|
||||
const cipher = { 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-256-cfb' } };
|
||||
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
@ -34,7 +34,7 @@ test('ss-stream-cipher, aes-256-cfb', async () => {
|
||||
});
|
||||
|
||||
test('ss-stream-cipher, rc4-md5', async () => {
|
||||
const cipher = { "name": "ss-stream-cipher", "params": { "method": "rc4-md5" } };
|
||||
const cipher = { 'name': 'ss-stream-cipher', 'params': { 'method': 'rc4-md5' } };
|
||||
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
@ -47,7 +47,7 @@ test('ss-stream-cipher, rc4-md5', async () => {
|
||||
});
|
||||
|
||||
test('ss-stream-cipher, rc4-md5-6', async () => {
|
||||
const cipher = { "name": "ss-stream-cipher", "params": { "method": "rc4-md5-6" } };
|
||||
const cipher = { 'name': 'ss-stream-cipher', 'params': { 'method': 'rc4-md5-6' } };
|
||||
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
|
@ -1,26 +1,26 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-aes128-md5" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-256-cfb" } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-aes128-md5' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-256-cfb' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-aes128-md5" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-256-cfb" } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-aes128-md5' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-256-cfb' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('ssr-auth-aes128-md5', async () => {
|
||||
|
@ -1,26 +1,26 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-aes128-sha1" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-256-cfb" } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-aes128-sha1' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-256-cfb' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-aes128-sha1" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-256-cfb" } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-aes128-sha1' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-256-cfb' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('ssr-auth-aes128-sha1', async () => {
|
||||
|
@ -1,26 +1,26 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-chain-a" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "none" } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-chain-a' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'none' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-chain-a" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "none" } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-chain-a' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'none' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('ssr-auth-chain-a', async () => {
|
||||
|
@ -1,26 +1,26 @@
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-chain-b" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "none" } }
|
||||
]
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-chain-b' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'none' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "ssr-auth-chain-b" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "none" } }
|
||||
]
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'ssr-auth-chain-b' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'none' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('ssr-auth-chain-b', async () => {
|
||||
|
@ -1,33 +1,43 @@
|
||||
import path from 'path';
|
||||
import clone from 'lodash.clonedeep';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const tlsKey = path.resolve(__dirname, 'resources', 'key.pem');
|
||||
const tlsCert = path.resolve(__dirname, 'resources', 'cert.pem');
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tls://localhost:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
const client = {
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tls://localhost:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
"tls_cert": tlsCert
|
||||
}
|
||||
'tls_cert': tlsCert,
|
||||
'tls_cert_self_signed': false,
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tls://localhost:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
const server = {
|
||||
'service': 'tls://localhost:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
"tls_cert": tlsCert,
|
||||
"tls_key": tlsKey
|
||||
'tls_cert': tlsCert,
|
||||
'tls_key': tlsKey,
|
||||
};
|
||||
|
||||
test('transport-layer-tls', async () => await run({ clientJson, serverJson }));
|
||||
test('transport-layer-tls, tls_cert_self_signed is false', async () => {
|
||||
await run({ clientJson: client, serverJson: server, not: true });
|
||||
});
|
||||
|
||||
test('transport-layer-tls, tls_cert_self_signed is true', async () => {
|
||||
const clientJson = clone(client);
|
||||
clientJson.server['tls_cert_self_signed'] = true;
|
||||
await run({ clientJson, serverJson: server });
|
||||
});
|
||||
|
@ -1,26 +1,39 @@
|
||||
import clone from 'lodash.clonedeep';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "ws://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
]
|
||||
}
|
||||
const client = {
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'ws://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "ws://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "ss-base" },
|
||||
{ "name": "obfs-random-padding" },
|
||||
{ "name": "ss-stream-cipher", "params": { "method": "aes-128-ctr" } }
|
||||
]
|
||||
const server = {
|
||||
'service': 'ws://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
};
|
||||
|
||||
test('transport-layer-ws', async () => await run({ clientJson, serverJson }));
|
||||
test('transport-layer-ws path=/', async () => {
|
||||
await run({ clientJson: client, serverJson: server });
|
||||
});
|
||||
|
||||
test('transport-layer-ws path=/test-path', async () => {
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
|
||||
clientJson.server.service = 'ws://127.0.0.1:1082/test-path';
|
||||
serverJson.service = 'ws://127.0.0.1:1082/test-path';
|
||||
|
||||
await run({ clientJson, serverJson });
|
||||
});
|
||||
|
34
test/e2e/transport-layer-wss.test.js
Normal file
34
test/e2e/transport-layer-wss.test.js
Normal file
@ -0,0 +1,34 @@
|
||||
import path from 'path';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const tlsKey = path.resolve(__dirname, 'resources', 'key.pem');
|
||||
const tlsCert = path.resolve(__dirname, 'resources', 'cert.pem');
|
||||
|
||||
const clientJson = {
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'wss://localhost:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
'tls_cert': tlsCert,
|
||||
'tls_cert_self_signed': true,
|
||||
},
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
'service': 'wss://localhost:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
'presets': [
|
||||
{ 'name': 'ss-base' },
|
||||
{ 'name': 'obfs-random-padding' },
|
||||
{ 'name': 'ss-stream-cipher', 'params': { 'method': 'aes-128-ctr' } },
|
||||
],
|
||||
'tls_cert': tlsCert,
|
||||
'tls_key': tlsKey,
|
||||
};
|
||||
|
||||
test('transport-layer-wss', async () => await run({ clientJson, serverJson }));
|
@ -2,25 +2,25 @@ import clone from 'lodash.clonedeep';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const client = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD"
|
||||
}
|
||||
'service': 'socks5://127.0.0.1:1081',
|
||||
'server': {
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
},
|
||||
};
|
||||
|
||||
const server = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD"
|
||||
'service': 'tcp://127.0.0.1:1082',
|
||||
'key': '9{*2gdBSdCrgnSBD',
|
||||
};
|
||||
|
||||
test('v2ray-vmess, none', async () => {
|
||||
const presets = [{
|
||||
"name": "v2ray-vmess",
|
||||
"params": {
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "none"
|
||||
}
|
||||
'name': 'v2ray-vmess',
|
||||
'params': {
|
||||
'id': 'c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7',
|
||||
'security': 'none',
|
||||
},
|
||||
}];
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
@ -33,11 +33,11 @@ test('v2ray-vmess, none', async () => {
|
||||
|
||||
test('v2ray-vmess, aes-128-gcm', async () => {
|
||||
const presets = [{
|
||||
"name": "v2ray-vmess",
|
||||
"params": {
|
||||
"id": "c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7",
|
||||
"security": "aes-128-gcm"
|
||||
}
|
||||
'name': 'v2ray-vmess',
|
||||
'params': {
|
||||
'id': 'c2485913-4e9e-41eb-8cc5-b2e7db8d3bc7',
|
||||
'security': 'aes-128-gcm',
|
||||
},
|
||||
}];
|
||||
const clientJson = clone(client);
|
||||
const serverJson = clone(server);
|
||||
|
@ -1,18 +1,18 @@
|
||||
const fs = jest.genMockFromModule('fs');
|
||||
|
||||
fs.statSync = function () {
|
||||
fs.statSync = function() {
|
||||
return {
|
||||
isFile: () => true
|
||||
isFile: () => true,
|
||||
};
|
||||
};
|
||||
|
||||
fs.lstatSync = function () {
|
||||
fs.lstatSync = function() {
|
||||
const err = new Error();
|
||||
err.code = 'ENOENT';
|
||||
throw err;
|
||||
};
|
||||
|
||||
fs.readFileSync = function () {
|
||||
fs.readFileSync = function() {
|
||||
return Buffer.alloc(0);
|
||||
};
|
||||
|
||||
|
@ -30,9 +30,9 @@ describe('Config#test', () => {
|
||||
service: 'tcp://127.0.0.1:1082',
|
||||
key: 'abc',
|
||||
presets: [{
|
||||
name: 'ss-base'
|
||||
}]
|
||||
}
|
||||
name: 'ss-base',
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
it('should throw when ?forward is invalid', () => {
|
||||
@ -96,9 +96,9 @@ describe('Config#testOnClient', () => {
|
||||
service: 'tcp://127.0.0.1:1082',
|
||||
key: 'abc',
|
||||
presets: [{
|
||||
name: 'ss-base'
|
||||
}]
|
||||
}
|
||||
name: 'ss-base',
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
it('should throw when service is not provided', () => {
|
||||
@ -175,13 +175,13 @@ describe('Config#init', () => {
|
||||
service: 'tcp://127.0.0.1:1082',
|
||||
key: 'abc',
|
||||
presets: [{
|
||||
name: 'ss-base'
|
||||
}]
|
||||
name: 'ss-base',
|
||||
}],
|
||||
},
|
||||
dns: ['8.8.8.8'],
|
||||
log_level: 'warn',
|
||||
log_path: 'blinksocks.log',
|
||||
log_max_days: 30
|
||||
log_max_days: 30,
|
||||
};
|
||||
|
||||
it('should config set correctly', () => {
|
||||
@ -205,15 +205,15 @@ describe('Config#initServer', () => {
|
||||
service: 'tls://127.0.0.1:1082',
|
||||
key: 'abc',
|
||||
presets: [{
|
||||
name: 'ss-base'
|
||||
name: 'ss-base',
|
||||
}],
|
||||
tls_cert: 'mock_cert.pem',
|
||||
tls_key: 'mock_key.pem'
|
||||
tls_key: 'mock_key.pem',
|
||||
};
|
||||
|
||||
it('should config set correctly', () => {
|
||||
const config = new Config(serverConf);
|
||||
expect(config.transport).toBe('tls');
|
||||
expect(config.server_protocol).toBe('tls');
|
||||
expect(config.tls_cert).toBeDefined();
|
||||
expect(config.tls_key).toBeDefined();
|
||||
});
|
||||
|
7
test/src/core/speed-tester.test.js
Normal file
7
test/src/core/speed-tester.test.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { SpeedTester } from '../../../src/core/speed-tester';
|
||||
|
||||
test('SpeedTester::getSpeed()', () => {
|
||||
const st = new SpeedTester();
|
||||
st.feed(10);
|
||||
expect(st.getSpeed()).toBeGreaterThan(0);
|
||||
});
|
13
test/src/presets/index.test.js
Normal file
13
test/src/presets/index.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { getPresetClassByName } from '../../../src/presets';
|
||||
|
||||
test('getPresetClassByName, fail', () => {
|
||||
expect(() => getPresetClassByName('_unknown_')).toThrow();
|
||||
expect(() => getPresetClassByName('mux')).toThrow();
|
||||
expect(() => getPresetClassByName(require.resolve('./mock_invalid_preset_a'))).toThrow();
|
||||
expect(() => getPresetClassByName(require.resolve('./mock_invalid_preset_b'))).toThrow();
|
||||
});
|
||||
|
||||
test('getPresetClassByName, success', () => {
|
||||
expect(() => getPresetClassByName('ss-base')).not.toThrow();
|
||||
expect(() => getPresetClassByName(require.resolve('./mock_valid_preset'))).not.toThrow();
|
||||
});
|
3
test/src/presets/mock_invalid_preset_a.js
Normal file
3
test/src/presets/mock_invalid_preset_a.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = class MockPreset {
|
||||
|
||||
};
|
1
test/src/presets/mock_invalid_preset_b.js
Normal file
1
test/src/presets/mock_invalid_preset_b.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = null;
|
5
test/src/presets/mock_valid_preset.js
Normal file
5
test/src/presets/mock_valid_preset.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { IPreset } from '../../../src/presets/defs';
|
||||
|
||||
module.exports = class MockPreset extends IPreset {
|
||||
|
||||
};
|
@ -1,31 +1,31 @@
|
||||
import { AdvancedBuffer } from '../../../src/utils/advanced-buffer';
|
||||
|
||||
describe('AdvancedBuffer#constructor', function () {
|
||||
describe('AdvancedBuffer#constructor', function() {
|
||||
|
||||
it('should throw when options is not given', function () {
|
||||
it('should throw when options is not given', function() {
|
||||
expect(() => new AdvancedBuffer()).toThrow();
|
||||
});
|
||||
|
||||
it('should throw when getPacketLength not Function', function () {
|
||||
it('should throw when getPacketLength not Function', function() {
|
||||
expect(() => new AdvancedBuffer({ getPacketLength: null })).toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('AdvancedBuffer#put', function () {
|
||||
describe('AdvancedBuffer#put', function() {
|
||||
|
||||
it('should throw when pass a non-buffer to put() ', function () {
|
||||
it('should throw when pass a non-buffer to put() ', function() {
|
||||
const buffer = new AdvancedBuffer({
|
||||
getPacketLength: () => 0
|
||||
getPacketLength: () => 0,
|
||||
});
|
||||
expect(() => buffer.put()).toThrow();
|
||||
});
|
||||
|
||||
it('should leave 0xff', function () {
|
||||
it('should leave 0xff', function() {
|
||||
const buffer = new AdvancedBuffer({
|
||||
getPacketLength: (chunk) => {
|
||||
return (chunk.length < 2) ? 0 : chunk.readUInt16BE(0);
|
||||
}
|
||||
},
|
||||
});
|
||||
const callback = jest.fn();
|
||||
buffer.on('data', callback);
|
||||
@ -39,7 +39,7 @@ describe('AdvancedBuffer#put', function () {
|
||||
expect(callback).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should drop the first byte', function () {
|
||||
it('should drop the first byte', function() {
|
||||
let dropped = false;
|
||||
const buffer = new AdvancedBuffer({
|
||||
getPacketLength: (chunk) => {
|
||||
@ -49,7 +49,7 @@ describe('AdvancedBuffer#put', function () {
|
||||
} else {
|
||||
return chunk.length > 1 ? chunk.readUInt16BE(0) : 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const callback = jest.fn();
|
||||
buffer.on('data', callback);
|
||||
@ -63,9 +63,9 @@ describe('AdvancedBuffer#put', function () {
|
||||
expect(callback).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should drop buffer', function () {
|
||||
it('should drop buffer', function() {
|
||||
const buffer = new AdvancedBuffer({
|
||||
getPacketLength: () => -1
|
||||
getPacketLength: () => -1,
|
||||
});
|
||||
const callback = jest.fn();
|
||||
buffer.on('data', callback);
|
||||
@ -76,9 +76,9 @@ describe('AdvancedBuffer#put', function () {
|
||||
|
||||
});
|
||||
|
||||
describe('AdvancedBuffer#clear', function () {
|
||||
describe('AdvancedBuffer#clear', function() {
|
||||
|
||||
it('should throw when pass a non-buffer to put() ', function () {
|
||||
it('should throw when pass a non-buffer to put() ', function() {
|
||||
const buffer = new AdvancedBuffer({ getPacketLength: () => 0 });
|
||||
buffer.put(Buffer.from([0x00]));
|
||||
buffer.clear();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user