diff --git a/lib/index.js b/lib/index.js index 0b362c9..57d56e6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -71,6 +71,7 @@ let argv = _yargs.default.command('$0', 'start nshd').command('addr', 'show addr }).help('help').alias('h', 'help').wrap(Math.min(120, _yargs.default.terminalWidth())).argv; const isWindows = _os.default.platform() === 'win32'; +const clientConnectTimeout = 20000; const pingInterval = 20000; const forcePingInterval = 60000; const ptyCols = 120; @@ -222,12 +223,6 @@ function getAuthorizedUser(src) { return null; } -const client = new _nknSdk.default.MultiClient({ - originalClient: true, - seed: wallet.getSeed(), - identifier: identifier -}); - class Session { constructor(client, remoteAddr, options) { this.client = client; @@ -286,10 +281,9 @@ class Session { } -let sessions = {}; -client.onConnect(() => { - console.log('Listening at', client.addr); +var sessions = {}; +function keepalive(client) { for (let c of Object.values(client.clients)) { setInterval(function () { try { @@ -371,112 +365,145 @@ client.onConnect(() => { }, 3000); }); }, forcePingInterval); -}); -client.onMessage(async ({ - src, - payload, - payloadType, - isEncrypted -}) => { - if (!isEncrypted) { - console.log('Received unencrypted msg from', src); - return false; - } +} - if (src.endsWith(client.getPublicKey()) && !payload) { - return; - } +(async () => { + let client; - let au = getAuthorizedUser(src); + while (true) { + try { + client = new _nknSdk.default.MultiClient({ + originalClient: true, + seed: wallet.getSeed(), + identifier: identifier + }); + } catch (e) { + console.error('Create client error:', e); + continue; + } - if (!au) { - console.log('Received msg from unauthorized sender', src); - return false; - } + try { + await new Promise((resolve, reject) => { + client.onConnect(resolve); + setTimeout(reject, clientConnectTimeout); + }); + } catch (e) { + console.error('Client connect timeout'); + client.close().catch(console.error); + continue; + } - if (payloadType !== _nknSdk.default.pb.payloads.PayloadType.TEXT) { - console.log('Received msg with wrong payload type from', src); - return false; + break; } - let msg = JSON.parse(payload); + console.log('Listening at', client.addr); + keepalive(client); + client.onMessage(async ({ + src, + payload, + payloadType, + isEncrypted + }) => { + if (!isEncrypted) { + console.log('Received unencrypted msg from', src); + return false; + } - if (msg.timestamp && Date.now() - Date.parse(msg.timestamp) > 60000) { - return false; - } + if (src.endsWith(client.getPublicKey()) && !payload) { + return; + } - let options = { - uid: au.uid, - gid: au.gid - }; + let au = getAuthorizedUser(src); - if (session && msg.resize) { - if (!sessions[src]) { - sessions[src] = new Session(client, src, options); + if (!au) { + console.log('Received msg from unauthorized sender', src); + return false; } - sessions[src].resize(msg.resize); - console.log('Resize to', msg.resize, 'from', src); - } + if (payloadType !== _nknSdk.default.pb.payloads.PayloadType.TEXT) { + console.log('Received msg with wrong payload type from', src); + return false; + } - let cmd = msg.cmd || msg.content; + let msg = JSON.parse(payload); - if (!cmd) { - return false; - } + if (msg.timestamp && Date.now() - Date.parse(msg.timestamp) > 60000) { + return false; + } + + let options = { + uid: au.uid, + gid: au.gid + }; + + if (session && msg.resize) { + if (!sessions[src]) { + sessions[src] = new Session(client, src, options); + } - console.log('Execute cmd' + (logCmd ? ' ' + cmd : ''), 'from', src); + sessions[src].resize(msg.resize); + console.log('Resize to', msg.resize, 'from', src); + } - if (msg.execSync) { - options.timeout = msg.execTimeout || syncExecTimeout; - let stdout, stderr; + let cmd = msg.cmd || msg.content; - try { - stdout = (0, _child_process.execSync)(cmd, options).toString(); - } catch (e) { - stderr = e.stderr ? e.stderr.toString() : e.error; + if (!cmd) { + return false; } - return JSON.stringify({ - stdout, - stderr - }); - } else { - options.timeout = msg.execTimeout || asyncExecTimeout; + console.log('Execute cmd' + (logCmd ? ' ' + cmd : ''), 'from', src); - if (session && !msg.content) { - if (!sessions[src]) { - sessions[src] = new Session(client, src, options); + if (msg.execSync) { + options.timeout = msg.execTimeout || syncExecTimeout; + let stdout, stderr; + + try { + stdout = (0, _child_process.execSync)(cmd, options).toString(); + } catch (e) { + stderr = e.stderr ? e.stderr.toString() : e.error; } - sessions[src].write(cmd); + return JSON.stringify({ + stdout, + stderr + }); } else { - (0, _child_process.exec)(cmd, options, async (error, stdout, stderr) => { - let res; - - if (msg.content) { - // d-chat protocol - res = { - content: "```\n" + (stdout || stderr) + "\n```", - contentType: "text", - timestamp: new Date().toUTCString(), - isPrivate: true - }; - } else { - res = { - stdout, - stderr - }; - } + options.timeout = msg.execTimeout || asyncExecTimeout; - try { - await client.send(src, JSON.stringify(res), { - noReply: true - }); - } catch (e) { - console.error("Send msg error:", e); + if (session && !msg.content) { + if (!sessions[src]) { + sessions[src] = new Session(client, src, options); } - }); + + sessions[src].write(cmd); + } else { + (0, _child_process.exec)(cmd, options, async (error, stdout, stderr) => { + let res; + + if (msg.content) { + // d-chat protocol + res = { + content: "```\n" + (stdout || stderr) + "\n```", + contentType: "text", + timestamp: new Date().toUTCString(), + isPrivate: true + }; + } else { + res = { + stdout, + stderr + }; + } + + try { + await client.send(src, JSON.stringify(res), { + noReply: true + }); + } catch (e) { + console.error("Send msg error:", e); + } + }); + } } - } -}); \ No newline at end of file + }); +})(); \ No newline at end of file diff --git a/package.json b/package.json index 50f2948..f6adc0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nkn-shell-daemon", - "version": "1.0.9", + "version": "1.1.0", "description": "NKN Shell Daemon", "main": "nshd.js", "bin": { @@ -28,7 +28,7 @@ }, "homepage": "https://nkn.org", "dependencies": { - "nkn-sdk": "^1.1.6", + "nkn-sdk": "^1.1.7", "node-pty": "^0.9.0", "yargs": "^14.2.0" }, diff --git a/src/index.js b/src/index.js index 72ab327..72e68d0 100644 --- a/src/index.js +++ b/src/index.js @@ -77,6 +77,7 @@ let argv = yargs .argv const isWindows = os.platform() === 'win32' +const clientConnectTimeout = 20000 const pingInterval = 20000 const forcePingInterval = 60000 const ptyCols = 120 @@ -205,12 +206,6 @@ function getAuthorizedUser(src) { return null } -const client = new nkn.MultiClient({ - originalClient: true, - seed: wallet.getSeed(), - identifier: identifier, -}) - class Session { constructor(client, remoteAddr, options) { this.client = client @@ -267,11 +262,9 @@ class Session { } } -let sessions = {} - -client.onConnect(() => { - console.log('Listening at', client.addr) +var sessions = {} +function keepalive(client) { for (let c of Object.values(client.clients)) { setInterval(function () { try { @@ -344,96 +337,129 @@ client.onConnect(() => { }, 3000); }) }, forcePingInterval) -}) +} -client.onMessage(async ({ src, payload, payloadType, isEncrypted }) => { - if (!isEncrypted) { - console.log('Received unencrypted msg from', src) - return false - } +(async () => { + let client + while (true) { + try { + client = new nkn.MultiClient({ + originalClient: true, + seed: wallet.getSeed(), + identifier: identifier, + }) + } catch (e) { + console.error('Create client error:', e); + continue + } - if (src.endsWith(client.getPublicKey()) && !payload) { - return - } + try { + await new Promise((resolve, reject) => { + client.onConnect(resolve) + setTimeout(reject, clientConnectTimeout); + }) + } catch (e) { + console.error('Client connect timeout') + client.close().catch(console.error) + continue + } - let au = getAuthorizedUser(src) - if (!au) { - console.log('Received msg from unauthorized sender', src) - return false + break } - if (payloadType !== nkn.pb.payloads.PayloadType.TEXT) { - console.log('Received msg with wrong payload type from', src) - return false - } + console.log('Listening at', client.addr) - let msg = JSON.parse(payload) + keepalive(client) - if (msg.timestamp && (Date.now() - Date.parse(msg.timestamp)) > 60000) { - return false - } + client.onMessage(async ({ src, payload, payloadType, isEncrypted }) => { + if (!isEncrypted) { + console.log('Received unencrypted msg from', src) + return false + } - let options = { - uid: au.uid, - gid: au.gid, - } + if (src.endsWith(client.getPublicKey()) && !payload) { + return + } - if (session && msg.resize) { - if (!sessions[src]) { - sessions[src] = new Session(client, src, options) + let au = getAuthorizedUser(src) + if (!au) { + console.log('Received msg from unauthorized sender', src) + return false } - sessions[src].resize(msg.resize) - console.log('Resize to', msg.resize, 'from', src) - } - let cmd = msg.cmd || msg.content - if (!cmd) { - return false - } + if (payloadType !== nkn.pb.payloads.PayloadType.TEXT) { + console.log('Received msg with wrong payload type from', src) + return false + } - console.log('Execute cmd' + (logCmd ? ' ' + cmd : ''), 'from', src) + let msg = JSON.parse(payload) - if (msg.execSync) { - options.timeout = msg.execTimeout || syncExecTimeout - let stdout, stderr - try { - stdout = execSync(cmd, options).toString() - } catch (e) { - stderr = e.stderr ? e.stderr.toString() : e.error + if (msg.timestamp && (Date.now() - Date.parse(msg.timestamp)) > 60000) { + return false } - return JSON.stringify({ - stdout, - stderr, - }) - } else { - options.timeout = msg.execTimeout || asyncExecTimeout - if (session && !msg.content) { + + let options = { + uid: au.uid, + gid: au.gid, + } + + if (session && msg.resize) { if (!sessions[src]) { sessions[src] = new Session(client, src, options) } - sessions[src].write(cmd) + sessions[src].resize(msg.resize) + console.log('Resize to', msg.resize, 'from', src) + } + + let cmd = msg.cmd || msg.content + if (!cmd) { + return false + } + + console.log('Execute cmd' + (logCmd ? ' ' + cmd : ''), 'from', src) + + if (msg.execSync) { + options.timeout = msg.execTimeout || syncExecTimeout + let stdout, stderr + try { + stdout = execSync(cmd, options).toString() + } catch (e) { + stderr = e.stderr ? e.stderr.toString() : e.error + } + return JSON.stringify({ + stdout, + stderr, + }) } else { - exec(cmd, options, async (error, stdout, stderr) => { - let res; - if (msg.content) { // d-chat protocol - res = { - content: "```\n" + (stdout || stderr) + "\n```", - contentType: "text", - timestamp: new Date().toUTCString(), - isPrivate: true, + options.timeout = msg.execTimeout || asyncExecTimeout + if (session && !msg.content) { + if (!sessions[src]) { + sessions[src] = new Session(client, src, options) + } + sessions[src].write(cmd) + } else { + exec(cmd, options, async (error, stdout, stderr) => { + let res; + if (msg.content) { // d-chat protocol + res = { + content: "```\n" + (stdout || stderr) + "\n```", + contentType: "text", + timestamp: new Date().toUTCString(), + isPrivate: true, + } + } else { + res = { + stdout, + stderr, + } } - } else { - res = { - stdout, - stderr, + try { + await client.send(src, JSON.stringify(res), { noReply: true }) + } catch (e) { + console.error("Send msg error:", e) } - } - try { - await client.send(src, JSON.stringify(res), { noReply: true }) - } catch (e) { - console.error("Send msg error:", e) - } - }); + }); + } } - } -}) + }) +})() diff --git a/yarn.lock b/yarn.lock index 489e035..f736d5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1875,10 +1875,10 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" -nkn-sdk@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/nkn-sdk/-/nkn-sdk-1.1.6.tgz#2735c8217df080175bf3fdca3c8fda2594828b97" - integrity sha512-LYiUZCWFite5hRlVSShOiZUTcvIt6O+YXT0XddHhuJqESh6YFaXR9Gs7tsr70JXp8GX8Us+/CVTyomIs0p+z1Q== +nkn-sdk@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/nkn-sdk/-/nkn-sdk-1.1.7.tgz#866c85f54212ec0a2a41d6ff48c24a241ef619b2" + integrity sha512-1hoqtk+uopfA6KdVX7jCsMLzj3Tz/U1d25OpK/iIqnEWprPrZOL5b/AKzfomp7sf1DBBmwT12uFI0BBK9yU2xw== dependencies: "@nkn/ncp" "^1.0.6" axios "^0.19.2"