refactor: replace ctx with config

refactor: set static config for preset
This commit is contained in:
Indexyz 2018-02-13 16:46:28 +08:00 committed by Micooz
parent d63f0bfddc
commit d355ed9065
34 changed files with 310 additions and 331 deletions

@ -194,19 +194,19 @@ describe('Config#init', () => {
log_max_days: 30
};
it('should ctx set correctly', () => {
const ctx = new Config(clientConf);
expect(ctx.LOCAL_PROTOCOL).toBe('tcp');
expect(ctx.LOCAL_HOST).toBe('localhost');
expect(ctx.LOCAL_PORT).toBe(1080);
expect(ctx.IS_CLIENT).toBe(true);
expect(ctx.IS_SERVER).toBe(false);
expect(ctx.SERVERS.length).toBe(1);
expect(ctx.TIMEOUT).toBe(600 * 1e3);
expect(ctx.WORKERS).toBe(0);
expect(ctx.LOG_LEVEL).toBe('warn');
expect(ctx.LOG_PATH.endsWith('blinksocks.log')).toBe(true);
expect(ctx.LOG_MAX_DAYS).toBe(30);
it('should config set correctly', () => {
const config = new Config(clientConf);
expect(config.local_protocol).toBe('tcp');
expect(config.local_host).toBe('localhost');
expect(config.local_port).toBe(1080);
expect(config.is_client).toBe(true);
expect(config.is_server).toBe(false);
expect(config.servers.length).toBe(1);
expect(config.timeout).toBe(600 * 1e3);
expect(config.workers).toBe(0);
expect(config.log_level).toBe('warn');
expect(config.log_path.endsWith('blinksocks.log')).toBe(true);
expect(config.log_max_days).toBe(30);
});
});
@ -223,11 +223,11 @@ describe('Config#initServer', () => {
tls_key: 'mock_key.pem'
};
it('should ctx set correctly', () => {
const ctx = new Config(serverConf);
expect(ctx.TRANSPORT).toBe('tls');
expect(ctx.TLS_CERT).toBeDefined();
expect(ctx.TLS_KEY).toBeDefined();
it('should config set correctly', () => {
const config = new Config(serverConf);
expect(config.transport).toBe('tls');
expect(config.tls_cert).toBeDefined();
expect(config.tls_key).toBeDefined();
});
});

@ -7,128 +7,128 @@ import url from 'url';
import qs from 'qs';
import winston from 'winston';
import isPlainObject from 'lodash.isplainobject';
import {getPresetClassByName, IPresetAddressing} from '../presets';
import {DNSCache, isValidHostname, isValidPort, logger, DNS_DEFAULT_EXPIRE} from '../utils';
import { getPresetClassByName, IPresetAddressing } from '../presets';
import { DNSCache, isValidHostname, isValidPort, logger, DNS_DEFAULT_EXPIRE } from '../utils';
function loadFileSync(file) {
return fs.readFileSync(path.resolve(process.cwd(), file));
}
export class Config {
LOCAL_PROTOCOL = null;
LOCAL_HOST = null;
LOCAL_PORT = null;
local_protocol = null;
local_host = null;
local_port = null;
SERVERS = null;
IS_CLIENT = null;
IS_SERVER = null;
servers = null;
is_client = null;
is_server = null;
FORWARD_HOST = null;
FORWARD_PORT = null;
forward_host = null;
forward_port = null;
TIMEOUT = null;
REDIRECT = null;
WORKERS = null;
timeout = null;
redirect = null;
workers = null;
DNS_EXPIRE = null;
DNS_DEFAULT_EXPIRE = null;
DNS = null;
dns_expire = null;
dns = null;
TRANSPORT = null;
SERVER_HOST = null;
SERVER_PORT = null;
TLS_CERT = null;
KEY = null;
transport = null;
server_host = null;
server_port = null;
tls_cert = null;
tls_key = null;
key = null;
PRESETS = null;
UDP_PRESETS = null;
presets = null;
udp_presets = null;
MUX = null;
MUX_CONCURRENCY = null;
LOG_PATH = null;
LOG_LEVEL = null;
LOG_MAX_DAYS = null;
mux = null;
mux_concurrency = null;
constructor(json){
const {protocol, hostname, port, query} = url.parse(json.service);
this.LOCAL_PROTOCOL = protocol.slice(0, -1);
this.LOCAL_HOST = hostname;
this.LOCAL_PORT = +port;
log_path = null;
log_level = null;
log_max_days = null;
constructor(json) {
const { protocol, hostname, port, query } = url.parse(json.service);
this.local_protocol = protocol.slice(0, -1);
this.local_host = hostname;
this.local_port = +port;
if (json.servers !== undefined) {
this.SERVERS = json.servers.filter((server) => !!server.enabled);
this.IS_CLIENT = true;
this.IS_SERVER = false;
this.servers = json.servers.filter((server) => !!server.enabled);
this.is_client = true;
this.is_server = false;
} else {
this.IS_CLIENT = false;
this.IS_SERVER = true;
this.is_client = false;
this.is_server = true;
}
this._initLogger(json);
if (this.IS_SERVER) {
this._initServer(json);
if (this.is_server) {
this.initServer(json);
}
if (this.IS_CLIENT && this.LOCAL_PROTOCOL === 'tcp') {
const {forward} = qs.parse(query);
const {hostname, port} = url.parse('tcp://' + forward);
this.FORWARD_HOST = hostname;
this.FORWARD_PORT = +port;
if (this.is_client && this.local_protocol === 'tcp') {
const { forward } = qs.parse(query);
const { hostname, port } = url.parse('tcp://' + forward);
this.forward_host = hostname;
this.forward_port = +port;
}
this.TIMEOUT = (json.timeout !== undefined) ? json.timeout * 1e3 : 600 * 1e3;
this.REDIRECT = (json.redirect !== '') ? json.redirect : null;
this.WORKERS = (json.workers !== undefined) ? json.workers : 0;
this.DNS_EXPIRE = (json.dns_expire !== undefined) ? json.dns_expire * 1e3 : DNS_DEFAULT_EXPIRE;
this.timeout = (json.timeout !== undefined) ? json.timeout * 1e3 : 600 * 1e3;
this.redirect = (json.redirect !== '') ? json.redirect : null;
this.workers = (json.workers !== undefined) ? json.workers : 0;
this.dns_expire = (json.dns_expire !== undefined) ? json.dns_expire * 1e3 : DNS_DEFAULT_EXPIRE;
// dns
if (json.dns !== undefined && json.dns.length > 0) {
this.DNS = json.dns;
this.dns = json.dns;
dns.setServers(json.dns);
}
// dns-cache
DNSCache.init(this.DNS_EXPIRE);
DNSCache.init(this.dns_expire);
}
_initServer(server) {
initServer(server) {
// service
const {protocol, hostname, port} = url.parse(server.service);
this.TRANSPORT = protocol.slice(0, -1);
this.SERVER_HOST = hostname;
this.SERVER_PORT = +port;
const { protocol, hostname, port } = url.parse(server.service);
this.transport = protocol.slice(0, -1);
this.server_host = hostname;
this.server_port = +port;
// preload tls_cert or tls_key
if (this.TRANSPORT === 'tls') {
if (this.transport === 'tls') {
logger.info(`[config] loading ${server.tls_cert}`);
this.TLS_CERT = loadFileSync(server.tls_cert);
if (this.IS_SERVER) {
this.tls_cert = loadFileSync(server.tls_cert);
if (this.is_server) {
logger.info(`[config] loading ${server.tls_key}`);
this.TLS_KEY = loadFileSync(server.tls_key);
this.tls_key = loadFileSync(server.tls_key);
}
}
this.KEY = server.key;
this.PRESETS = server.presets;
this.UDP_PRESETS = server.presets;
this.key = server.key;
this.presets = server.presets;
this.udp_presets = server.presets;
// mux
this.MUX = !!server.mux;
if (this.IS_CLIENT) {
this.MUX_CONCURRENCY = server.mux_concurrency || 10;
this.mux = !!server.mux;
if (this.is_client) {
this.mux_concurrency = server.mux_concurrency || 10;
}
// remove unnecessary presets
if (this.MUX) {
this.PRESETS = this.PRESETS.filter(
({name}) => !IPresetAddressing.isPrototypeOf(getPresetClassByName(name))
if (this.mux) {
this.presets = this.presets.filter(
({ name }) => !IPresetAddressing.isPrototypeOf(getPresetClassByName(name))
);
}
// pre-init presets
for (const {name, params = {}} of server.presets) {
for (const { name, params = {} } of server.presets) {
const clazz = getPresetClassByName(name);
clazz.checked = false;
clazz.checkParams(params);
@ -148,12 +148,12 @@ export class Config {
}
// log_path, log_level, log_max_days
this.LOG_PATH = isFile ? absolutePath : path.join(absolutePath, `bs-${this.IS_CLIENT ? 'client' : 'server'}.log`);
this.LOG_LEVEL = (json.log_level !== undefined) ? json.log_level : 'info';
this.LOG_MAX_DAYS = (json.log_max_days !== undefined) ? json.log_max_days : 0;
this.log_path = isFile ? absolutePath : path.join(absolutePath, `bs-${this.is_client ? 'client' : 'server'}.log`);
this.log_level = (json.log_level !== undefined) ? json.log_level : 'info';
this.log_max_days = (json.log_max_days !== undefined) ? json.log_max_days : 0;
logger.configure({
level: this.LOG_LEVEL,
level: this.log_level,
transports: [
new (winston.transports.Console)({
colorize: true,
@ -162,9 +162,9 @@ export class Config {
new (require('winston-daily-rotate-file'))({
json: false,
eol: os.EOL,
filename: this.LOG_PATH,
level: this.LOG_LEVEL,
maxDays: this.LOG_MAX_DAYS
filename: this.log_path,
level: this.log_level,
maxDays: this.log_max_days
})
]
});
@ -188,7 +188,7 @@ export class Config {
throw Error('"service" must be provided as "<protocol>://<host>:<port>[?params]"');
}
const {protocol: _protocol, hostname, port, query} = url.parse(json.service);
const { protocol: _protocol, hostname, port, query } = url.parse(json.service);
// service.protocol
if (typeof _protocol !== 'string') {
@ -216,14 +216,14 @@ export class Config {
// service.query
if (protocol === 'tcp') {
const {forward} = qs.parse(query);
const { forward } = qs.parse(query);
// ?forward
if (!forward) {
throw Error('require "?forward=<host>:<port>" parameter in service when using "tcp" on client side');
}
const {hostname, port} = url.parse('tcp://' + forward);
const { hostname, port } = url.parse('tcp://' + forward);
if (!isValidHostname(hostname)) {
throw Error('service.?forward.host is invalid');
}
@ -278,7 +278,7 @@ export class Config {
throw Error('"service" must be provided as "<protocol>://<host>:<port>[?params]"');
}
const {protocol: _protocol, hostname, port} = url.parse(server.service);
const { protocol: _protocol, hostname, port } = url.parse(server.service);
// service.protocol
if (typeof _protocol !== 'string') {
@ -343,7 +343,7 @@ export class Config {
// presets[].parameters
for (const preset of server.presets) {
const {name, params} = preset;
const { name, params } = preset;
if (typeof name !== 'string') {
throw Error('"server.presets[].name" must be a string');
}
@ -361,7 +361,7 @@ export class Config {
static _testCommon(common) {
// timeout
if (common.timeout !== undefined) {
const {timeout} = common;
const { timeout } = common;
if (typeof timeout !== 'number') {
throw Error('"timeout" must be a number');
}
@ -390,7 +390,7 @@ export class Config {
// log_max_days
if (common.log_max_days !== undefined) {
const {log_max_days} = common;
const { log_max_days } = common;
if (typeof log_max_days !== 'number') {
throw Error('"log_max_days" must a number');
}
@ -401,7 +401,7 @@ export class Config {
// workers
if (common.workers !== undefined) {
const {workers} = common;
const { workers } = common;
if (typeof workers !== 'number') {
throw Error('"workers" must be a number');
}
@ -415,7 +415,7 @@ export class Config {
// dns
if (common.dns !== undefined) {
const {dns} = common;
const { dns } = common;
if (!Array.isArray(dns)) {
throw Error('"dns" must be an array');
}
@ -428,7 +428,7 @@ export class Config {
// dns_expire
if (common.dns_expire !== undefined) {
const {dns_expire} = common;
const { dns_expire } = common;
if (typeof dns_expire !== 'number') {
throw Error('"dns_expire" must be a number');
}

@ -35,10 +35,10 @@ export class Hub {
_udpRelays = null; // LRU cache
_ctx = null;
_config = null;
constructor(config) {
this._ctx = new Config(config);
this._config = new Config(config);
this._onConnection = this._onConnection.bind(this);
this._udpRelays = LRU({
max: 500,
@ -50,14 +50,14 @@ export class Hub {
terminate(callback) {
// relays
this._udpRelays.reset();
if (this._ctx.MUX) {
if (this._config.mux) {
this._muxRelays.forEach((relay) => relay.destroy());
this._muxRelays.clear();
}
this._tcpRelays.forEach((relay) => relay.destroy());
this._tcpRelays.clear();
// balancer
if (this._ctx.IS_CLIENT) {
if (this._config.is_client) {
Balancer.destroy();
logger.info(`[balancer-${this._wkId}] stopped`);
}
@ -73,8 +73,8 @@ export class Hub {
if (this._tcpServer !== null) {
this.terminate();
}
if (this._ctx.IS_CLIENT) {
Balancer.start(this._ctx.SERVERS);
if (this._config.is_client) {
Balancer.start(this._config.servers);
logger.info(`[balancer-${this._wkId}] started`);
this._switchServer();
}
@ -87,7 +87,7 @@ export class Hub {
}
async _createServer() {
if (this._ctx.IS_CLIENT) {
if (this._config.is_client) {
this._tcpServer = await this._createServerOnClient();
} else {
this._tcpServer = await this._createServerOnServer();
@ -98,30 +98,30 @@ export class Hub {
async _createServerOnClient() {
return new Promise((resolve, reject) => {
let server = null;
switch (this._ctx.LOCAL_PROTOCOL) {
switch (this._config.local_protocol) {
case 'tcp':
server = tcp.createServer({forwardHost: this._ctx.FORWARD_HOST, forwardPort: this._ctx.FORWARD_PORT});
server = tcp.createServer({forwardHost: this._config.forward_host, forwardPort: this._config.forward_port});
break;
case 'socks':
case 'socks5':
case 'socks4':
case 'socks4a':
server = socks.createServer({bindAddress: this._ctx.LOCAL_HOST, bindPort: this._ctx.LOCAL_PORT});
server = socks.createServer({bindAddress: this._config.local_host, bindPort: this._config.local_port});
break;
case 'http':
case 'https':
server = http.createServer();
break;
default:
return reject(Error(`unsupported protocol: "${this._ctx.LOCAL_PROTOCOL}"`));
return reject(Error(`unsupported protocol: "${this._config.local_protocol}"`));
}
const address = {
host: this._ctx.LOCAL_HOST,
port: this._ctx.LOCAL_PORT
host: this._config.local_host,
port: this._config.local_port
};
server.on('proxyConnection', this._onConnection);
server.listen(address, () => {
const service = `${this._ctx.LOCAL_PROTOCOL}://${this._ctx.LOCAL_HOST}:${this._ctx.LOCAL_PORT}`;
const service = `${this._config.local_protocol}://${this._config.local_host}:${this._config.local_port}`;
logger.info(`[hub-${this._wkId}] blinksocks client is running at ${service}`);
resolve(server);
});
@ -131,15 +131,15 @@ export class Hub {
async _createServerOnServer() {
return new Promise((resolve, reject) => {
const address = {
host: this._ctx.LOCAL_HOST,
port: this._ctx.LOCAL_PORT
host: this._config.local_host,
port: this._config.local_port
};
const onListening = (server) => {
const service = `${this._ctx.LOCAL_PROTOCOL}://${this._ctx.LOCAL_HOST}:${this._ctx.LOCAL_PORT}`;
const service = `${this._config.local_protocol}://${this._config.local_host}:${this._config.local_port}`;
logger.info(`[hub-${this._wkId}] blinksocks server is running at ${service}`);
resolve(server);
};
switch (this._ctx.LOCAL_PROTOCOL) {
switch (this._config.local_protocol) {
case 'tcp': {
const server = net.createServer();
server.on('connection', this._onConnection);
@ -160,13 +160,13 @@ export class Hub {
break;
}
case 'tls': {
const server = tls.createServer({key: [this._ctx.TLS_KEY], cert: [this._ctx.TLS_CERT]});
const server = tls.createServer({key: [this._config.tls_key], cert: [this._config.tls_cert]});
server.on('secureConnection', this._onConnection);
server.listen(address, () => onListening(server));
break;
}
default:
return reject(Error(`unsupported protocol: "${this._ctx.LOCAL_PROTOCOL}"`));
return reject(Error(`unsupported protocol: "${this._config.local_protocol}"`));
}
});
}
@ -180,7 +180,7 @@ export class Hub {
const {address, port} = rinfo;
let proxyRequest = null;
let packet = msg;
if (this._ctx.IS_CLIENT) {
if (this._config.is_client) {
const parsed = socks.parseSocks5UdpRequest(msg);
if (parsed === null) {
logger.warn(`[hub] [${address}:${port}] drop invalid udp packet: ${dumpHex(msg)}`);
@ -219,7 +219,7 @@ export class Hub {
})(server.close);
// monkey patch for Socket.send() to meet Socks5 protocol
if (this._ctx.IS_CLIENT) {
if (this._config.is_client) {
server.send = ((send) => (data, port, host, isSs, ...args) => {
let packet = null;
if (isSs) {
@ -232,8 +232,8 @@ export class Hub {
})(server.send);
}
server.bind({address: this._ctx.LOCAL_HOST, port: this._ctx.LOCAL_PORT}, () => {
const service = `udp://${this._ctx.LOCAL_HOST}:${this._ctx.LOCAL_PORT}`;
server.bind({address: this._config.local_host, port: this._config.local_port}, () => {
const service = `udp://${this._config.local_host}:${this._config.local_port}`;
logger.info(`[hub-${this._wkId}] blinksocks udp server is running at ${service}`);
resolve(server);
});
@ -242,7 +242,7 @@ export class Hub {
_onConnection(socket, proxyRequest = null) {
logger.verbose(`[hub] [${socket.remoteAddress}:${socket.remotePort}] connected`);
if (this._ctx.IS_CLIENT) {
if (this._config.is_client) {
this._switchServer();
}
const context = {
@ -255,8 +255,8 @@ export class Hub {
};
let muxRelay = null, cid = null;
if (this._ctx.MUX) {
if (this._ctx.IS_CLIENT) {
if (this._config.mux) {
if (this._config.is_client) {
cid = makeConnID();
muxRelay = this._getMuxRelayOnClient(context, cid);
context.muxRelay = muxRelay;
@ -269,8 +269,8 @@ export class Hub {
const relay = this._createRelay(context);
// setup association between relay and muxRelay
if (this._ctx.MUX) {
if (this._ctx.IS_CLIENT) {
if (this._config.mux) {
if (this._config.is_client) {
relay.id = cid; // NOTE: this cid will be used in mux preset
muxRelay.addSubRelay(relay);
} else {
@ -316,25 +316,25 @@ export class Hub {
_createRelay(context, isMux = false) {
const props = {
context: context,
transport: this._ctx.TRANSPORT,
presets: this._ctx.PRESETS
transport: this._config.transport,
presets: this._config.presets
};
if (isMux) {
return new MuxRelay(props, this._ctx);
return new MuxRelay(props, this._config);
}
if (this._ctx.MUX) {
if (this._ctx.IS_CLIENT) {
return new Relay({...props, transport: 'mux', presets: []}, this._ctx);
if (this._config.mux) {
if (this._config.is_client) {
return new Relay({...props, transport: 'mux', presets: []}, this._config);
} else {
return new MuxRelay(props, this._ctx);
return new MuxRelay(props, this._config);
}
} else {
return new Relay(props, this._ctx);
return new Relay(props, this._config);
}
}
_createUdpRelay(context) {
return new Relay({transport: 'udp', context, presets: this._ctx.UDP_PRESETS}, this._ctx);
return new Relay({transport: 'udp', context, presets: this._config.udp_presets}, this._config);
}
_selectMuxRelay() {
@ -343,7 +343,7 @@ export class Hub {
if (concurrency < 1) {
return null;
}
if (concurrency < this._ctx.MUX_CONCURRENCY && getRandomInt(0, 1) === 0) {
if (concurrency < this._config.mux_concurrency && getRandomInt(0, 1) === 0) {
return null;
}
return relays.get([...relays.keys()][getRandomInt(0, concurrency - 1)]);
@ -352,8 +352,8 @@ export class Hub {
_switchServer() {
const server = Balancer.getFastest();
if (server) {
Config.initServer(server);
logger.info(`[balancer-${this._wkId}] use server: ${this._ctx.SERVER_HOST}:${this._ctx.SERVER_PORT}`);
this._config.initServer(server);
logger.info(`[balancer-${this._wkId}] use server: ${this._config.server_host}:${this._config.server_port}`);
}
}

@ -5,12 +5,13 @@ import {kebabCase} from '../utils';
const staticPresetCache = new Map(/* 'ClassName': <preset> */);
function createPreset(name, params = {}, ctx) {
function createPreset(name, params = {}, config) {
const ImplClass = getPresetClassByName(name);
const createOne = () => {
ImplClass.checkParams(params, ctx);
ImplClass.onInit(params, ctx);
return new ImplClass(params, ctx);
ImplClass.config = config;
ImplClass.checkParams(params);
ImplClass.onInit(params);
return new ImplClass(params);
};
let preset = null;
if (IPresetStatic.isPrototypeOf(ImplClass)) {
@ -32,16 +33,15 @@ function createPreset(name, params = {}, ctx) {
export class Middleware extends EventEmitter {
_impl = null;
_ctx = null;
_config = null;
constructor(preset, ctx) {
constructor(preset, config) {
super();
console.log(this._ctx)
this._ctx = ctx;
this._config = config;
this.onPresetNext = this.onPresetNext.bind(this);
this.onPresetBroadcast = this.onPresetBroadcast.bind(this);
this.onPresetFail = this.onPresetFail.bind(this);
this._impl = createPreset(preset.name, preset.params || {}, this._ctx);
this._impl = createPreset(preset.name, preset.params || {}, this._config);
this._impl.next = this.onPresetNext;
this._impl.broadcast = this.onPresetBroadcast;
this._impl.fail = this.onPresetFail;
@ -108,7 +108,7 @@ export class Middleware extends EventEmitter {
// clientXXX, serverXXX
const nextLifeCycleHook = (buf/* , isReverse = false */) => {
const args = {buffer: buf, next, broadcast, direct, fail};
const ret = this._ctx.IS_CLIENT ? this._impl[`client${type}`](args, extraArgs) : this._impl[`server${type}`](args, extraArgs);
const ret = this._config.is_client ? this._impl[`client${type}`](args, extraArgs) : this._impl[`server${type}`](args, extraArgs);
if (ret instanceof Buffer) {
next(ret);
}

@ -28,7 +28,7 @@ export class MuxRelay extends Relay {
'tls': [TlsInbound, TlsOutbound],
'ws': [WsInbound, WsOutbound],
};
const [Inbound, Outbound] = this._globalCtx.IS_CLIENT ? [MuxInbound, mapping[transport][1]] : [mapping[transport][0], MuxOutbound];
const [Inbound, Outbound] = this._config.is_client ? [MuxInbound, mapping[transport][1]] : [mapping[transport][0], MuxOutbound];
return {Inbound, Outbound};
}
@ -121,7 +121,7 @@ export class MuxRelay extends Relay {
logger.error(`[mux-${this.id}] fail to dispatch data frame(size=${data.length}), no such sub connection(cid=${cid})`);
return;
}
if (this._globalCtx.IS_CLIENT || relay.isOutboundReady()) {
if (this._config.is_client || relay.isOutboundReady()) {
relay.decode(data);
} else {
// TODO: find a way to avoid using relay._pendingFrames

@ -21,7 +21,7 @@ export class Pipe extends EventEmitter {
_presets = null;
_ctx = null;
_config = null;
get destroyed() {
return this._destroyed;
@ -31,9 +31,9 @@ export class Pipe extends EventEmitter {
return this._presets;
}
constructor({presets, isUdp = false}, ctx) {
constructor({presets, isUdp = false}, config) {
super();
this._ctx = ctx;
this._config = config;
this.broadcast = this.broadcast.bind(this);
this.onReadProperty = this.onReadProperty.bind(this);
this.createMiddlewares(presets);
@ -143,7 +143,7 @@ export class Pipe extends EventEmitter {
}
_createMiddleware(preset) {
const middleware = new Middleware(preset, this._ctx);
const middleware = new Middleware(preset, this._config);
this._attachEvents(middleware);
// set readProperty()
const impl = middleware.getImplement();

@ -54,7 +54,7 @@ export class Relay extends EventEmitter {
_destroyed = false;
_globalCtx = null;
_config = null;
get id() {
return this._id;
@ -65,9 +65,9 @@ export class Relay extends EventEmitter {
this._ctx.cid = id;
}
constructor({transport, context, presets = []}, ctx) {
constructor({transport, context, presets = []}, config) {
super();
this._globalCtx = ctx;
this._config = config;
this.updatePresets = this.updatePresets.bind(this);
this.onBroadcast = this.onBroadcast.bind(this);
this.onEncoded = this.onEncoded.bind(this);
@ -85,8 +85,8 @@ export class Relay extends EventEmitter {
};
// bounds
const {Inbound, Outbound} = this.getBounds(transport);
const inbound = new Inbound({context: this._ctx, globalContext: this._globalCtx});
const outbound = new Outbound({context: this._ctx, globalContext: this._globalCtx});
const inbound = new Inbound({context: this._ctx, globalContext: this._config});
const outbound = new Outbound({context: this._ctx, globalContext: this._config});
this._inbound = inbound;
this._outbound = outbound;
// outbound
@ -130,7 +130,7 @@ export class Relay extends EventEmitter {
if (transport === 'udp') {
[Inbound, Outbound] = [UdpInbound, UdpOutbound];
} else {
[Inbound, Outbound] = this._globalCtx.IS_CLIENT ? [TcpInbound, mapping[transport][1]] : [mapping[transport][0], TcpOutbound];
[Inbound, Outbound] = this._config.is_client ? [TcpInbound, mapping[transport][1]] : [mapping[transport][0], TcpOutbound];
}
return {Inbound, Outbound};
}
@ -167,7 +167,7 @@ export class Relay extends EventEmitter {
if (type === CONNECT_TO_REMOTE) {
const remote = `${this._remoteInfo.host}:${this._remoteInfo.port}`;
const target = `${action.payload.host}:${action.payload.port}`;
if (this._globalCtx.MUX && this._globalCtx.IS_CLIENT && this._transport !== 'udp') {
if (this._config.mux && this._config.is_client && this._transport !== 'udp') {
logger.info(`[relay-${this.id}] [${remote}] request over mux-${this._ctx.muxRelay.id}: ${target}`);
return;
}
@ -196,7 +196,7 @@ export class Relay extends EventEmitter {
type: CONNECTION_CREATED,
payload: {transport, ...this._remoteInfo}
});
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
this._pipe.broadcast(null, {
type: CONNECT_TO_REMOTE,
payload: {...proxyRequest, keepAlive: true} // keep previous connection alive, don't re-connect
@ -207,7 +207,7 @@ export class Relay extends EventEmitter {
}
onEncoded(buffer) {
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
this._outbound.write(buffer);
} else {
this._inbound.write(buffer);
@ -215,7 +215,7 @@ export class Relay extends EventEmitter {
}
onDecoded(buffer) {
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
this._inbound.write(buffer);
} else {
this._outbound.write(buffer);
@ -271,7 +271,7 @@ export class Relay extends EventEmitter {
* create pipes for both data forward and backward
*/
createPipe(presets) {
const pipe = new Pipe({presets, isUdp: this._transport === 'udp'}, this._globalCtx);
const pipe = new Pipe({presets, isUdp: this._transport === 'udp'}, this._config);
pipe.on('broadcast', this.onBroadcast.bind(this)); // if no action were caught by presets
pipe.on(`post_${PIPE_ENCODE}`, this.onEncoded);
pipe.on(`post_${PIPE_DECODE}`, this.onDecoded);
@ -294,7 +294,7 @@ export class Relay extends EventEmitter {
this._remoteInfo = null;
this._proxyRequest = null;
this._destroyed = true;
this._globalCtx = null;
this._config = null;
}
}

@ -118,11 +118,11 @@ export default class AeadRandomCipherPreset extends IPreset {
}
}
static onInit({method, info = DEFAULT_INFO, factor = DEFAULT_FACTOR}, {KEY}) {
static onInit({method, info = DEFAULT_INFO, factor = DEFAULT_FACTOR}) {
AeadRandomCipherPreset.cipherName = method;
AeadRandomCipherPreset.info = Buffer.from(info);
AeadRandomCipherPreset.factor = factor;
AeadRandomCipherPreset.rawKey = Buffer.from(KEY);
AeadRandomCipherPreset.rawKey = Buffer.from(AeadRandomCipherPreset.config.key);
AeadRandomCipherPreset.keySaltSize = ciphers[method];
}

@ -76,8 +76,6 @@ export default class AutoConfPreset extends IPreset {
_header = null;
_ctx = null;
static suites = [];
static checkParams({suites}) {
@ -86,11 +84,6 @@ export default class AutoConfPreset extends IPreset {
}
}
constructor(_, ctx) {
super();
this._ctx = ctx;
}
static async onInit({suites: uri}) {
logger.info(`[auto-conf] loading suites from: ${uri}`);
let suites = [];
@ -118,7 +111,7 @@ export default class AutoConfPreset extends IPreset {
createRequestHeader(suites) {
const sid = crypto.randomBytes(2);
const utc = ntb(getCurrentTimestampInt(), 4, BYTE_ORDER_LE);
const key = EVP_BytesToKey(Buffer.from(this._ctx.KEY).toString('base64') + hash('md5', sid).toString('base64'), 16, 16);
const key = EVP_BytesToKey(Buffer.from(AutoConfPreset.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]));
@ -156,7 +149,7 @@ export default class AutoConfPreset extends IPreset {
}
const sid = buffer.slice(0, 2);
const request_hmac = buffer.slice(6, 22);
const key = EVP_BytesToKey(Buffer.from(this._ctx.KEY).toString('base64') + hash('md5', sid).toString('base64'), 16, 16);
const key = EVP_BytesToKey(Buffer.from(AutoConfPreset.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)}`);

@ -78,8 +78,6 @@ export default class BaseAuthPreset extends IPresetAddressing {
_port = null; // buffer
_ctx = null;
static checkParams({method = DEFAULT_HASH_METHOD}) {
const methods = Object.keys(HMAC_METHODS);
if (!methods.includes(method)) {
@ -87,18 +85,17 @@ export default class BaseAuthPreset extends IPresetAddressing {
}
}
static onInit({method = DEFAULT_HASH_METHOD}, {KEY}) {
static onInit({method = DEFAULT_HASH_METHOD}) {
BaseAuthPreset.hmacMethod = method;
BaseAuthPreset.hmacLen = HMAC_METHODS[method];
BaseAuthPreset.hmacKey = EVP_BytesToKey(KEY, 16, 16);
BaseAuthPreset.hmacKey = EVP_BytesToKey(BaseAuthPreset.config.key, 16, 16);
}
constructor(_, ctx) {
constructor() {
super();
this._ctx = ctx;
const {hmacKey: key} = BaseAuthPreset;
const iv = hash('md5', Buffer.from(ctx.KEY + 'base-auth'));
if (ctx.IS_CLIENT) {
const iv = hash('md5', Buffer.from(BaseAuthPreset.config.key + 'base-auth'));
if (BaseAuthPreset.config.is_client) {
this._cipher = crypto.createCipheriv('aes-128-cfb', key, iv);
} else {
this._decipher = crypto.createDecipheriv('aes-128-cfb', key, iv);
@ -114,7 +111,7 @@ export default class BaseAuthPreset extends IPresetAddressing {
}
onNotified(action) {
if (this._ctx.IS_CLIENT && action.type === CONNECT_TO_REMOTE) {
if (BaseAuthPreset.config.is_client && action.type === CONNECT_TO_REMOTE) {
const {host, port} = action.payload;
this._host = Buffer.from(host);
this._port = numberToBuffer(port);

@ -110,6 +110,13 @@ export class IPreset {
*/
static checked = false;
/**
* server config
* @type {Config}
*/
static config = null;
/**
* will become true after onInit()
* @type {boolean}
@ -120,7 +127,7 @@ export class IPreset {
* check params passed to the preset, if any errors, should throw directly
* @param params
*/
static checkParams(params, ctx) {
static checkParams(params) {
}
@ -128,7 +135,7 @@ export class IPreset {
* you can make some cache in this function
* @param params
*/
static onInit(params, ctx) {
static onInit(params) {
}

@ -126,13 +126,13 @@ export default class SsAeadCipherPreset extends IPreset {
}
}
static onInit({method}, {KEY}) {
static onInit({method}) {
const [keySize, saltSize, nonceSize] = ciphers[method];
SsAeadCipherPreset.cipherName = method;
SsAeadCipherPreset.keySize = keySize;
SsAeadCipherPreset.saltSize = saltSize;
SsAeadCipherPreset.nonceSize = nonceSize;
SsAeadCipherPreset.evpKey = EVP_BytesToKey(KEY, keySize, 16);
SsAeadCipherPreset.evpKey = EVP_BytesToKey(SsAeadCipherPreset.config.key, keySize, 16);
SsAeadCipherPreset.isUseLibSodium = Object.keys(libsodium_functions).includes(method);
}

@ -73,8 +73,6 @@ export default class SsBasePreset extends IPresetAddressing {
_headSize = 0;
_ctx = null;
get headSize() {
return this._headSize;
}
@ -85,13 +83,8 @@ export default class SsBasePreset extends IPresetAddressing {
this._port = null;
}
constructor(_, ctx) {
super();
this._ctx = ctx;
}
onNotified(action) {
if (this._ctx.IS_CLIENT && action.type === CONNECT_TO_REMOTE) {
if (SsBasePreset.config.is_client && action.type === CONNECT_TO_REMOTE) {
const {host, port} = action.payload;
const type = getHostType(host);
this._atyp = type;

@ -97,14 +97,14 @@ export default class SsStreamCipherPreset extends IPreset {
return this._iv;
}
constructor({method}, {KEY}) {
constructor({method}) {
super();
const [keySize, ivSize] = ciphers[method];
const iv = crypto.randomBytes(ivSize);
this._algorithm = ['rc4-md5', 'rc4-md5-6'].includes(method) ? 'rc4' : method;
this._keySize = keySize;
this._ivSize = ivSize;
this._key = EVP_BytesToKey(KEY, keySize, ivSize);
this._key = EVP_BytesToKey(SsStreamCipherPreset.config.key, keySize, ivSize);
this._iv = method === 'rc4-md5-6' ? iv.slice(0, 6) : iv;
}

@ -102,8 +102,8 @@ export default class SsrAuthAes128Preset extends IPreset {
_adBuf = null;
static onInit({KEY}) {
SsrAuthAes128Preset.userKey = EVP_BytesToKey(KEY, 16, 16);
static onInit() {
SsrAuthAes128Preset.userKey = EVP_BytesToKey(SsrAuthAes128Preset.config.key, 16, 16);
SsrAuthAes128Preset.clientId = crypto.randomBytes(4);
SsrAuthAes128Preset.connectionId = getRandomInt(0, 0x00ffffff);
}

@ -143,16 +143,13 @@ export default class SsrAuthChainPreset extends IPreset {
_adBuf = null;
_ctx = null;
static onInit() {
SsrAuthChainPreset.clientId = crypto.randomBytes(4);
SsrAuthChainPreset.connectionId = getRandomInt(0, 0x00ffffff);
}
constructor(_, ctx) {
constructor() {
super();
this._ctx = ctx;
this._rngClient = xorshift128plus();
this._rngServer = xorshift128plus();
this._adBuf = new AdvancedBuffer({getPacketLength: this.onReceiving.bind(this)});
@ -235,17 +232,17 @@ export default class SsrAuthChainPreset extends IPreset {
createChunks(buffer) {
const userKey = this._userKey;
const max_payload_size = this._ctx.IS_CLIENT ? 2800 : (this._tcpMss - this._overhead);
const max_payload_size = SsrAuthChainPreset.config.is_client ? 2800 : (this._tcpMss - this._overhead);
return getChunks(buffer, max_payload_size).map((payload) => {
let _payload = payload;
if (this._ctx.IS_SERVER && this._encodeChunkId === 1) {
if (SsrAuthChainPreset.config.is_server && this._encodeChunkId === 1) {
_payload = Buffer.concat([ntb(this._tcpMss, 2, BYTE_ORDER_LE), payload]);
}
const rc4_enc_payload = this._cipher.update(_payload);
const hash = this._ctx.IS_CLIENT ? this._lastClientHash : this._lastServerHash;
const hash = SsrAuthChainPreset.config.is_client ? this._lastClientHash : this._lastServerHash;
const size = rc4_enc_payload.length ^ hash.slice(-2).readUInt16LE(0);
// generate two pieces of random bytes
const rng = this._ctx.IS_CLIENT ? this._rngClient : this._rngServer;
const rng = SsrAuthChainPreset.config.is_client ? this._rngClient : this._rngServer;
const random_bytes_len = this.getRandomBytesLengthForTcp(hash, _payload.length, rng);
const random_bytes = crypto.randomBytes(random_bytes_len);
const random_divide_pos = random_bytes_len > 0 ? rng.next().mod(8589934609).mod(random_bytes_len).toNumber() : 0;
@ -256,7 +253,7 @@ export default class SsrAuthChainPreset extends IPreset {
const hmac_key = Buffer.concat([userKey, ntb(this._encodeChunkId, 4, BYTE_ORDER_LE)]);
const chunk_hmac = hmac('md5', hmac_key, chunk);
chunk = Buffer.concat([chunk, chunk_hmac.slice(0, 2)]);
if (this._ctx.IS_CLIENT) {
if (SsrAuthChainPreset.config.is_client) {
this._lastClientHash = chunk_hmac;
} else {
this._lastServerHash = chunk_hmac;
@ -353,9 +350,9 @@ export default class SsrAuthChainPreset extends IPreset {
if (buffer.length < 2 || this._adBuf === null) {
return; // too short to get size
}
const hash = this._ctx.IS_CLIENT ? this._lastServerHash : this._lastClientHash;
const hash = SsrAuthChainPreset.config.is_client ? this._lastServerHash : this._lastClientHash;
const payload_len = buffer.readUInt16LE(0) ^ hash.readUInt16LE(14);
const rng = this._ctx.IS_CLIENT ? this._rngServer : this._rngClient;
const rng = SsrAuthChainPreset.config.is_client ? this._rngServer : this._rngClient;
const random_bytes_len = this.getRandomBytesLengthForTcp(hash, payload_len, rng);
const chunk_size = 2 + random_bytes_len + payload_len + 2;
if (chunk_size >= 4096) {
@ -379,9 +376,9 @@ export default class SsrAuthChainPreset extends IPreset {
return fail(`unexpected chunk hmac, chunk=${dumpHex(chunk)}`);
}
// drop random_bytes, get encrypted payload
const hash = this._ctx.IS_CLIENT ? this._lastServerHash : this._lastClientHash;
const hash = SsrAuthChainPreset.config.is_client ? this._lastServerHash : this._lastClientHash;
const payload_len = chunk.readUInt16LE(0) ^ hash.readUInt16LE(14);
const rng = this._ctx.IS_CLIENT ? this._rngServer : this._rngClient;
const rng = SsrAuthChainPreset.config.is_client ? this._rngServer : this._rngClient;
const random_bytes_len = this.getRandomBytesLengthForTcp(hash, payload_len, rng);
let enc_payload = null;
if (random_bytes_len > 0) {
@ -393,12 +390,12 @@ export default class SsrAuthChainPreset extends IPreset {
// decrypt payload
let payload = this._decipher.update(enc_payload);
// update hash
if (this._ctx.IS_CLIENT) {
if (SsrAuthChainPreset.config.is_client) {
this._lastServerHash = new_hash;
} else {
this._lastClientHash = new_hash;
}
if (this._ctx.IS_CLIENT && this._decodeChunkId === 1) {
if (SsrAuthChainPreset.config.is_client && this._decodeChunkId === 1) {
this._tcpMss = payload.readUInt16LE(0);
payload = payload.slice(2);
}

@ -29,13 +29,6 @@ export default class TrackerPreset extends IPreset {
_targetHost;
_targetPort;
_ctx = null;
constructor(_, ctx) {
super();
this._ctx = ctx;
}
onNotified({type, payload}) {
switch (type) {
case CONNECTION_CREATED: {
@ -102,7 +95,7 @@ export default class TrackerPreset extends IPreset {
if (strs.length > TRACK_MAX_SIZE) {
strs = strs.slice(0, perSize).concat([' ... ']).concat(strs.slice(-perSize));
}
const summary = this._ctx.IS_CLIENT ? `out/in = ${up}/${dp}, ${ub}b/${db}b` : `in/out = ${dp}/${up}, ${db}b/${ub}b`;
const summary = TrackerPreset.config.is_client ? `out/in = ${up}/${dp}, ${ub}b/${db}b` : `in/out = ${dp}/${up}, ${db}b/${ub}b`;
logger.info(`[tracker:${this._transport}] summary(${summary}) abstract(${strs.join(' ')})`);
}

@ -192,8 +192,6 @@ export default class V2rayVmessPreset extends IPresetAddressing {
_cipherNonce = 0;
_decipherNonce = 0;
_ctx = null;
static checkParams({id, security = 'aes-128-gcm'}) {
if (Buffer.from(id.split('-').join(''), 'hex').length !== 16) {
throw Error('id is not a valid uuid');
@ -204,9 +202,9 @@ export default class V2rayVmessPreset extends IPresetAddressing {
}
}
static onInit({id, security = 'aes-128-gcm'}, {IS_CLIENT}) {
static onInit({id, security = 'aes-128-gcm'}) {
V2rayVmessPreset.uuid = Buffer.from(id.split('-').join(''), 'hex');
if (IS_CLIENT) {
if (V2rayVmessPreset.config.is_client) {
V2rayVmessPreset.security = securityTypes[security];
}
setInterval(() => V2rayVmessPreset.updateAuthCache(), 1e3);
@ -233,9 +231,8 @@ export default class V2rayVmessPreset extends IPresetAddressing {
this.userHashCache = newItems;
}
constructor(_, ctx) {
constructor() {
super();
this._ctx = ctx;
this._adBuf = new AdvancedBuffer({getPacketLength: this.onReceiving.bind(this)});
this._adBuf.on('data', this.onChunkReceived.bind(this));
}
@ -257,7 +254,7 @@ export default class V2rayVmessPreset extends IPresetAddressing {
}
onNotified(action) {
if (this._ctx.IS_CLIENT && action.type === CONNECT_TO_REMOTE) {
if (V2rayVmessPreset.config.is_client && action.type === CONNECT_TO_REMOTE) {
const {host, port} = action.payload;
const type = getAddrType(host);
this._atyp = type;
@ -272,7 +269,7 @@ export default class V2rayVmessPreset extends IPresetAddressing {
beforeOut({buffer}) {
if (!this._isHeaderSent) {
this._isHeaderSent = true;
const header = this._ctx.IS_CLIENT ? this.createRequestHeader() : this.createResponseHeader();
const header = V2rayVmessPreset.config.is_client ? this.createRequestHeader() : this.createResponseHeader();
const chunks = this.getBufferChunks(buffer);
return Buffer.concat([header, ...chunks]);
} else {

@ -5,12 +5,12 @@ import EventEmitter from 'events';
class Bound extends EventEmitter {
_ctx = null;
_globalCtx = null;
_config = null;
constructor({context, globalContext}) {
constructor({context, config}) {
super();
this._ctx = context;
this._globalCtx = globalContext;
this._config = config;
}
get ctx() {

@ -7,7 +7,7 @@ export class MuxInbound extends Inbound {
constructor(props) {
super(props);
this.onDrain = this.onDrain.bind(this);
if (this._globalCtx.IS_SERVER) {
if (this._config.is_server) {
const inbound = this.ctx.muxRelay.getInbound();
inbound.on('drain', this.onDrain);
} else {
@ -20,7 +20,7 @@ export class MuxInbound extends Inbound {
}
get bufferSize() {
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
const totalBufferSize = 0;
// const subRelays = this.ctx.thisRelay.getSubRelays();
// if (subRelays) {
@ -70,7 +70,7 @@ export class MuxInbound extends Inbound {
}
write(buffer) {
if (this._globalCtx.IS_SERVER) {
if (this._config.is_server) {
const {muxRelay, cid} = this.ctx;
muxRelay.encode(buffer, {cid});
}
@ -83,7 +83,7 @@ export class MuxInbound extends Inbound {
close() {
const doClose = () => {
if (this._globalCtx.IS_SERVER) {
if (this._config.is_server) {
const {muxRelay, cid} = this.ctx;
const inbound = muxRelay.getInbound();
if (inbound) {
@ -112,7 +112,7 @@ export class MuxOutbound extends Outbound {
constructor(props) {
super(props);
this.onDrain = this.onDrain.bind(this);
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
const outbound = this.ctx.muxRelay.getOutbound();
outbound.on('drain', this.onDrain);
} else {
@ -121,7 +121,7 @@ export class MuxOutbound extends Outbound {
}
get bufferSize() {
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
const outbound = this.ctx.muxRelay.getOutbound();
if (outbound) {
return outbound.bufferSize;
@ -148,7 +148,7 @@ export class MuxOutbound extends Outbound {
}
write(buffer) {
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
const {muxRelay, proxyRequest, cid} = this.ctx;
if (this._isFirstFrame) {
this._isFirstFrame = false;
@ -166,7 +166,7 @@ export class MuxOutbound extends Outbound {
close() {
const doClose = () => {
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
const {muxRelay, cid} = this.ctx;
const outbound = muxRelay.getOutbound();
if (outbound) {

@ -35,7 +35,7 @@ export class TcpInbound extends Inbound {
this._socket.on('timeout', this.onTimeout);
this._socket.on('end', this.onHalfClose);
this._socket.on('close', this.onClose);
this._socket.setTimeout && this._socket.setTimeout(this._globalCtx.TIMEOUT);
this._socket.setTimeout && this._socket.setTimeout(this._config.timeout);
}
}
@ -62,7 +62,7 @@ export class TcpInbound extends Inbound {
}
onReceive(buffer) {
const direction = this._globalCtx.IS_CLIENT ? PIPE_ENCODE : PIPE_DECODE;
const direction = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
this.ctx.pipe.feed(direction, buffer);
// throttle receiving data to reduce memory grow:
// https://github.com/blinksocks/blinksocks/issues/60
@ -85,7 +85,7 @@ export class TcpInbound extends Inbound {
}
onTimeout() {
logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._globalCtx.TIMEOUT / 1e3}s`);
logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
this.onClose();
}
@ -153,16 +153,16 @@ export class TcpInbound extends Inbound {
logger.error(`[${this.name}] [${this.remote}] preset "${name}" fail to process: ${message}`);
// close connection directly on client side
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
logger.warn(`[${this.name}] [${this.remote}] connection closed`);
this.onClose();
}
// for server side, redirect traffic if "redirect" is set, otherwise, close connection after a random timeout
if (this._globalCtx.IS_SERVER && !this._globalCtx.MUX) {
if (this._globalCtx.REDIRECT) {
if (this._config.is_server && !this._config.mux) {
if (this._config.redirect) {
const {orgData} = action.payload;
const [host, port] = this._globalCtx.REDIRECT.split(':');
const [host, port] = this._config.redirect.split(':');
logger.warn(`[${this.name}] [${this.remote}] connection is redirecting to: ${host}:${port}`);
@ -189,11 +189,11 @@ export class TcpInbound extends Inbound {
}
onPresetPauseRecv() {
this._globalCtx.IS_SERVER && (this._socket && this._socket.pause())
this._config.is_server && (this._socket && this._socket.pause())
}
onPresetResumeRecv() {
this._globalCtx.IS_SERVER && (this._socket && this._socket.resume());
this._config.is_server && (this._socket && this._socket.resume());
}
}
@ -237,7 +237,7 @@ export class TcpOutbound extends Outbound {
}
onReceive(buffer) {
const direction = this._globalCtx.IS_CLIENT ? PIPE_DECODE : PIPE_ENCODE;
const direction = this._config.is_client ? PIPE_DECODE : PIPE_ENCODE;
this.ctx.pipe.feed(direction, buffer);
// throttle receiving data to reduce memory grow:
// https://github.com/blinksocks/blinksocks/issues/60
@ -260,7 +260,7 @@ export class TcpOutbound extends Outbound {
}
onTimeout() {
logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._globalCtx.TIMEOUT / 1e3}s`);
logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
this.onClose();
}
@ -318,17 +318,17 @@ export class TcpOutbound extends Outbound {
const {host, port, keepAlive, onConnected} = action.payload;
if (!keepAlive || !this._socket) {
try {
if (this._globalCtx.IS_SERVER) {
if (this._config.is_server) {
await this.connect({host, port});
}
if (this._globalCtx.IS_CLIENT) {
await this.connect({host: this._globalCtx.SERVER_HOST, port: this._globalCtx.SERVER_PORT});
if (this._config.is_client) {
await this.connect({host: this._config.server_host, port: this._config.server_port});
}
this._socket.on('connect', () => {
if (typeof onConnected === 'function') {
onConnected((buffer) => {
if (buffer) {
const type = this._globalCtx.IS_CLIENT ? PIPE_ENCODE : PIPE_DECODE;
const type = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
this.ctx.pipe.feed(type, buffer, {cid: this.ctx.proxyRequest.cid, host, port});
}
});
@ -345,11 +345,11 @@ export class TcpOutbound extends Outbound {
}
onPresetPauseSend() {
this._globalCtx.IS_SERVER && (this._socket && this._socket.pause());
this._config.is_server && (this._socket && this._socket.pause());
}
onPresetResumeSend() {
this._globalCtx.IS_SERVER && (this._socket && this._socket.resume());
this._config.is_server && (this._socket && this._socket.resume());
}
async connect({host, port}) {
@ -364,7 +364,7 @@ export class TcpOutbound extends Outbound {
this._socket.on('timeout', this.onTimeout);
this._socket.on('data', this.onReceive);
this._socket.on('drain', this.onDrain);
this._socket.setTimeout(this._globalCtx.TIMEOUT);
this._socket.setTimeout(this._config.timeout);
}
async _connect({host, port}) {

@ -29,7 +29,7 @@ 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._globalCtx.TLS_CERT]});
return tls.connect({host, port, ca: [this._config.tls_cert]});
}
}

@ -18,7 +18,7 @@ export class UdpInbound extends Inbound {
}
onReceive(buffer, rinfo) {
const type = this._globalCtx.IS_CLIENT ? PIPE_ENCODE : PIPE_DECODE;
const type = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
this._rinfo = rinfo;
this.ctx.pipe.feed(type, buffer);
}
@ -51,7 +51,7 @@ export class UdpInbound extends Inbound {
logger.warn(`[udp:inbound] [${this.remote}]:`, err);
}
};
if (this._globalCtx.IS_CLIENT) {
if (this._config.is_client) {
const isSs = this.ctx.pipe.presets.some(({name}) => ['ss-base'].includes(name));
this._socket.send(buffer, port, address, isSs, onSendError);
} else {
@ -85,7 +85,7 @@ export class UdpOutbound extends Outbound {
}
onReceive(buffer) {
const type = this._globalCtx.IS_CLIENT ? PIPE_DECODE : PIPE_ENCODE;
const type = this._config.is_client ? PIPE_DECODE : PIPE_ENCODE;
this.ctx.pipe.feed(type, buffer);
}
@ -121,11 +121,11 @@ export class UdpOutbound extends Outbound {
onConnectToRemote(action) {
const {host, port, onConnected} = action.payload;
if (this._globalCtx.IS_CLIENT) {
this._targetHost = this._globalCtx.SERVER_HOST;
this._targetPort = this._globalCtx.SERVER_PORT;
if (this._config.is_client) {
this._targetHost = this._config.server_host;
this._targetPort = this._config.server_port;
}
if (this._globalCtx.IS_SERVER) {
if (this._config.is_server) {
this._targetHost = host;
this._targetPort = port;
if (typeof onConnected === 'function') {

@ -5,13 +5,15 @@ import {Middleware} from '../../src/core/middleware';
export class PresetRunner extends EventEmitter {
_ctx = null;
_config = null;
constructor({name, params = {}}, context = {}) {
constructor({name, params = {}}, config = {}) {
super();
this._ctx = context;
getPresetClassByName(name).checkParams(params);
this.middleware = new Middleware({name, params}, context);
this._config = config;
const clazz = getPresetClassByName(name);
clazz.config = config;
clazz.checkParams(params);
this.middleware = new Middleware({name, params}, config);
}
notify(action) {
@ -32,7 +34,7 @@ export class PresetRunner extends EventEmitter {
this.middleware.on('fail', reject);
this.middleware.on('broadcast', (name, action) => this.emit('broadcast', action));
this.middleware.write({
direction: this._ctx.IS_CLIENT ? PIPE_ENCODE : PIPE_DECODE,
direction: this._config.is_client ? PIPE_ENCODE : PIPE_DECODE,
buffer: data,
direct: resolve,
isUdp
@ -50,7 +52,7 @@ export class PresetRunner extends EventEmitter {
this.middleware.on('fail', reject);
this.middleware.on('broadcast', (name, action) => this.emit('broadcast', action));
this.middleware.write({
direction: this._ctx.IS_CLIENT ? PIPE_DECODE : PIPE_ENCODE,
direction: this._config.is_client ? PIPE_DECODE : PIPE_ENCODE,
buffer: data,
direct: resolve,
isUdp

@ -14,8 +14,8 @@ test('running on both client and server', async () => {
acl: path.join(__dirname, 'acl.txt')
}
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
await sleep(20);

@ -7,9 +7,9 @@ test('running on client and server', async () => {
method: 'aes-128-gcm'
}
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_server: false
});
const packet_1 = await runner.forward('12');

@ -5,9 +5,9 @@ test('tcp relay on client and server', async () => {
let runner = new PresetRunner({
name: 'base-auth'
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_server: false
});
// client
@ -36,9 +36,9 @@ test('tcp relay on client and server', async () => {
runner = new PresetRunner({
name: 'base-auth'
}, {
KEY: 'secret',
IS_CLIENT: false,
IS_SERVER: true
key: 'secret',
is_client: false,
is_server: true
});
runner.on('broadcast', (action) => {
@ -61,9 +61,9 @@ test('udp relay on client and server', async () => {
let runner = new PresetRunner({
name: 'base-auth'
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_server: false
});
// client
@ -86,9 +86,9 @@ test('udp relay on client and server', async () => {
runner = new PresetRunner({
name: 'base-auth'
}, {
KEY: 'secret',
IS_CLIENT: false,
IS_SERVER: true
key: 'secret',
is_client: false,
is_server: true
});
runner.on('broadcast', (action) => {

@ -4,8 +4,8 @@ test('running on both client and server', async () => {
const runner = new PresetRunner({
name: 'obfs-random-padding'
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
const chunk = await runner.forward('12');

@ -7,9 +7,9 @@ test('tcp relay on client and server', async () => {
method: 'aes-128-gcm'
}
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_serevr: false
});
const packet_1 = await runner.forward('12');
@ -38,9 +38,9 @@ test('udp relay on client and server', async () => {
method: 'aes-128-gcm'
}
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_server: false
});
const packet = await runner.forwardUdp('12');

@ -5,8 +5,8 @@ test('running on client', async () => {
const runner = new PresetRunner({
name: 'ss-base'
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
runner.notify({
@ -40,8 +40,8 @@ test('running on server', async () => {
const runner = new PresetRunner({
name: 'ss-base'
}, {
IS_CLIENT: false,
IS_SERVER: true
is_client: false,
is_server: true
});
runner.on('broadcast', (action) => {

@ -7,9 +7,9 @@ test('tcp relay on client and server', async () => {
method: 'aes-128-ctr'
}
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_server: false
});
// should add 16 more bytes at the beginning
@ -35,9 +35,9 @@ test('udp relay on client and server', async () => {
method: 'rc4-md5-6'
}
}, {
KEY: 'secret',
IS_CLIENT: true,
IS_SERVER: false
key: 'secret',
is_client: true,
is_server: false
});
expect(await runner.forwardUdp('1')).toHaveLength(6 + 1);

@ -13,8 +13,8 @@ test('running on both client and server', async () => {
save_interval: 1
}
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
runner.notify({type: CONNECTION_CREATED});

@ -5,8 +5,8 @@ test('tcp relay on client and server', async () => {
const runner = new PresetRunner({
name: 'tracker'
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
const actionPayload = {
@ -33,8 +33,8 @@ test('udp relay on client and server', async () => {
const runner = new PresetRunner({
name: 'tracker'
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
expect(await runner.forwardUdp('12')).toMatchSnapshot();

@ -9,8 +9,8 @@ test('running on client', async () => {
security: 'aes-128-gcm'
}
}, {
IS_CLIENT: true,
IS_SERVER: false
is_client: true,
is_server: false
});
runner.notify({
@ -40,8 +40,8 @@ test('running on server', async () => {
id: 'a3482e88-686a-4a58-8126-99c9df64b7bf'
}
}, {
IS_CLIENT: false,
IS_SERVER: true
is_client: false,
is_server: true
});
// fail on wrong data