{"version":3,"sources":["jsdelivr-header.js","/npm/shadowsocks-over-websocket@0.1.8/tcprelay.js"],"names":[],"mappings":"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","file":"/npm/shadowsocks-over-websocket@0.1.8/tcprelay.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/shadowsocks-over-websocket@0.1.8/tcprelay.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","const net = require('net');\nconst path = require('path');\nconst log4js = require('log4js');\nconst WebSocket = require('ws');\nconst Encryptor = require('shadowsocks/lib/shadowsocks/encrypt').Encryptor;\nconst WSErrorCode = require('ws/lib/ErrorCodes');\n\nconst MAX_CONNECTIONS = 50000;\n\nconst TCP_RELAY_TYPE_LOCAL = 1;\nconst TCP_RELAY_TYPE_SERVER = 2;\n\nconst ADDRESS_TYPE_IPV4 = 0x01;\nconst ADDRESS_TYPE_DOMAIN_NAME = 0x03;\nconst ADDRESS_TYPE_IPV6 = 0x04;\nconst ADDRESS_TYPE = {\n\t1: 'IPV4',\n\t3: 'DOMAIN_NAME',\n\t4: 'IPV6'\n};\n\nconst VERSION = 0x05;\n\nconst METHOD_NO_AUTHENTICATION_REQUIRED = 0x00;\nconst METHOD_GSSAPI = 0x01;\nconst METHOD_USERNAME_PASSWORD = 0x02;\nconst METHOD_NO_ACCEPTABLE_METHODS = 0xff;\n\nconst CMD_CONNECT = 0x01;\nconst CMD_BIND = 0x02;\nconst CMD_UDP_ASSOCIATE = 0x03;\nconst CMD = {\n\t1: 'CONNECT',\n\t2: 'BIND',\n\t3: 'UDP_ASSOCIATE'\n};\n\nconst REPLIE_SUCCEEDED = 0x00;\nconst REPLIE_GENERAL_SOCKS_SERVER_FAILURE = 0x01;\nconst REPLIE_CONNECTION_NOT_ALLOWED_BY_RULESET = 0x02;\nconst REPLIE_NETWORK_UNREACHABLE = 0x03;\nconst REPLIE_HOST_UNREACHABLE = 0x04;\nconst REPLIE_CONNECTION_REFUSED = 0x05;\nconst REPLIE_TTL_EXPIRED = 0x06;\nconst REPLIE_COMMAND_NOT_SUPPORTED = 0x07;\nconst REPLIE_ADDRESS_TYPE_NOT_SUPPORTED = 0x08;\n\nconst STAGE_INIT = 0;\nconst STAGE_ADDR = 1;\nconst STAGE_UDP_ASSOC = 2;\nconst STAGE_DNS = 3;\nconst STAGE_CONNECTING = 4;\nconst STAGE_STREAM = 5;\nconst STAGE_DESTROYED = -1;\n\nconst STAGE = {\n\t[-1]: 'STAGE_DESTROYED',\n\t0: 'STAGE_INIT',\n\t1: 'STAGE_ADDR',\n\t2: 'STAGE_UDP_ASSOC',\n\t3: 'STAGE_DNS',\n\t4: 'STAGE_CONNECTING',\n\t5: 'STAGE_STREAM'\n};\n\nconst SERVER_STATUS_INIT = 0;\nconst SERVER_STATUS_RUNNING = 1;\nconst SERVER_STATUS_STOPPED = 2;\n\nvar globalConnectionId = 1;\nvar connections = {};\n\nfunction parseAddressHeader(data, offset) {\n\tvar addressType = data.readUInt8(offset);\n\tvar headerLen, dstAddr, dstPort, dstAddrLen;\n\t//domain name\n\tif (addressType == ADDRESS_TYPE_DOMAIN_NAME) {\n\t\tdstAddrLen = data.readUInt8(offset + 1);\n\t\tdstAddr = data.slice(offset + 2, offset + 2 + dstAddrLen).toString();\n\t\tdstPort = data.readUInt16BE(offset + 2 + dstAddrLen);\n\t\theaderLen = 4 + dstAddrLen;\n\t}\n\t//ipv4\n\telse if (addressType == ADDRESS_TYPE_IPV4) {\n\t\tdstAddr = data.slice(offset + 1, offset + 5).join('.').toString();\n\t\tdstPort = data.readUInt16BE(offset + 5);\n\t\theaderLen = 7;\n\t} else {\n\t\treturn false;\n\t}\n\treturn {\n\t\taddressType: addressType,\n\t\theaderLen: headerLen,\n\t\tdstAddr: dstAddr,\n\t\tdstPort: dstPort\n\t};\n}\n\nfunction TCPRelay(config, isLocal) {\n\tthis.isLocal = isLocal;\n\tthis.server = null;\n\tthis.status = SERVER_STATUS_INIT;\n\tthis.config = require('./config.json');\n\tif (config) {\n\t\tthis.config = Object.assign(this.config, config);\n\t}\n\tthis.logger = null;\n\tthis.logLevel = 'error';\n\tthis.logFile = null;\n\tthis.serverName = null;\n}\n\nTCPRelay.prototype.getStatus = function() {\n\treturn this.status;\n};\n\nTCPRelay.prototype.setServerName = function(serverName) {\n\tthis.serverName = serverName;\n\treturn this;\n};\n\nTCPRelay.prototype.getServerName = function() {\n\tif (!this.serverName) {\n\t\tthis.serverName = this.isLocal ? 'local' : 'server';\n\t}\n\treturn this.serverName;\n};\n\nTCPRelay.prototype.setLogLevel = function(logLevel) {\n\tthis.logLevel = logLevel;\n\treturn this;\n};\n\nTCPRelay.prototype.getLogLevel = function() {\n\treturn this.logLevel;\n};\n\nTCPRelay.prototype.setLogFile = function(logFile) {\n\tif (logFile && !path.isAbsolute(logFile)) {\n\t\tlogFile = process.cwd() + '/' + logFile;\n\t}\n\tthis.logFile = logFile;\n\treturn this;\n};\n\nTCPRelay.prototype.getLogFile = function() {\n\treturn this.logFile;\n};\n\nTCPRelay.prototype.initLogger = function() {\n\tif (this.logFile) {\n\t\tlog4js.loadAppender('file');\n\t\tlog4js.addAppender(log4js.appenders.file(this.logFile), this.getServerName());\n\t}\n\tthis.logger = log4js.getLogger(this.getServerName());\n\tthis.logger.setLevel(this.logLevel);\n};\n\nTCPRelay.prototype.initServer = function() {\n\tvar self = this;\n\treturn new Promise(function(resolve, reject) {\n\t\tvar config = self.config;\n\t\tvar port = self.isLocal ? config.localPort : config.serverPort;\n\t\tvar address = self.isLocal ? config.localAddress : config.serverAddress;\n\t\tvar server;\n\n\t\tif (self.isLocal) {\n\t\t\tserver = self.server = net.createServer({\n\t\t\t\tallowHalfOpen: true,\n\t\t\t});\n\t\t\tserver.maxConnections = MAX_CONNECTIONS;\n\t\t\tserver.on('connection', function(connection) {\n\t\t\t\treturn self.handleConnectionByLocal(connection);\n\t\t\t});\n\t\t\tserver.on('close', function() {\n\t\t\t\tself.logger.info('server is closed');\n\t\t\t\tself.status = SERVER_STATUS_STOPPED;\n\t\t\t});\n\t\t\tserver.listen(port, address);\n\t\t} else {\n\t\t\tserver = self.server = new WebSocket.Server({\n\t\t\t\thost: address,\n\t\t\t\tport: port,\n\t\t\t\tperMessageDeflate: false,\n\t\t\t\tbacklog: MAX_CONNECTIONS\n\t\t\t});\n\t\t\tserver.on('connection', function(connection) {\n\t\t\t\treturn self.handleConnectionByServer(connection);\n\t\t\t});\n\t\t}\n\t\tserver.on('error', function(error) {\n\t\t\tself.logger.fatal('an error of', self.getServerName(), 'occured', error);\n\t\t\tself.status = SERVER_STATUS_STOPPED;\n\t\t\treject(error);\n\t\t});\n\t\tserver.on('listening', function() {\n\t\t\tself.logger.info(self.getServerName(), 'is listening on', address + ':' + port);\n\t\t\tself.status = SERVER_STATUS_RUNNING;\n\t\t\tresolve();\n\t\t});\n\t});\n};\n\n//server\nTCPRelay.prototype.handleConnectionByServer = function(connection) {\n\tvar self = this;\n\tvar config = self.config;\n\tvar method = config.method;\n\tvar password = config.password;\n\tvar serverAddress = config.serverAddress;\n\tvar serverPort = config.serverPort;\n\n\tvar logger = self.logger;\n\tvar encryptor = new Encryptor(password, method);\n\n\tvar stage = STAGE_INIT;\n\tvar connectionId = (globalConnectionId++) % MAX_CONNECTIONS;\n\tvar targetConnection, addressHeader;\n\n\tvar dataCache = [];\n\n\tlogger.info(`[${connectionId}]: accept connection from local`);\n\tconnections[connectionId] = connection;\n\tconnection.on('message', function(data) {\n\t\tdata = encryptor.decrypt(data);\n\t\tlogger.debug(`[${connectionId}]: read data[length = ${data.length}] from local connection at stage[${STAGE[stage]}]`);\n\n\t\tswitch (stage) {\n\n\t\t\tcase STAGE_INIT:\n\t\t\t\tif (data.length < 7) {\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\treturn connection.close();\n\t\t\t\t}\n\t\t\t\taddressHeader = parseAddressHeader(data, 0);\n\t\t\t\tif (!addressHeader) {\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\treturn connection.close();\n\t\t\t\t}\n\n\t\t\t\tlogger.info(`[${connectionId}]: connecting to ${addressHeader.dstAddr}:${addressHeader.dstPort}`);\n\t\t\t\tstage = STAGE_CONNECTING;\n\n\t\t\t\ttargetConnection = net.createConnection({\n\t\t\t\t\tport: addressHeader.dstPort,\n\t\t\t\t\thost: addressHeader.dstAddr,\n\t\t\t\t\tallowHalfOpen: true\n\t\t\t\t}, function() {\n\t\t\t\t\tlogger.info(`[${connectionId}]: connecting to target`);\n\n\t\t\t\t\tdataCache = Buffer.concat(dataCache);\n\t\t\t\t\ttargetConnection.write(dataCache, function() {\n\t\t\t\t\t\tlogger.debug(`[${connectionId}]: write data[length = ${dataCache.length}] to target connection`);\n\t\t\t\t\t\tdataCache = null;\n\t\t\t\t\t});\n\t\t\t\t\tstage = STAGE_STREAM;\n\t\t\t\t});\n\n\t\t\t\ttargetConnection.on('data', function(data) {\n\t\t\t\t\tlogger.debug(`[${connectionId}]: read data[length = ${data.length}] from target connection`);\n\t\t\t\t\tif (connection.readyState == WebSocket.OPEN) {\n\t\t\t\t\t\tconnection.send(encryptor.encrypt(data), {\n\t\t\t\t\t\t\tbinary: true\n\t\t\t\t\t\t}, function() {\n\t\t\t\t\t\t\tlogger.debug(`[${connectionId}]: write data[length = ${data.length}] to local connection`);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\ttargetConnection.on('end', function() {\n\t\t\t\t\tlogger.info(`[${connectionId}]: end event of target connection has been triggered`);\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\tconnection.close();\n\t\t\t\t});\n\t\t\t\ttargetConnection.on('close', function(hadError) {\n\t\t\t\t\tlogger.info(`[${connectionId}]: close event[had error = ${hadError}] of target connection has been triggered`);\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\tconnection.close();\n\t\t\t\t});\n\t\t\t\ttargetConnection.on('error', function(error) {\n\t\t\t\t\tlogger.error(`[${connectionId}]: an error of target connection occured`, error);\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\ttargetConnection.destroy();\n\t\t\t\t\tconnection.close();\n\t\t\t\t});\n\n\t\t\t\tif (data.length > addressHeader.headerLen) {\n\t\t\t\t\tdataCache.push(data.slice(addressHeader.headerLen));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase STAGE_CONNECTING:\n\t\t\t\tdataCache.push(data);\n\t\t\t\tbreak;\n\n\t\t\tcase STAGE_STREAM:\n\t\t\t\ttargetConnection.write(data, function() {\n\t\t\t\t\tlogger.debug(`[${connectionId}]: write data[length = ${data.length}] to target connection`);\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t}\n\t});\n\tconnection.on('close', function(code, reason) {\n\t\tlogger.info(`[${connectionId}]: close event[code = '${WSErrorCode[code]}'] of local connection has been triggered`);\n\t\tconnections[connectionId] = null;\n\t\ttargetConnection && targetConnection.destroy();\n\t});\n\tconnection.on('error', function(error) {\n\t\tlogger.error(`[${connectionId}]: an error of connection local occured`, error);\n\t\tconnection.terminate();\n\t\tconnections[connectionId] = null;\n\t\ttargetConnection && targetConnection.end();\n\t});\n};\n\n//local\nTCPRelay.prototype.handleConnectionByLocal = function(connection) {\n\tvar self = this;\n\tvar config = self.config;\n\tvar method = config.method;\n\tvar password = config.password;\n\tvar serverAddress = config.serverAddress;\n\tvar serverPort = config.serverPort;\n\n\tvar logger = self.logger;\n\tvar encryptor = new Encryptor(password, method);\n\n\tvar stage = STAGE_INIT;\n\tvar connectionId = (globalConnectionId++) % MAX_CONNECTIONS;\n\tvar serverConnection, cmd, addressHeader;\n\n\tvar canWriteToLocalConnection = true;\n\tvar dataCache = [];\n\n\tlogger.info(`[${connectionId}]: accept connection from client`);\n\tconnections[connectionId] = connection;\n\tconnection.setKeepAlive(false);\n\tconnection.on('data', function(data) {\n\t\tlogger.debug(`[${connectionId}]: read data[length = ${data.length}] from client connection at stage[${STAGE[stage]}]`);\n\t\tswitch (stage) {\n\n\t\t\tcase STAGE_INIT:\n\t\t\t\tif (data.length < 3 || data.readUInt8(0) != 5) {\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\treturn connection.end();\n\t\t\t\t}\n\t\t\t\tconnection.write(\"\\x05\\x00\");\n\t\t\t\tstage = STAGE_ADDR;\n\t\t\t\tbreak;\n\n\t\t\tcase STAGE_ADDR:\n\t\t\t\tif (data.length < 10 || data.readUInt8(0) != 5) {\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\treturn connection.end();\n\t\t\t\t}\n\t\t\t\tcmd = data.readUInt8(1);\n\t\t\t\taddressHeader = parseAddressHeader(data, 3);\n\t\t\t\tif (!addressHeader) {\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\treturn connection.end();\n\t\t\t\t}\n\n\t\t\t\t//only supports connect cmd\n\t\t\t\tif (cmd != CMD_CONNECT) {\n\t\t\t\t\tlogger.error('[${connectionId}]: only supports connect cmd');\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\treturn connection.end(\"\\x05\\x07\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\");\n\t\t\t\t}\n\n\t\t\t\tlogger.info(`[${connectionId}]: connecting to ${addressHeader.dstAddr}:${addressHeader.dstPort}`);\n\t\t\t\tconnection.write(\"\\x05\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\");\n\n\t\t\t\tstage = STAGE_CONNECTING;\n\n\t\t\t\tserverConnection = new WebSocket('ws://' + serverAddress + ':' + serverPort, {\n\t\t\t\t\tperMessageDeflate: false\n\t\t\t\t});\n\t\t\t\tserverConnection.on('open', function() {\n\t\t\t\t\tlogger.info(`[${connectionId}]: connecting to server`);\n\t\t\t\t\tserverConnection.send(encryptor.encrypt(data.slice(3)), function() {\n\t\t\t\t\t\tstage = STAGE_STREAM;\n\t\t\t\t\t\tdataCache = Buffer.concat(dataCache);\n\t\t\t\t\t\tserverConnection.send(encryptor.encrypt(dataCache), {\n\t\t\t\t\t\t\tbinary: true\n\t\t\t\t\t\t}, function() {\n\t\t\t\t\t\t\tlogger.debug(`[${connectionId}]: write data[length = ${dataCache.length}] to client connection`);\n\t\t\t\t\t\t\tdataCache = null;\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\tserverConnection.on('message', function(data) {\n\t\t\t\t\tlogger.debug(`[${connectionId}]: read data[length = ${data.length}] from server connection`);\n\t\t\t\t\tcanWriteToLocalConnection && connection.write(encryptor.decrypt(data), function() {\n\t\t\t\t\t\tlogger.debug(`[${connectionId}]: write data[length = ${data.length}] to client connection`);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\tserverConnection.on('error', function(error) {\n\t\t\t\t\tlogger.error(`[${connectionId}]: an error of server connection occured`, error);\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\tconnection.end();\n\t\t\t\t});\n\t\t\t\tserverConnection.on('close', function(code, reason) {\n\t\t\t\t\tlogger.info(`[${connectionId}]: close event[code = '${WSErrorCode[code]}'] of server connection has been triggered`);\n\t\t\t\t\tstage = STAGE_DESTROYED;\n\t\t\t\t\tconnection.end();\n\t\t\t\t});\n\n\t\t\t\tif (data.length > addressHeader.headerLen + 3) {\n\t\t\t\t\tdataCache.push(data.slice(addressHeader.headerLen + 3));\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase STAGE_CONNECTING:\n\t\t\t\tdataCache.push(data);\n\t\t\t\tbreak;\n\n\t\t\tcase STAGE_STREAM:\n\t\t\t\tcanWriteToLocalConnection && serverConnection.send(encryptor.encrypt(data), {\n\t\t\t\t\tbinary: true\n\t\t\t\t}, function() {\n\t\t\t\t\tlogger.debug(`[${connectionId}]: write data[length = ${data.length}] to server connection`);\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t}\n\t});\n\tconnection.on('end', function() {\n\t\tlogger.info(`[${connectionId}]: end event of client connection has been triggered`);\n\t\tstage = STAGE_DESTROYED;\n\t});\n\tconnection.on('close', function(hadError) {\n\t\tlogger.info(`[${connectionId}]: close event[had error = ${hadError}] of client connection has been triggered`);\n\t\tstage = STAGE_DESTROYED;\n\t\tcanWriteToLocalConnection = false;\n\t\tconnections[connectionId] = null;\n\t\tserverConnection && serverConnection.terminate();\n\t});\n\tconnection.on('error', function(error) {\n\t\tlogger.error(`[${connectionId}]: an error of client connection occured`, error);\n\t\tstage = STAGE_DESTROYED;\n\t\tconnection.destroy();\n\t\tcanWriteToLocalConnection = false;\n\t\tconnections[connectionId] = null;\n\t\tserverConnection && serverConnection.close();\n\t});\n};\n\n\nTCPRelay.prototype.bootstrap = function() {\n\tthis.initLogger();\n\treturn this.initServer();\n};\n\nTCPRelay.prototype.stop = function() {\n\tvar self = this;\n\tvar connId = null;\n\treturn new Promise(function(resolve, reject) {\n\t\tif (self.server) {\n\t\t\tself.server.close(function() {\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\tfor (connId in connections) {\n\t\t\t\tif (connections[connId]) {\n\t\t\t\t\tself.isLocal ? connections[connId].destroy() : connections[connId].terminate();\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\t\t\tresolve();\n\t\t}\n\t});\n};\n\nmodule.exports.TCPRelay = TCPRelay;"]}