UNPKG

shadowsocks-single

Version:

Yet another shadowsocks implementation for nodejs to help you bypass firewalls.

264 lines (217 loc) 6.4 kB
'use strict'; exports.__esModule = true; exports.BufferFrom = undefined; exports.isWindows = isWindows; exports.safelyKill = safelyKill; exports.safelyKillChild = safelyKillChild; exports.fileLog = fileLog; exports.createSafeAfterHandler = createSafeAfterHandler; exports.closeSilently = closeSilently; exports.mkdirIfNotExistSync = mkdirIfNotExistSync; exports.sendDgram = sendDgram; exports.writeOrPause = writeOrPause; exports.getDstInfo = getDstInfo; exports.getDstInfoFromUDPMsg = getDstInfoFromUDPMsg; exports.formatConfig = formatConfig; exports.getDstStr = getDstStr; var _ip = require('ip'); var _ip2 = _interopRequireDefault(_ip); var _os = require('os'); var _os2 = _interopRequireDefault(_os); var _fs = require('fs'); var _path = require('path'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DEFAULT_PATH = (0, _path.join)(__dirname, '../logs/debug.log'); var hasOwnProperty = {}.hasOwnProperty; var platform = null; var BufferFrom = exports.BufferFrom = function () { try { Buffer.from('aa', 'hex'); } catch (err) { return function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return new (Function.prototype.bind.apply(Buffer, [null].concat(args)))(); }; } return Buffer.from; }(); function isWindows() { if (!platform) { platform = _os2.default.type(); } return platform === 'Windows_NT'; } function safelyKill(pid, signal) { if (pid === null || pid === undefined) { return; } if (signal && !isWindows()) { process.kill(pid, signal); } else { process.kill(pid); } } function safelyKillChild(child, signal) { if (!child) { return; } if (signal && !isWindows()) { child.kill(signal); } else { child.kill(); } } function fileLog(content) { var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_PATH; (0, _fs.writeFileSync)(path, content); } // NOTE: https://github.com/winstonjs/winston/issues/228 // Winston will log things asynchronously so we have to // make sure it has log the error before exiting this // process. // And this is disappointing. function createSafeAfterHandler(logger, next) { var numFlushes = 0; var numFlushed = 0; return function () { Object.keys(logger.transports).forEach(function (k) { // eslint-disable-next-line var stream = logger.transports[k]._stream; if (stream) { numFlushes += 1; stream.once('finish', function () { numFlushed += 1; if (numFlushes === numFlushed) { next(); } }); stream.end(); } }); if (numFlushes === 0) { next(); } }; } function closeSilently(server) { if (server) { try { server.close(); } catch (e) { // already closed } } } function mkdirIfNotExistSync(path) { try { (0, _fs.accessSync)(path); } catch (e) { (0, _fs.mkdirSync)(path); } } function sendDgram(socket, data) { for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { args[_key2 - 2] = arguments[_key2]; } socket.send.apply(socket, [data, 0, data.length].concat(args)); } function writeOrPause(fromCon, toCon, data) { var res = toCon.write(data); if (!res) { fromCon.pause(); } return res; } function parseDstInfo(data, offset) { var atyp = data[offset]; var dstAddr = void 0; var dstPort = void 0; var dstAddrLength = void 0; var dstPortIndex = void 0; var dstPortEnd = void 0; // length of non-data field var totalLength = void 0; switch (atyp) { case 0x01: dstAddrLength = 4; dstAddr = data.slice(offset + 1, offset + 5); dstPort = data.slice(offset + 5, offset + 7); totalLength = offset + 7; break; case 0x04: dstAddrLength = 16; dstAddr = data.slice(offset + 1, offset + 17); dstPort = data.slice(offset + 17, offset + 19); totalLength = offset + 19; break; case 0x03: dstAddrLength = data[offset + 1]; dstPortIndex = 2 + offset + dstAddrLength; dstAddr = data.slice(offset + 2, dstPortIndex); dstPortEnd = dstPortIndex + 2; dstPort = data.slice(dstPortIndex, dstPortEnd); totalLength = dstPortEnd; break; default: return null; } if (data.length < totalLength) { return null; } return { atyp: atyp, dstAddrLength: dstAddrLength, dstAddr: dstAddr, dstPort: dstPort, totalLength: totalLength }; } function getDstInfo(data, isServer) { // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // Yet shadowsocks begin with ATYP. var offset = isServer ? 0 : 3; return parseDstInfo(data, offset); } function getDstInfoFromUDPMsg(data, isServer) { // +----+------+------+----------+----------+----------+ // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | // +----+------+------+----------+----------+----------+ // | 2 | 1 | 1 | Variable | 2 | Variable | // +----+------+------+----------+----------+----------+ var offset = isServer ? 0 : 3; return parseDstInfo(data, offset); } var formatKeyValues = { server: 'serverAddr', server_port: 'serverPort', local_addr: 'localAddr', local_port: 'localPort', local_addr_ipv6: 'localAddrIPv6', server_addr_ipv6: 'serverAddrIPv6' }; function formatConfig(_config) { var formattedConfig = Object.assign({}, _config); Object.keys(formatKeyValues).forEach(function (key) { if (hasOwnProperty.call(formattedConfig, key)) { formattedConfig[formatKeyValues[key]] = formattedConfig[key]; delete formattedConfig[key]; } }); return formattedConfig; } function getDstStr(dstInfo) { if (!dstInfo) { return null; } switch (dstInfo.atyp) { case 1: case 4: return _ip2.default.toString(dstInfo.dstAddr) + ':' + dstInfo.dstPort.readUInt16BE(); case 3: return dstInfo.dstAddr.toString('utf8') + ':' + dstInfo.dstPort.readUInt16BE(); default: return 'WARN: invalid atyp'; } }