UNPKG

domotz-remote-pawn

Version:

Domotz Agent

303 lines (239 loc) 9.18 kB
// Node.js Telnet client var events = require('events') var net = require('net') var Promise = require('bluebird') var socket = new net.Socket() var util = require('util') // define a constructor (object) and inherit EventEmitter functions function Telnet() { events.EventEmitter.call(this) if (false === (this instanceof Telnet)) return new Telnet() } util.inherits(Telnet, events.EventEmitter) Telnet.prototype.connect = function(opts) { var self = this return new Promise(function(resolve) { var host = (typeof opts.host !== 'undefined' ? opts.host : '127.0.0.1') var port = (typeof opts.port !== 'undefined' ? opts.port : 23) self.timeout = (typeof opts.timeout !== 'undefined' ? opts.timeout : 500) // Set prompt regex defaults self.shellPrompt = (typeof opts.shellPrompt !== 'undefined' ? opts.shellPrompt : /(?:\/ )?#\s/) self.loginPrompt = (typeof opts.loginPrompt !== 'undefined' ? opts.loginPrompt : /login[: ]*$/i) self.passwordPrompt = (typeof opts.passwordPrompt !== 'undefined' ? opts.passwordPrompt : /Password: /i) self.failedLoginMatch = opts.failedLoginMatch self.debug = (typeof opts.debug !== 'undefined' ? opts.debug : false) self.username = (typeof opts.username !== 'undefined' ? opts.username : 'root') self.password = (typeof opts.password !== 'undefined' ? opts.password : 'guest') self.irs = (typeof opts.irs !== 'undefined' ? opts.irs : '\r\n') self.ors = (typeof opts.ors !== 'undefined' ? opts.ors : '\n') self.echoLines = (typeof opts.echoLines !== 'undefined' ? opts.echoLines : 1) self.stripShellPrompt = (typeof opts.stripShellPrompt !== 'undefined' ? opts.stripShellPrompt : true) self.pageSeparator = (typeof opts.pageSeparator !== 'undefined' ? opts.pageSeparator : '---- More') self.negotiationMandatory = (typeof opts.negotiationMandatory !== 'undefined' ? opts.negotiationMandatory : true) self.sendTimeout = (typeof opts.sendTimeout !== 'undefined' ? opts.sendTimeout : 2000) self.response = '' self.telnetState self.telnetSocket = net.createConnection({ port: port, host: host }, function() { self.telnetState = 'start' self.stringData = '' self.emit('connect') if (self.negotiationMandatory === false) resolve() }) self.telnetSocket.setTimeout(self.timeout, function() { if (self.telnetSocket._connecting === true) { // info: if cannot connect, emit error and destroy self.emit('error', 'Cannot connect') self.telnetSocket.destroy() } else self.emit('timeout') }) self.telnetSocket.on('data', function(data) { if (self.telnetState === 'standby') return self.emit('data', data) parseData(data, self, function(event, parsed) { if (event === 'ready') { resolve(parsed) } }) }) self.telnetSocket.on('error', function(error) { self.emit('error', error) }) self.telnetSocket.on('end', function() { self.emit('end') }) self.telnetSocket.on('close', function() { self.emit('close') }) }) } Telnet.prototype.exec = function(cmd, opts, callback) { var self = this if (opts && opts instanceof Function) callback = opts return new Promise(function(resolve, reject) { if (opts && opts instanceof Object) { self.shellPrompt = opts.shellPrompt || self.shellPrompt self.loginPrompt = opts.loginPrompt || self.loginPrompt self.failedLoginMatch = opts.failedLoginMatch || self.failedLoginMatch self.timeout = opts.timeout || self.timeout self.irs = opts.irs || self.irs self.ors = opts.ors || self.ors self.echoLines = opts.echoLines || self.echoLines } cmd += self.ors if (self.telnetSocket.writable) { self.telnetSocket.write(cmd, function() { self.telnetState = 'response' self.emit('writedone') self.once('responseready', function() { if (self.cmdOutput !== 'undefined') { resolve(self.cmdOutput.join('\n')) } else reject(new Error('invalid response')) // reset stored response self.stringData = '' // set state back to 'standby' for possible telnet server push data self.telnetState = 'standby' }) }) } }).asCallback(callback) } Telnet.prototype.send = function(data, opts, callback) { var self = this if (opts && opts instanceof Function) callback = opts return new Promise(function(resolve, reject) { if (opts && opts instanceof Object) { this.ors = opts.ors || self.ors self.sendTimeout = opts.timeout || self.sendTimeout } data += this.ors if (self.telnetSocket.writable) { self.telnetSocket.write(data, function() { var response = '' self.telnetState = 'standby' self.on('data', function(data) { response += data.toString() if (opts.waitfor !== undefined) { if (response.indexOf(opts.waitfor) === -1) return resolve(response) } }) if (opts.waitfor === undefined) { setTimeout(function() { if (response === '') return reject(new Error('response not received')) resolve(response) }, self.sendTimeout) } }) } }).asCallback(callback) } Telnet.prototype.end = function() { var self = this return new Promise(function(resolve) { self.telnetSocket.end() resolve() }) } Telnet.prototype.destroy = function() { var self = this return new Promise(function(resolve) { self.telnetSocket.destroy() resolve() }) } function parseData(chunk, telnetObj, callback) { var promptIndex = '' if (chunk[0] === 255 && chunk[1] !== 255) { telnetObj.stringData = '' var negReturn = negotiate(telnetObj.telnetSocket, chunk) if (negReturn == undefined) return else chunk = negReturn } if (telnetObj.telnetState === 'start') { telnetObj.telnetState = 'getprompt' } if (telnetObj.telnetState === 'getprompt') { var stringData = chunk.toString() var promptIndex = search(stringData, telnetObj.shellPrompt) if (typeof telnetObj.failedLoginMatch !== 'undefined' && search(stringData, telnetObj.failedLoginMatch) !== -1) { telnetObj.telnetState = 'failedlogin' telnetObj.emit('failedlogin', stringData) telnetObj.destroy() } else if (promptIndex !== -1) { telnetObj.shellPrompt = stringData.substring(promptIndex) telnetObj.telnetState = 'standby' telnetObj.stringData = '' telnetObj.emit('ready', telnetObj.shellPrompt) if (callback) callback('ready', telnetObj.shellPrompt) } else if (search(stringData, telnetObj.loginPrompt) !== -1) { telnetObj.telnetState = 'login' login(telnetObj, 'username') } else if (search(stringData, telnetObj.passwordPrompt) !== -1) { telnetObj.telnetState = 'login' login(telnetObj, 'password') } else return } else if (telnetObj.telnetState === 'response') { var stringData = chunk.toString() telnetObj.stringData += stringData promptIndex = telnetObj.stringData.indexOf(telnetObj.shellPrompt) if (promptIndex === -1 && stringData.length !== 0) { if (search(stringData, telnetObj.pageSeparator) !== -1) { telnetObj.telnetSocket.write(Buffer('20', 'hex')) } return } telnetObj.cmdOutput = telnetObj.stringData.split(telnetObj.irs) for (var i = 0; i < telnetObj.cmdOutput.length; i++) { if (search(telnetObj.cmdOutput[i], telnetObj.pageSeparator) !== -1) { telnetObj.cmdOutput[i] = telnetObj.cmdOutput[i].replace(telnetObj.pageSeparator, '') if (telnetObj.cmdOutput[i].length === 0) telnetObj.cmdOutput.splice(i, 1) } } if (telnetObj.echoLines === 1) telnetObj.cmdOutput.shift() else if (telnetObj.echoLines > 1) telnetObj.cmdOutput.splice(0, telnetObj.echoLines) // remove prompt if (telnetObj.stripShellPrompt) telnetObj.cmdOutput.pop() telnetObj.emit('responseready') } } function login(telnetObj, handle) { if ((handle === 'username' || handle === 'password') && telnetObj.telnetSocket.writable) { telnetObj.telnetSocket.write(telnetObj[handle] + telnetObj.ors, function() { telnetObj.telnetState = 'getprompt' }) } } function negotiate(socket, chunk) { // info: http://tools.ietf.org/html/rfc1143#section-7 // refuse to start performing and ack the start of performance // DO -> WONT WILL -> DO var packetLength = chunk.length, negData = chunk, cmdData, negResp for (var i = 0; i < packetLength; i+=3) { if (chunk[i] != 255) { negData = chunk.slice(0, i) cmdData = chunk.slice(i) break } } negResp = negData.toString('hex').replace(/fd/g, 'fc').replace(/fb/g, 'fd') if (socket.writable) socket.write(Buffer(negResp, 'hex')) if (cmdData != undefined) return cmdData else return } function search(str, pattern){ if (pattern instanceof RegExp) return str.search(pattern) else return str.indexOf(pattern) } module.exports = Telnet