telnet-client
Advanced tools
Comparing version 0.11.1 to 0.12.0
41
app.js
@@ -1,4 +0,37 @@ | ||
var t = require('./lib/index') | ||
var c = new t() | ||
var telnet = require('./lib/index') | ||
var telnet_server = require('telnet') | ||
var srv = telnet_server.createServer(function(c) { | ||
c.write(new Buffer("BusyBox v1.19.2 () built-in shell (ash)\n" | ||
+ "Enter 'help' for a list of built-in commands.\n\n/ # ", 'ascii')) | ||
c.on('data', function() { | ||
c.write(new Buffer("uptime\r\n23:14 up 1 day, 21:50, 6 users, " | ||
+ "load averages: 1.41 1.43 1.41\r\n", 'ascii')) | ||
c.write(new Buffer("/ # ", 'ascii')) | ||
}) | ||
}) | ||
srv.listen(2323, function() { | ||
var connection = new telnet() | ||
var params = { | ||
host: '127.0.0.1', | ||
port: 2323, | ||
shellPrompt: '/ # ', | ||
timeout: 1500 | ||
} | ||
connection.on('ready', function(prompt) { | ||
connection.exec('uptime', function(err, resp) { | ||
console.log(err, resp) | ||
connection.end() | ||
}) | ||
}) | ||
connection.connect(params); | ||
}) | ||
/* | ||
var params = { | ||
@@ -14,3 +47,2 @@ host: '127.0.0.1', | ||
/* | ||
c.on("connect", function() { | ||
@@ -26,3 +58,2 @@ console.log('sending data') | ||
}) | ||
*/ | ||
@@ -42,1 +73,3 @@ | ||
}) | ||
*/ | ||
466
lib/index.js
// 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') | ||
const events = require('events') | ||
const net = require('net') | ||
const Promise = require('bluebird') | ||
const utils = require('./utils') | ||
// define a constructor (object) and inherit EventEmitter functions | ||
function Telnet() { | ||
events.EventEmitter.call(this) | ||
if (false === (this instanceof Telnet)) return new Telnet() | ||
} | ||
module.exports = class Telnet extends events.EventEmitter { | ||
constructor() { | ||
super() | ||
util.inherits(Telnet, events.EventEmitter) | ||
this.telnetSocket = null | ||
this.telnetState = null | ||
} | ||
Telnet.prototype.connect = function(opts) { | ||
var self = this | ||
connect(opts) { | ||
return new Promise(resolve => { | ||
const host = (typeof opts.host !== 'undefined' ? opts.host : '127.0.0.1') | ||
const port = (typeof opts.port !== 'undefined' ? opts.port : 23) | ||
this.timeout = (typeof opts.timeout !== 'undefined' ? opts.timeout : 500) | ||
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 | ||
this.shellPrompt = (typeof opts.shellPrompt !== 'undefined' ? opts.shellPrompt : /(?:\/ )?#\s/) | ||
this.loginPrompt = (typeof opts.loginPrompt !== 'undefined' ? opts.loginPrompt : /login[: ]*$/i) | ||
this.passwordPrompt = (typeof opts.passwordPrompt !== 'undefined' ? opts.passwordPrompt : /Password: /i) | ||
this.failedLoginMatch = opts.failedLoginMatch | ||
// 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 | ||
this.debug = (typeof opts.debug !== 'undefined' ? opts.debug : false) | ||
this.username = (typeof opts.username !== 'undefined' ? opts.username : 'root') | ||
this.password = (typeof opts.password !== 'undefined' ? opts.password : 'guest') | ||
this.irs = (typeof opts.irs !== 'undefined' ? opts.irs : '\r\n') | ||
this.ors = (typeof opts.ors !== 'undefined' ? opts.ors : '\n') | ||
this.echoLines = (typeof opts.echoLines !== 'undefined' ? opts.echoLines : 1) | ||
this.stripShellPrompt = (typeof opts.stripShellPrompt !== 'undefined' ? opts.stripShellPrompt : true) | ||
this.pageSeparator = (typeof opts.pageSeparator !== 'undefined' | ||
? opts.pageSeparator : '---- More') | ||
this.negotiationMandatory = (typeof opts.negotiationMandatory !== 'undefined' | ||
? opts.negotiationMandatory : true) | ||
this.execTimeout = (typeof opts.execTimeout !== 'undefined' ? opts.execTimeout : 2000) | ||
this.sendTimeout = (typeof opts.sendTimeout !== 'undefined' ? opts.sendTimeout : 2000) | ||
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.execTimeout = (typeof opts.execTimeout !== 'undefined' ? opts.execTimeout : 2000) | ||
self.sendTimeout = (typeof opts.sendTimeout !== 'undefined' ? opts.sendTimeout : 2000) | ||
this.telnetSocket = net.createConnection({ | ||
port, | ||
host | ||
}, () => { | ||
this.telnetState = 'start' | ||
this.stringData = '' | ||
self.telnetState | ||
this.emit('connect') | ||
self.telnetSocket = net.createConnection({ | ||
port: port, | ||
host: host | ||
}, function() { | ||
self.telnetState = 'start' | ||
self.stringData = '' | ||
self.emit('connect') | ||
if (this.negotiationMandatory === false) resolve() | ||
}) | ||
if (self.negotiationMandatory === false) resolve() | ||
}) | ||
this.telnetSocket.setTimeout(this.timeout, () => { | ||
if (this.telnetSocket._connecting === true) { | ||
// info: if cannot connect, emit error and destroy | ||
this.emit('error', 'Cannot connect') | ||
this.telnetSocket.destroy() | ||
} | ||
else this.emit('timeout') | ||
}) | ||
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') | ||
}) | ||
this.telnetSocket.on('data', data => { | ||
if (this.telnetState === 'standby') | ||
return this.emit('data', data) | ||
self.telnetSocket.on('data', function(data) { | ||
if (self.telnetState === 'standby') | ||
return self.emit('data', data) | ||
this._parseData(data, (event, parsed) => { | ||
if (event === 'ready') { | ||
resolve(parsed) | ||
} | ||
}) | ||
}) | ||
parseData(data, self, function(event, parsed) { | ||
if (event === 'ready') { | ||
resolve(parsed) | ||
} | ||
this.telnetSocket.on('error', error => { | ||
this.emit('error', error) | ||
}) | ||
}) | ||
self.telnetSocket.on('error', function(error) { | ||
self.emit('error', error) | ||
}) | ||
this.telnetSocket.on('end', () => { | ||
this.emit('end') | ||
}) | ||
self.telnetSocket.on('end', function() { | ||
self.emit('end') | ||
this.telnetSocket.on('close', () => { | ||
this.emit('close') | ||
}) | ||
}) | ||
} | ||
self.telnetSocket.on('close', function() { | ||
self.emit('close') | ||
}) | ||
}) | ||
} | ||
exec(cmd, opts, callback) { | ||
if (opts && opts instanceof Function) callback = opts | ||
Telnet.prototype.exec = function(cmd, opts, callback) { | ||
var self = this | ||
return new Promise((resolve, reject) => { | ||
if (opts && opts instanceof Object) { | ||
this.shellPrompt = opts.shellPrompt || this.shellPrompt | ||
this.loginPrompt = opts.loginPrompt || this.loginPrompt | ||
this.failedLoginMatch = opts.failedLoginMatch || this.failedLoginMatch | ||
this.timeout = opts.timeout || this.timeout | ||
this.execTimeout = opts.execTimeout || this.execTimeout | ||
this.irs = opts.irs || this.irs | ||
this.ors = opts.ors || this.ors | ||
this.echoLines = opts.echoLines || this.echoLines | ||
} | ||
if (opts && opts instanceof Function) callback = opts | ||
cmd += this.ors | ||
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.execTimeout = opts.execTimeout || self.execTimeout | ||
self.irs = opts.irs || self.irs | ||
self.ors = opts.ors || self.ors | ||
self.echoLines = opts.echoLines || self.echoLines | ||
} | ||
if (!this.telnetSocket.writable) | ||
return reject(new Error('socket not writable')) | ||
cmd += self.ors | ||
this.telnetSocket.write(cmd, () => { | ||
let execTimeout = null | ||
this.telnetState = 'response' | ||
if (self.telnetSocket.writable) { | ||
self.telnetSocket.write(cmd, function() { | ||
var execTimeout = null; | ||
self.telnetState = 'response' | ||
self.emit('writedone') | ||
this.emit('writedone') | ||
self.once('responseready', function() { | ||
this.once('responseready', () => { | ||
if (execTimeout !== null) { | ||
clearTimeout(execTimeout); | ||
clearTimeout(execTimeout) | ||
} | ||
if (self.cmdOutput !== 'undefined') { | ||
resolve(self.cmdOutput.join('\n')) | ||
if (this.cmdOutput !== 'undefined') { | ||
resolve(this.cmdOutput.join('\n')) | ||
} | ||
@@ -128,194 +125,187 @@ else reject(new Error('invalid response')) | ||
// reset stored response | ||
self.stringData = '' | ||
this.stringData = '' | ||
// set state back to 'standby' for possible telnet server push data | ||
self.telnetState = 'standby' | ||
this.telnetState = 'standby' | ||
}) | ||
if (self.execTimeout) { | ||
execTimeout = setTimeout(function() { | ||
execTimeout = null; | ||
if (!self.cmdOutput) return reject(new Error('response not received')) | ||
}, self.execTimeout) | ||
if (this.execTimeout) { | ||
execTimeout = setTimeout(() => { | ||
execTimeout = null | ||
if (!this.cmdOutput) return reject(new Error('response not received')) | ||
}, this.execTimeout) | ||
} | ||
}) | ||
} | ||
}).asCallback(callback) | ||
} | ||
}).asCallback(callback) | ||
} | ||
Telnet.prototype.send = function(data, opts, callback) { | ||
var self = this | ||
send(data, opts, callback) { | ||
if (opts && opts instanceof Function) callback = opts | ||
if (opts && opts instanceof Function) callback = opts | ||
return new Promise((resolve, reject) => { | ||
if (opts && opts instanceof Object) { | ||
this.ors = opts.ors || this.ors | ||
this.sendTimeout = opts.timeout || this.sendTimeout | ||
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 | ||
} | ||
data += this.ors | ||
} | ||
if (this.telnetSocket.writable) { | ||
this.telnetSocket.write(data, () => { | ||
let response = '' | ||
this.telnetState = 'standby' | ||
if (self.telnetSocket.writable) { | ||
self.telnetSocket.write(data, function() { | ||
var response = '' | ||
self.telnetState = 'standby' | ||
this.on('data', data => { | ||
response += data.toString() | ||
self.on('data', function(data) { | ||
response += data.toString() | ||
if (opts && opts.waitfor !== undefined) { | ||
if (!response.includes(opts.waitfor)) return | ||
if (opts && opts.waitfor !== undefined) { | ||
if (response.indexOf(opts.waitfor) === -1) return | ||
resolve(response) | ||
} | ||
}) | ||
resolve(response) | ||
if ((opts && opts.waitfor === undefined) || !opts) { | ||
setTimeout(() => { | ||
if (response === '') return reject(new Error('response not received')) | ||
resolve(response) | ||
}, this.sendTimeout) | ||
} | ||
}) | ||
} | ||
}).asCallback(callback) | ||
} | ||
if ((opts && opts.waitfor === undefined) || !opts) { | ||
setTimeout(function() { | ||
if (response === '') return reject(new Error('response not received')) | ||
end() { | ||
return new Promise(resolve => { | ||
this.telnetSocket.end() | ||
resolve() | ||
}) | ||
} | ||
resolve(response) | ||
}, self.sendTimeout) | ||
} | ||
}) | ||
} | ||
}).asCallback(callback) | ||
} | ||
destroy() { | ||
return new Promise(resolve => { | ||
this.telnetSocket.destroy() | ||
resolve() | ||
}) | ||
} | ||
Telnet.prototype.end = function() { | ||
var self = this | ||
_parseData(chunk, callback) { | ||
var promptIndex = '' | ||
return new Promise(function(resolve) { | ||
self.telnetSocket.end() | ||
resolve() | ||
}) | ||
} | ||
if (chunk[0] === 255 && chunk[1] !== 255) { | ||
this.stringData = '' | ||
const negReturn = this._negotiate(chunk) | ||
Telnet.prototype.destroy = function() { | ||
var self = this | ||
if (negReturn == undefined) return | ||
else chunk = negReturn | ||
} | ||
return new Promise(function(resolve) { | ||
self.telnetSocket.destroy() | ||
resolve() | ||
}) | ||
} | ||
if (this.telnetState === 'start') { | ||
this.telnetState = 'getprompt' | ||
} | ||
function parseData(chunk, telnetObj, callback) { | ||
var promptIndex = '' | ||
if (this.telnetState === 'getprompt') { | ||
var stringData = chunk.toString() | ||
if (chunk[0] === 255 && chunk[1] !== 255) { | ||
telnetObj.stringData = '' | ||
var negReturn = negotiate(telnetObj.telnetSocket, chunk) | ||
var promptIndex = utils.search(stringData, this.shellPrompt) | ||
if (negReturn == undefined) return | ||
else chunk = negReturn | ||
} | ||
if (typeof this.failedLoginMatch !== 'undefined' && utils.search(stringData, this.failedLoginMatch) !== -1) { | ||
this.telnetState = 'failedlogin' | ||
if (telnetObj.telnetState === 'start') { | ||
telnetObj.telnetState = 'getprompt' | ||
} | ||
this.emit('failedlogin', stringData) | ||
this.destroy() | ||
} | ||
else if (promptIndex !== -1) { | ||
this.shellPrompt = stringData.substring(promptIndex) | ||
this.telnetState = 'standby' | ||
this.stringData = '' | ||
if (telnetObj.telnetState === 'getprompt') { | ||
var stringData = chunk.toString() | ||
this.emit('ready', this.shellPrompt) | ||
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() | ||
if (callback) callback('ready', this.shellPrompt) | ||
} | ||
else if (utils.search(stringData, this.loginPrompt) !== -1) { | ||
this.telnetState = 'login' | ||
this._login('username') | ||
} | ||
else if (utils.search(stringData, this.passwordPrompt) !== -1) { | ||
this.telnetState = 'login' | ||
this._login('password') | ||
} | ||
else return | ||
} | ||
else if (promptIndex !== -1) { | ||
telnetObj.shellPrompt = stringData.substring(promptIndex) | ||
telnetObj.telnetState = 'standby' | ||
telnetObj.stringData = '' | ||
telnetObj.emit('ready', telnetObj.shellPrompt) | ||
else if (this.telnetState === 'response') { | ||
var stringData = chunk.toString() | ||
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() | ||
this.stringData += stringData | ||
promptIndex = utils.search(this.stringData, this.shellPrompt) | ||
telnetObj.stringData += stringData | ||
promptIndex = search(telnetObj.stringData, telnetObj.shellPrompt) | ||
if (promptIndex === -1 && stringData.length !== 0) { | ||
if (utils.search(stringData, this.pageSeparator) !== -1) { | ||
this.telnetSocket.write(Buffer.from('20', 'hex')) | ||
} | ||
if (promptIndex === -1 && stringData.length !== 0) { | ||
if (search(stringData, telnetObj.pageSeparator) !== -1) { | ||
telnetObj.telnetSocket.write(Buffer.from('20', 'hex')) | ||
return | ||
} | ||
return | ||
} | ||
this.cmdOutput = this.stringData.split(this.irs) | ||
telnetObj.cmdOutput = telnetObj.stringData.split(telnetObj.irs) | ||
for (let i = 0; i < this.cmdOutput.length; i++) { | ||
if (utils.search(this.cmdOutput[i], this.pageSeparator) !== -1) { | ||
this.cmdOutput[i] = this.cmdOutput[i].replace(this.pageSeparator, '') | ||
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 (this.cmdOutput[i].length === 0) this.cmdOutput.splice(i, 1) | ||
} | ||
} | ||
if (telnetObj.cmdOutput[i].length === 0) telnetObj.cmdOutput.splice(i, 1) | ||
if (this.echoLines === 1) this.cmdOutput.shift() | ||
else if (this.echoLines > 1) this.cmdOutput.splice(0, this.echoLines) | ||
// remove prompt | ||
if (this.stripShellPrompt) { | ||
this.cmdOutput.pop() | ||
// add a blank line so that command output | ||
// maintains the trailing new line | ||
this.cmdOutput.push('') | ||
} | ||
} | ||
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() | ||
// add a blank line so that command output | ||
// maintains the trailing new line | ||
telnetObj.cmdOutput.push(''); | ||
this.emit('responseready') | ||
} | ||
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' | ||
}) | ||
_login(handle) { | ||
if ((handle === 'username' || handle === 'password') && this.telnetSocket.writable) { | ||
this.telnetSocket.write(this[handle] + this.ors, () => { | ||
this.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 | ||
_negotiate(chunk) { | ||
// info: http://tools.ietf.org/html/rfc1143#section-7 | ||
// refuse to start performing and ack the start of performance | ||
// DO -> WONT WILL -> DO | ||
const packetLength = chunk.length | ||
for (var i = 0; i < packetLength; i+=3) { | ||
if (chunk[i] != 255) { | ||
negData = chunk.slice(0, i) | ||
cmdData = chunk.slice(i) | ||
break | ||
let negData = chunk | ||
let cmdData = null | ||
let negResp = null | ||
for (let 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') | ||
negResp = negData.toString('hex').replace(/fd/g, 'fc').replace(/fb/g, 'fd') | ||
if (socket.writable) socket.write(Buffer.from(negResp, 'hex')) | ||
if (this.telnetSocket.writable) this.telnetSocket.write(Buffer.from(negResp, 'hex')) | ||
if (cmdData != undefined) return cmdData | ||
else return | ||
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 |
@@ -8,3 +8,3 @@ { | ||
}, | ||
"version": "0.11.1", | ||
"version": "0.12.0", | ||
"main": "./lib/index.js", | ||
@@ -11,0 +11,0 @@ "engine": "node >= 6.9.1", |
@@ -25,5 +25,7 @@ [![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/mkozjak/node-telnet-client/blob/master/LICENSE) | ||
(see [PR #63](https://github.com/mkozjak/node-telnet-client/pull/63)). | ||
Also, since version 0.12.x, we have moved to ECMAScript 6 to utilize class and scoping features. | ||
This means we have moved exclusively to Current and LTS Node.js releases. | ||
Should there be any problems with it, please check if the corresponding issue is opened, | ||
and if not, open the issue so we can reach out and try to solve the deprecation problem. | ||
and if not, open it so we can reach out and try to solve the deprecation problem. | ||
@@ -30,0 +32,0 @@ ## Usage example |
129855
13
953
267