telnet-client
Advanced tools
Comparing version 0.2.2 to 0.3.0
// Node.js Telnet client | ||
// TODO: * login (username/password) | ||
// * raise and emit errors | ||
var util = require('util'); | ||
var events = require('events'); | ||
var net = require('net'); | ||
var socket = new net.Socket(); | ||
var events = require('events') | ||
var net = require('net') | ||
var Promise = require('bluebird') | ||
var socket = new net.Socket() | ||
var util = require('util') | ||
@@ -13,177 +12,199 @@ | ||
function Telnet() { | ||
events.EventEmitter.call(this); | ||
if (false === (this instanceof Telnet)) return new Telnet(); | ||
events.EventEmitter.call(this) | ||
if (false === (this instanceof Telnet)) return new Telnet() | ||
} | ||
util.inherits(Telnet, events.EventEmitter); | ||
util.inherits(Telnet, events.EventEmitter) | ||
Telnet.prototype.connect = function(opts) { | ||
var self = this; | ||
var host = (typeof opts.host !== 'undefined' ? opts.host : '127.0.0.1'); | ||
var port = (typeof opts.port !== 'undefined' ? opts.port : 23); | ||
this.timeout = (typeof opts.timeout !== 'undefined' ? opts.timeout : 500); | ||
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.failedLoginPrompt = (typeof opts.failedLoginPrompt !== 'undefined' | ||
? opts.failedLoginPrompt : undefined); | ||
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.pageSeparator = (typeof opts.pageSeparator !== 'undefined' | ||
? opts.pageSeparator : '---- More'); | ||
this.response = ''; | ||
this.telnetState; | ||
var self = this | ||
this.telnetSocket = net.createConnection({ | ||
port: port, | ||
host: host | ||
}, function() { | ||
self.telnetState = 'start'; | ||
self.stringData = ''; | ||
self.emit('connect'); | ||
}); | ||
return new Promise(function(resolve, reject) { | ||
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) | ||
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.failedLoginPrompt = (typeof opts.failedLoginPrompt !== 'undefined' | ||
? opts.failedLoginPrompt : undefined) | ||
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.pageSeparator = (typeof opts.pageSeparator !== 'undefined' | ||
? opts.pageSeparator : '---- More') | ||
self.response = '' | ||
self.telnetState | ||
this.telnetSocket.setTimeout(this.timeout, function() { | ||
if (self.telnetSocket._connecting === true) { | ||
// info: cannot connect; emit error and destroy | ||
self.emit('error', 'Cannot connect'); | ||
self.telnetSocket = net.createConnection({ | ||
port: port, | ||
host: host | ||
}, function() { | ||
self.telnetState = 'start' | ||
self.stringData = '' | ||
self.emit('connect') | ||
}) | ||
self.telnetSocket.destroy(); | ||
} | ||
else self.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', function(data) { | ||
parseData(data, self); | ||
}); | ||
self.telnetSocket.on('data', function(data) { | ||
parseData(data, self, function(event, parsed) { | ||
if (event === 'ready') { | ||
resolve(parsed) | ||
} | ||
}) | ||
}) | ||
this.telnetSocket.on('error', function(error) { | ||
self.emit('error', error); | ||
}); | ||
self.telnetSocket.on('error', function(error) { | ||
self.emit('error', error) | ||
reject(error) | ||
}) | ||
this.telnetSocket.on('end', function() { | ||
self.emit('end'); | ||
}); | ||
self.telnetSocket.on('end', function() { | ||
self.emit('end') | ||
}) | ||
this.telnetSocket.on('close', function() { | ||
self.emit('close'); | ||
}); | ||
self.telnetSocket.on('close', function() { | ||
self.emit('close') | ||
reject('connection closed') | ||
}) | ||
}) | ||
} | ||
Telnet.prototype.exec = function(cmd, opts, callback) { | ||
var self = this; | ||
cmd += this.ors; | ||
var self = this | ||
cmd += this.ors | ||
if (opts && opts instanceof Function) callback = opts | ||
if (opts && opts instanceof Function) callback = opts; | ||
else if (opts && opts instanceof Object) { | ||
self.shellPrompt = opts.shellPrompt || self.shellPrompt; | ||
self.loginPrompt = opts.loginPrompt || self.loginPrompt; | ||
self.failedLoginPrompt = opts.failedLoginPrompt || self.failedLoginPrompt; | ||
self.timeout = opts.timeout || self.timeout; | ||
self.irs = opts.irs || self.irs; | ||
self.ors = opts.ors || self.ors; | ||
self.echoLines = opts.echoLines || self.echoLines; | ||
} | ||
return new Promise(function(resolve, reject) { | ||
if (opts && opts instanceof Object) { | ||
self.shellPrompt = opts.shellPrompt || self.shellPrompt | ||
self.loginPrompt = opts.loginPrompt || self.loginPrompt | ||
self.failedLoginPrompt = opts.failedLoginPrompt || self.failedLoginPrompt | ||
self.timeout = opts.timeout || self.timeout | ||
self.irs = opts.irs || self.irs | ||
self.ors = opts.ors || self.ors | ||
self.echoLines = opts.echoLines || self.echoLines | ||
} | ||
if (this.telnetSocket.writable) { | ||
this.telnetSocket.write(cmd, function() { | ||
self.telnetState = 'response'; | ||
self.emit('writedone'); | ||
if (self.telnetSocket.writable) { | ||
self.telnetSocket.write(cmd, function() { | ||
self.telnetState = 'response' | ||
self.emit('writedone') | ||
self.once('responseready', function() { | ||
if (callback && self.cmdOutput !== 'undefined') { | ||
callback(self.cmdOutput.join('\n')); | ||
} | ||
else if (callback && self.cmdOutput === 'undefined') callback; | ||
self.once('responseready', function() { | ||
if (self.cmdOutput !== 'undefined') { | ||
resolve(self.cmdOutput.join('\n')) | ||
} | ||
else reject(new Error('invalid response')) | ||
// reset stored response | ||
self.stringData = ''; | ||
}); | ||
}); | ||
} | ||
// reset stored response | ||
self.stringData = '' | ||
}) | ||
}) | ||
} | ||
}).asCallback(callback) | ||
} | ||
Telnet.prototype.end = function() { | ||
this.telnetSocket.end(); | ||
var self = this | ||
return new Promise(function(resolve) { | ||
self.telnetSocket.end() | ||
resolve() | ||
}) | ||
} | ||
Telnet.prototype.destroy = function() { | ||
this.telnetSocket.destroy(); | ||
var self = this | ||
return new Promise(function(resolve) { | ||
self.telnetSocket.destroy() | ||
resolve() | ||
}) | ||
} | ||
function parseData(chunk, telnetObj) { | ||
var promptIndex = ''; | ||
function parseData(chunk, telnetObj, callback) { | ||
var promptIndex = '' | ||
if (chunk[0] === 255 && chunk[1] !== 255) { | ||
telnetObj.stringData = ''; | ||
var negReturn = negotiate(telnetObj, chunk); | ||
telnetObj.stringData = '' | ||
var negReturn = negotiate(telnetObj.telnetSocket, chunk) | ||
if (negReturn == undefined) return; | ||
else chunk = negReturn; | ||
if (negReturn == undefined) return | ||
else chunk = negReturn | ||
} | ||
if (telnetObj.telnetState === 'start') { | ||
telnetObj.telnetState = 'getprompt'; | ||
telnetObj.telnetState = 'getprompt' | ||
} | ||
if (telnetObj.telnetState === 'getprompt') { | ||
var stringData = chunk.toString(); | ||
var promptIndex = stringData.search(regexEscape(telnetObj.shellPrompt)); | ||
var stringData = chunk.toString() | ||
var promptIndex = stringData.search(regexEscape(telnetObj.shellPrompt)) | ||
if (promptIndex !== -1) { | ||
telnetObj.shellPrompt = stringData.substring(promptIndex); | ||
telnetObj.telnetState = 'sendcmd'; | ||
telnetObj.stringData = ''; | ||
telnetObj.emit('ready', telnetObj.shellPrompt); | ||
telnetObj.shellPrompt = stringData.substring(promptIndex) | ||
telnetObj.telnetState = 'sendcmd' | ||
telnetObj.stringData = '' | ||
telnetObj.emit('ready', telnetObj.shellPrompt) | ||
if (callback) callback('ready', telnetObj.shellPrompt) | ||
} | ||
else if (stringData.search(regexEscape(telnetObj.loginPrompt)) !== -1) { | ||
telnetObj.telnetState = 'login'; | ||
login(telnetObj, 'username'); | ||
telnetObj.telnetState = 'login' | ||
login(telnetObj, 'username') | ||
} | ||
else if (stringData.search(regexEscape(telnetObj.passwordPrompt)) !== -1) { | ||
telnetObj.telnetState = 'login'; | ||
login(telnetObj, 'password'); | ||
telnetObj.telnetState = 'login' | ||
login(telnetObj, 'password') | ||
} | ||
else if (typeof telnetObj.failedLoginPrompt !== 'undefined' && stringData.search(telnetObj.failedLoginPrompt) !== -1) { | ||
telnetObj.telnetState = 'failedlogin'; | ||
telnetObj.emit('failedlogin', stringData); | ||
telnetObj.destroy(); | ||
telnetObj.telnetState = 'failedlogin' | ||
telnetObj.emit('failedlogin', stringData) | ||
telnetObj.destroy() | ||
} | ||
else return; | ||
else return | ||
} | ||
else if (telnetObj.telnetState === 'response') { | ||
var stringData = chunk.toString(); | ||
telnetObj.stringData += stringData; | ||
promptIndex = stringData.search(regexEscape(telnetObj.shellPrompt)); | ||
var stringData = chunk.toString() | ||
telnetObj.stringData += stringData | ||
promptIndex = stringData.search(regexEscape(telnetObj.shellPrompt)) | ||
if (promptIndex === -1 && stringData.length !== 0) { | ||
if (stringData.search(regexEscape(telnetObj.pageSeparator)) !== -1) { | ||
telnetObj.telnetSocket.write(Buffer('20', 'hex')); | ||
telnetObj.telnetSocket.write(Buffer('20', 'hex')) | ||
} | ||
return; | ||
return | ||
} | ||
telnetObj.cmdOutput = telnetObj.stringData.split(telnetObj.irs); | ||
telnetObj.cmdOutput = telnetObj.stringData.split(telnetObj.irs) | ||
for (var i = 0; i < telnetObj.cmdOutput.length; i++) { | ||
if (telnetObj.cmdOutput[i].search(regexEscape(telnetObj.pageSeparator)) !== -1) { | ||
telnetObj.cmdOutput[i] = telnetObj.cmdOutput[i].replace(telnetObj.pageSeparator, ''); | ||
if (telnetObj.cmdOutput[i].length === 0) telnetObj.cmdOutput.splice(i, 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); | ||
if (telnetObj.echoLines === 1) telnetObj.cmdOutput.shift() | ||
else if (telnetObj.echoLines > 1) telnetObj.cmdOutput.splice(0, telnetObj.echoLines) | ||
// remove prompt | ||
telnetObj.cmdOutput.pop(); | ||
telnetObj.cmdOutput.pop() | ||
telnetObj.emit('responseready'); | ||
telnetObj.emit('responseready') | ||
} | ||
@@ -195,34 +216,33 @@ } | ||
telnetObj.telnetSocket.write(telnetObj[handle] + telnetObj.ors, function() { | ||
telnetObj.telnetState = 'getprompt'; | ||
}); | ||
telnetObj.telnetState = 'getprompt' | ||
}) | ||
} | ||
} | ||
function negotiate(telnetObj, chunk) { | ||
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; | ||
// 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; | ||
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 (telnetObj.telnetSocket.writable) | ||
telnetObj.telnetSocket.write(Buffer(negResp, 'hex')); | ||
if (socket.writable) socket.write(Buffer(negResp, 'hex')) | ||
if (cmdData != undefined) return cmdData; | ||
else return; | ||
if (cmdData != undefined) return cmdData | ||
else return | ||
} | ||
function regexEscape(string) { | ||
return string.toString().replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
return string.toString().replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') | ||
} | ||
module.exports = Telnet; | ||
module.exports = Telnet |
@@ -8,6 +8,9 @@ { | ||
}, | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"main": "./lib/telnet-client.js", | ||
"engine": "node >= 0.10.23", | ||
"engine": "node >= 0.10.29", | ||
"license": "MIT", | ||
"dependencies": { | ||
"bluebird": "3.0.5" | ||
}, | ||
"devDependencies": { | ||
@@ -29,5 +32,5 @@ "coveralls": "^2.11.x", | ||
"name": "Petar Koretic", | ||
"email": "petar.koretic@sartura.hr" | ||
"email": "petar.koretic@gmail.com" | ||
} | ||
] | ||
} |
@@ -20,2 +20,3 @@ [![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/mkozjak/node-telnet-client/blob/master/LICENSE) | ||
## Example usage | ||
### Callback-style | ||
@@ -52,2 +53,60 @@ ```js | ||
### Promises | ||
```js | ||
var telnet = require('telnet-client'); | ||
var connection = new telnet(); | ||
var params = { | ||
host: '127.0.0.1', | ||
port: 23, | ||
shellPrompt: '/ # ', | ||
timeout: 1500, | ||
// removeEcho: 4 | ||
}; | ||
connection.connect(params) | ||
.then(function(prompt) { | ||
connection.exec(cmd) | ||
.then(function(res) { | ||
console.log('promises result:', res) | ||
}) | ||
}, function(error) { | ||
console.log('promises reject:', error) | ||
}); | ||
``` | ||
### Generators | ||
```js | ||
var co = require('co') | ||
var bluebird = require('bluebird') | ||
var telnet = require('telnet-client'); | ||
var connection = new telnet(); | ||
var params = { | ||
host: '127.0.0.1', | ||
port: 23, | ||
shellPrompt: '/ # ', | ||
timeout: 1500, | ||
// removeEcho: 4 | ||
}; | ||
// using 'co' | ||
co(function*() { | ||
yield connection.connect(params) | ||
let res = yield connection.exec(cmd) | ||
console.log('coroutine result:', res) | ||
}) | ||
// using 'bluebird' | ||
Promise.coroutine(function*() { | ||
yield connection.connect(params) | ||
let res = yield connection.exec(cmd) | ||
console.log('coroutine result:', res) | ||
})() | ||
``` | ||
## API | ||
@@ -60,3 +119,3 @@ | ||
### connection.connect(options) | ||
### connection.connect(options) -> Promise | ||
@@ -81,7 +140,9 @@ Creates a new TCP connection to the specified host, where 'options' is an object | ||
### connection.exec(data, [options], [callback]) | ||
### connection.exec(data, [options], [callback]) -> Promise | ||
Sends data on the socket (should be a compatible remote host's command if sane information is wanted). | ||
The optional callback parameter will be executed when the data is finally written out - this may not be immediately. | ||
Command result will be passed as the first argument to the callback. | ||
The optional callback parameter will be executed with an error and response when the command is finally written out and the response data has been received. | ||
_*** important notice/API change from 0.3.0 ***_ | ||
If there was no error when executing the command, 'error' as the first argument will be undefined. | ||
Command result will be passed as the second argument to the callback. | ||
@@ -99,7 +160,7 @@ Options: | ||
### connection.end() | ||
### connection.end() -> Promise | ||
Half-closes the socket. i.e., it sends a FIN packet. It is possible the server will still send some data. | ||
### connection.destroy() | ||
### connection.destroy() -> Promise | ||
@@ -106,0 +167,0 @@ Ensures that no more I/O activity happens on this socket. Only necessary in case of errors (parse error or so). |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18447
9
296
197
1
4
+ Addedbluebird@3.0.5
+ Addedbluebird@3.0.5(transitive)