diff --git a/lib/socks5-server.js b/lib/socks5-server.js new file mode 100644 index 0000000..c088a91 --- /dev/null +++ b/lib/socks5-server.js @@ -0,0 +1,73 @@ +'use strict'; + +module.exports.create = function () { + var PromiseA = require('bluebird'); + var enableDestroy = require('server-destroy'); + var server; + + function curState() { + if (!server) { + return PromiseA.resolve({running: false}); + } + return PromiseA.resolve({ + running: true + , port: server.address().port + }); + } + + function start() { + if (server) { + return curState(); + } + + server = require('socksv5').createServer(function (info, accept) { + accept(); + }); + + enableDestroy(server); + server.on('close', function () { + server = null; + }); + + server.useAuth(require('socksv5').auth.None()); + + return new PromiseA(function (resolve, reject) { + server.on('error', function (err) { + if (err.code === 'EADDRINUSE') { + server.listen(0); + } else { + server = null; + reject(err); + } + }); + server.listen(1080, function () { + resolve(curState()); + }); + }); + } + + function stop() { + if (!server) { + return curState(); + } + return new PromiseA(function (resolve, reject) { + var timeoutId = setTimeout(function () { + server.destroy(); + }, 1000); + server.close(function (err) { + clearTimeout(timeoutId); + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } + + return { + isRunning: curState + , start: start + , stop: stop + }; +}; diff --git a/lib/worker.js b/lib/worker.js index 5fb9764..6826d12 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -38,6 +38,7 @@ function create(conf) { } } }; + deps.socks5 = require('./socks5-server').create(deps, conf); require('./goldilocks.js').create(deps, conf); process.removeListener('message', create); diff --git a/packages/apis/com.daplie.goldilocks/index.js b/packages/apis/com.daplie.goldilocks/index.js index 293fdaa..2091c6e 100644 --- a/packages/apis/com.daplie.goldilocks/index.js +++ b/packages/apis/com.daplie.goldilocks/index.js @@ -326,6 +326,32 @@ module.exports.create = function (deps, conf) { }); }); } + , socks5: function (req, res) { + if (handleCors(req, res, ['GET', 'POST', 'DELETE'])) { + return; + } + isAuthorized(req, res, function () { + var method = req.method.toUpperCase(); + var prom; + + if (method === 'POST') { + prom = deps.socks5.start(); + } else if (method === 'DELETE') { + prom = deps.socks5.stop(); + } else { + prom = deps.socks5.curState(); + } + + res.setHeader('Content-Type', 'application/json;'); + prom.then(function (result) { + res.end(JSON.stringify(result)); + }, function (err) { + err.message = err.message || err.toString(); + res.statusCode = 500; + res.end(JSON.stringify({error: {message: err.message, code: err.code}})); + }); + }); + } , _api: api }; };