presets,test: remove "auto-conf" preset
This commit is contained in:
parent
100f1fe48d
commit
18a91d3b61
@ -17,7 +17,6 @@ import {
|
||||
CONNECT_TO_REMOTE,
|
||||
CONNECTION_CLOSED,
|
||||
CONNECTION_WILL_CLOSE,
|
||||
CHANGE_PRESET_SUITE,
|
||||
PRESET_FAILED,
|
||||
} from '../presets/actions';
|
||||
|
||||
@ -102,12 +101,10 @@ export class Relay extends EventEmitter {
|
||||
this._outbound.setInbound(this._inbound);
|
||||
this._outbound.on('_error', (err) => this.emit('_error', err));
|
||||
this._outbound.on('close', () => this.onBoundClose(outbound, inbound));
|
||||
this._outbound.on('updatePresets', this.updatePresets);
|
||||
// inbound
|
||||
this._inbound.setOutbound(this._outbound);
|
||||
this._inbound.on('_error', (err) => this.emit('_error', err));
|
||||
this._inbound.on('close', () => this.onBoundClose(inbound, outbound));
|
||||
this._inbound.on('updatePresets', this.updatePresets);
|
||||
// acl
|
||||
if (config.acl) {
|
||||
this._acl = new ACL({ remoteInfo: this._remoteInfo, rules: config.acl_rules });
|
||||
@ -202,35 +199,10 @@ export class Relay extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (action.type === CHANGE_PRESET_SUITE) {
|
||||
this.onChangePresetSuite(action);
|
||||
return;
|
||||
}
|
||||
this._inbound && this._inbound.onBroadcast(action);
|
||||
this._outbound && this._outbound.onBroadcast(action);
|
||||
}
|
||||
|
||||
onChangePresetSuite = (action) => {
|
||||
const { type, suite, data } = action.payload;
|
||||
logger.verbose(`[relay] changing presets suite to: ${JSON.stringify(suite)}`);
|
||||
// 1. update preset list
|
||||
this.updatePresets(this.preparePresets([
|
||||
...suite.presets,
|
||||
{ 'name': 'auto-conf' },
|
||||
]));
|
||||
// 2. initialize newly created presets
|
||||
const proxyRequest = this._proxyRequest;
|
||||
if (this._config.is_client) {
|
||||
this._pipe.initTargetAddress(proxyRequest);
|
||||
this.onBroadcast({
|
||||
type: CONNECT_TO_REMOTE,
|
||||
payload: { ...proxyRequest, keepAlive: true }, // keep previous connection alive, don't re-connect
|
||||
});
|
||||
}
|
||||
// 3. re-pipe
|
||||
this._pipe.feed(type, data);
|
||||
};
|
||||
|
||||
onPreDecode = (buffer, cb) => {
|
||||
if (this._tracker !== null) {
|
||||
this._tracker.trace(PIPE_DECODE, buffer.length);
|
||||
@ -294,15 +266,6 @@ export class Relay extends EventEmitter {
|
||||
return presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* update presets of pipe
|
||||
* @param value
|
||||
*/
|
||||
updatePresets = (value) => {
|
||||
this._presets = typeof value === 'function' ? value(this._presets) : value;
|
||||
this._pipe.updatePresets(this._presets);
|
||||
};
|
||||
|
||||
/**
|
||||
* create pipes for both data forward and backward
|
||||
*/
|
||||
|
@ -60,18 +60,6 @@ export const CONNECTED_TO_REMOTE = '@action:connected_to_remote';
|
||||
*/
|
||||
export const PRESET_FAILED = '@action:preset_failed';
|
||||
|
||||
/**
|
||||
* {
|
||||
* type: CHANGE_PRESET_SUITE,
|
||||
* payload: {
|
||||
* type: <PIPE_ENCODE|PIPE_DECODE>,
|
||||
* suite: [...],
|
||||
* data: <Buffer>
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export const CHANGE_PRESET_SUITE = '@action:change_preset_suite';
|
||||
|
||||
export const MUX_NEW_CONN = '@action:mux_new_conn';
|
||||
export const MUX_DATA_FRAME = '@action:mux_data_frame';
|
||||
export const MUX_CLOSE_CONN = '@action:mux_close_conn';
|
||||
|
@ -1,215 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import crypto from 'crypto';
|
||||
import fetch from 'node-fetch';
|
||||
import { IPreset } from './defs';
|
||||
import { CHANGE_PRESET_SUITE } from './actions';
|
||||
import {
|
||||
logger,
|
||||
hmac,
|
||||
hash,
|
||||
dumpHex,
|
||||
numberToBuffer as ntb, BYTE_ORDER_LE,
|
||||
getCurrentTimestampInt,
|
||||
EVP_BytesToKey,
|
||||
} from '../utils';
|
||||
import { PIPE_DECODE, PIPE_ENCODE } from '../constants';
|
||||
|
||||
const MAX_TIME_DIFF = 30; // seconds
|
||||
const NOOP = Buffer.alloc(0);
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Auto configure preset suite.
|
||||
*
|
||||
* @notice
|
||||
* This is an experimental preset, protocol can be changed at any time.
|
||||
*
|
||||
* @params
|
||||
* suites: A json file includes a set of preset combinations.
|
||||
*
|
||||
* @examples
|
||||
*
|
||||
* // use local file
|
||||
* {"name": "auto-conf", "params": {"suites": "suites.json"}}
|
||||
*
|
||||
* // load from remote use http(s)
|
||||
* {"name": "auto-conf", "params": {"suites": "https://some.where/suites.json"}}
|
||||
*
|
||||
* @protocol
|
||||
*
|
||||
* # TCP handshake request & UDP packets (client -> server)
|
||||
* +------------+-----------+------------+-------------+
|
||||
* | Suite ID | UTC | HMAC-MD5 | PAYLOAD |
|
||||
* +------------+-----------+------------+-------------+
|
||||
* | 2 | 4 | 16 | Variable |
|
||||
* +------------+-----------+------------+-------------+
|
||||
* |<-- RC4 -->|
|
||||
*
|
||||
* # TCP chunks (client <-> server)
|
||||
* +-------------+
|
||||
* | PAYLOAD |
|
||||
* +-------------+
|
||||
* | Variable |
|
||||
* +-------------+
|
||||
*
|
||||
* # UDP packets (client <- server)
|
||||
* +-------------+
|
||||
* | PAYLOAD |
|
||||
* +-------------+
|
||||
* | Variable |
|
||||
* +-------------+
|
||||
*
|
||||
* @explain
|
||||
* 1. Suite ID should be randomly generated and mapped to real one (Suite ID % suites.length) in the pre-shared suites.
|
||||
* 2. Suite ID and UTC are little-endian.
|
||||
* 3. UTC is encrypted by RC4.
|
||||
* 4. HMAC-MD5 is HMAC(Suite ID + RC4(UTC)).
|
||||
* 5. RC4 and HMAC-MD5 key are EVP_BytesToKey(base64(orgKey) + base64(md5(Suite ID)), 16, 16).
|
||||
*/
|
||||
export default class AutoConfPreset extends IPreset {
|
||||
|
||||
_isSuiteChanged = false;
|
||||
|
||||
_isHeaderSent = false;
|
||||
|
||||
_header = null;
|
||||
|
||||
static onCheckParams({ suites }) {
|
||||
if (typeof suites !== 'string' || suites.length < 1) {
|
||||
throw Error('\'suites\' is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
static async onCache({ suites: uri }) {
|
||||
logger.info(`[auto-conf] loading suites from: ${uri}`);
|
||||
let suites = [];
|
||||
if (uri.startsWith('http')) {
|
||||
// load from remote
|
||||
const res = await fetch(uri);
|
||||
suites = await res.json();
|
||||
} else {
|
||||
// load from file system
|
||||
const suiteJson = path.resolve(process.cwd(), uri);
|
||||
const rawText = fs.readFileSync(suiteJson, { encoding: 'utf-8' });
|
||||
suites = JSON.parse(rawText);
|
||||
}
|
||||
if (suites.length < 1) {
|
||||
throw Error(`you must provide at least one suite in ${uri}`);
|
||||
}
|
||||
logger.info(`[auto-conf] ${suites.length} suites loaded`);
|
||||
return { suites };
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this._header = null;
|
||||
}
|
||||
|
||||
createRequestHeader(suites) {
|
||||
const sid = crypto.randomBytes(2);
|
||||
const utc = ntb(getCurrentTimestampInt(), 4, BYTE_ORDER_LE);
|
||||
const key = EVP_BytesToKey(Buffer.from(this._config.key).toString('base64') + hash('md5', sid).toString('base64'), 16, 16);
|
||||
const cipher = crypto.createCipheriv('rc4', key, NOOP);
|
||||
const enc_utc = cipher.update(utc);
|
||||
const request_hmac = hmac('md5', key, Buffer.concat([sid, enc_utc]));
|
||||
return {
|
||||
header: Buffer.concat([sid, enc_utc, request_hmac]),
|
||||
suite: suites[sid.readUInt16LE(0) % suites.length]
|
||||
};
|
||||
}
|
||||
|
||||
encodeChangeSuite({ buffer, broadcast, fail }) {
|
||||
const { suites } = this.getStore();
|
||||
if (suites.length < 1) {
|
||||
return fail('suites are not initialized properly');
|
||||
}
|
||||
const { header, suite } = this.createRequestHeader(suites);
|
||||
this._header = header;
|
||||
this._isSuiteChanged = true;
|
||||
return broadcast({
|
||||
type: CHANGE_PRESET_SUITE,
|
||||
payload: {
|
||||
type: PIPE_ENCODE,
|
||||
suite: suite,
|
||||
data: buffer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
decodeChangeSuite({ buffer, broadcast, fail }) {
|
||||
const { suites } = this.getStore();
|
||||
if (suites.length < 1) {
|
||||
return fail('suites are not initialized properly');
|
||||
}
|
||||
if (buffer.length < 22) {
|
||||
return fail(`client request is too short, dump=${dumpHex(buffer)}`);
|
||||
}
|
||||
const sid = buffer.slice(0, 2);
|
||||
const request_hmac = buffer.slice(6, 22);
|
||||
const key = EVP_BytesToKey(Buffer.from(this._config.key).toString('base64') + hash('md5', sid).toString('base64'), 16, 16);
|
||||
const hmac_calc = hmac('md5', key, buffer.slice(0, 6));
|
||||
if (!hmac_calc.equals(request_hmac)) {
|
||||
return fail(`unexpected hmac of client request, dump=${dumpHex(buffer)}`);
|
||||
}
|
||||
const enc_utc = buffer.slice(2, 6);
|
||||
const decipher = crypto.createDecipheriv('rc4', key, NOOP);
|
||||
const utc = decipher.update(enc_utc).readUInt32LE(0);
|
||||
const time_diff = Math.abs(utc - getCurrentTimestampInt());
|
||||
if (time_diff > MAX_TIME_DIFF) {
|
||||
return fail(`timestamp diff is over ${MAX_TIME_DIFF}s, dump=${dumpHex(buffer)}`);
|
||||
}
|
||||
const suite = suites[sid.readUInt16LE(0) % suites.length];
|
||||
this._isSuiteChanged = true;
|
||||
return broadcast({
|
||||
type: CHANGE_PRESET_SUITE,
|
||||
payload: {
|
||||
type: PIPE_DECODE,
|
||||
suite: suite,
|
||||
data: buffer.slice(22)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// tcp
|
||||
|
||||
clientOut({ buffer, broadcast, fail }) {
|
||||
if (!this._isSuiteChanged) {
|
||||
return this.encodeChangeSuite({ buffer, broadcast, fail })
|
||||
}
|
||||
if (!this._isHeaderSent) {
|
||||
this._isHeaderSent = true;
|
||||
return Buffer.concat([this._header, buffer]);
|
||||
} else {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
serverIn({ buffer, broadcast, fail }) {
|
||||
if (!this._isSuiteChanged) {
|
||||
return this.decodeChangeSuite({ buffer, broadcast, fail });
|
||||
} else {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// udp
|
||||
|
||||
clientOutUdp({ buffer, broadcast, fail }) {
|
||||
if (!this._isSuiteChanged) {
|
||||
return this.encodeChangeSuite({ buffer, broadcast, fail });
|
||||
} else {
|
||||
this._isSuiteChanged = false;
|
||||
return Buffer.concat([this._header, buffer]);
|
||||
}
|
||||
}
|
||||
|
||||
serverInUdp({ buffer, broadcast, fail }) {
|
||||
if (!this._isSuiteChanged) {
|
||||
return this.decodeChangeSuite({ buffer, broadcast, fail });
|
||||
} else {
|
||||
this._isSuiteChanged = false;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
// functional
|
||||
import AutoConfPreset from './auto-conf';
|
||||
import MuxPreset from './mux';
|
||||
|
||||
// basic
|
||||
@ -29,7 +28,6 @@ import AeadRandomCipherPreset from './aead-random-cipher';
|
||||
|
||||
const presetMap = {
|
||||
// functional
|
||||
'auto-conf': AutoConfPreset,
|
||||
'mux': MuxPreset,
|
||||
|
||||
// basic
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import EventEmitter from 'events';
|
||||
|
||||
// .on('updatePresets')
|
||||
class Bound extends EventEmitter {
|
||||
|
||||
_ctx = null;
|
||||
@ -54,10 +53,6 @@ class Bound extends EventEmitter {
|
||||
|
||||
}
|
||||
|
||||
updatePresets(value) {
|
||||
this.emit('updatePresets', value);
|
||||
}
|
||||
|
||||
broadcast(action) {
|
||||
!this.ctx.pipe.destroyed && this.ctx.pipe.broadcast('pipe', action);
|
||||
}
|
||||
|
@ -173,8 +173,8 @@ export class TcpInbound extends Inbound {
|
||||
|
||||
logger.warn(`[${this.name}] [${this.remote}] connection is redirecting to: ${host}:${port}`);
|
||||
|
||||
// replace presets to tracker only
|
||||
this.updatePresets([{ name: 'tracker' }]);
|
||||
// clear preset list
|
||||
this.ctx.pipe.updatePresets([]);
|
||||
|
||||
// connect to "redirect" remote
|
||||
await this._outbound.connect({ host, port: +port });
|
||||
|
@ -1,25 +0,0 @@
|
||||
import path from 'path';
|
||||
import run from '../common/run-e2e';
|
||||
|
||||
const suites = path.resolve(__dirname, 'resources', 'auto-conf-suites.json');
|
||||
|
||||
const clientJson = {
|
||||
"service": "socks5://127.0.0.1:1081",
|
||||
"server": {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "auto-conf", "params": { "suites": suites } },
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const serverJson = {
|
||||
"service": "tcp://127.0.0.1:1082",
|
||||
"key": "9{*2gdBSdCrgnSBD",
|
||||
"presets": [
|
||||
{ "name": "auto-conf", "params": { "suites": suites } },
|
||||
]
|
||||
};
|
||||
|
||||
test('auto-conf', async () => await run({ clientJson, serverJson, repeat: 5 }));
|
@ -1,104 +0,0 @@
|
||||
[
|
||||
{
|
||||
"description": "shadowsocks stream cipher with random padding",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "shadowsocks aead cipher with random padding",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "obfs-random-padding"
|
||||
},
|
||||
{
|
||||
"name": "ss-aead-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-gcm"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "shadowsocksr auth_aes128_md5",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ssr-auth-aes128-md5"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "shadowsocksr auth_aes128_sha1",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ssr-auth-aes128-sha1"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "shadowsocksr auth_chain_a",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ssr-auth-chain-a"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "shadowsocksr auth_chain_b",
|
||||
"presets": [
|
||||
{
|
||||
"name": "ss-base"
|
||||
},
|
||||
{
|
||||
"name": "ssr-auth-chain-b"
|
||||
},
|
||||
{
|
||||
"name": "ss-stream-cipher",
|
||||
"params": {
|
||||
"method": "aes-128-ctr"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue
Block a user