telnet-client
Advanced tools
Comparing version 1.4.11 to 2.0.0-alpha.0
@@ -0,69 +1,87 @@ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { Stream } from 'stream'; | ||
import { Socket, SocketConnectOpts } from 'net'; | ||
declare interface ConnectOptions { | ||
host?: string; | ||
port?: number; | ||
localAddress?: string; | ||
socketConnectOptions?: SocketConnectOpts; | ||
timeout?: number; | ||
shellPrompt?: string|RegExp; | ||
loginPrompt?: string|RegExp; | ||
passwordPrompt?: string|RegExp; | ||
failedLoginMatch?: string|RegExp; | ||
initialLFCR?: boolean; | ||
username?: string; | ||
password?: string; | ||
sock?: Socket; | ||
irs?: string; | ||
ors?: string; | ||
import { Callback, Stream } from './utils'; | ||
export declare type TelnetState = null | 'end' | 'failedlogin' | 'getprompt' | 'login' | 'ready' | 'response' | 'standby' | 'start'; | ||
export declare type EscapeHandler = (escapeSequence: string) => string | null; | ||
export interface ExecOptions { | ||
echoLines?: number; | ||
pageSeparator?: string|RegExp; | ||
negotiationMandatory?: boolean; | ||
execTimeout?: number; | ||
sendTimeout?: number; | ||
sendTmaxBufferLengthimeout?: number; | ||
debug?: boolean; | ||
} | ||
declare interface ExecOptions { | ||
shellPrompt?: string; | ||
loginPrompt?: string; | ||
failedLoginMatch?: string; | ||
timeout?: number; | ||
execTimeout?: number; | ||
irs?: string; | ||
loginPrompt?: string; | ||
maxBufferLength?: number; | ||
newlineReplace?: string; | ||
ors?: string; | ||
echoLines?: number; | ||
shellPrompt?: string; | ||
stripControls?: boolean; | ||
timeout?: number; | ||
} | ||
export interface SendOptions { | ||
maxBufferLength?: number; | ||
} | ||
declare interface SendOptions { | ||
newlineReplace?: string; | ||
ors?: string; | ||
waitfor?: string|RegExp; | ||
shellPrompt?: string | RegExp; | ||
stripControls?: boolean; | ||
timeout?: number; | ||
maxBufferLength?: number; | ||
waitFor?: string | RegExp | false; | ||
/** @deprecated */ | ||
waitfor?: string | RegExp | false; | ||
} | ||
export default class telnet_client extends EventEmitter { | ||
export interface ConnectOptions extends SendOptions { | ||
debug?: boolean; | ||
echoLines?: number; | ||
encoding?: BufferEncoding; | ||
escapeHandler?: EscapeHandler; | ||
execTimeout?: number; | ||
extSock?: any; | ||
failedLoginMatch?: string | RegExp; | ||
host?: string; | ||
/** @deprecated */ | ||
initialCTRLC?: boolean; | ||
initialCtrlC?: boolean; | ||
initialLFCR?: boolean; | ||
irs?: string; | ||
localAddress?: string; | ||
loginPrompt?: string | RegExp; | ||
maxEndWait?: number; | ||
negotiationMandatory?: boolean; | ||
pageSeparator?: string | RegExp; | ||
password?: string; | ||
passwordPrompt?: string | RegExp; | ||
port?: number; | ||
sendTimeout?: number; | ||
sock?: Socket; | ||
socketConnectOptions?: SocketConnectOpts; | ||
stripShellPrompt?: boolean; | ||
terminalHeight?: number; | ||
terminalWidth?: number; | ||
username?: string; | ||
} | ||
export declare class Telnet extends EventEmitter { | ||
private dataResolver; | ||
private endEmitted; | ||
private inputBuffer; | ||
private loginPromptReceived; | ||
private opts; | ||
private pendingData; | ||
private response; | ||
private socket; | ||
private state; | ||
constructor(); | ||
connect(params: ConnectOptions): Promise<void>; | ||
private pushNextData; | ||
nextData(): Promise<string | null>; | ||
connect(opts: any): Promise<void>; | ||
shell(callback?: Callback<Stream>): Promise<Stream>; | ||
exec(cmd: string, opts?: ExecOptions | Callback<string>, callback?: Callback<string>): Promise<string>; | ||
send(data: Buffer | string, opts?: SendOptions | Callback<string>, callback?: Callback<string>): Promise<string>; | ||
write(data: Buffer | string, opts?: SendOptions, callback?: Callback<string>): Promise<string>; | ||
getSocket(): Socket; | ||
end(): Promise<void>; | ||
destroy(): Promise<void>; | ||
end(): Promise<void>; | ||
exec(cmd: string, options?: ExecOptions): Promise<string>; | ||
getSocket(): Socket; | ||
send(cmd: string, options?: SendOptions): Promise<string>; | ||
shell(): Promise<Stream>; | ||
public socket: Socket; | ||
public state: 'ready'|'start'|'standby'|'response'|'getprompt'|'login'|'failedlogin'|null; | ||
parseData(chunk: Buffer, isReady?: boolean[]): Buffer; | ||
private login; | ||
negotiate(chunk: Buffer): Buffer; | ||
private static checkSocket; | ||
private decode; | ||
} |
916
lib/index.js
@@ -1,449 +0,495 @@ | ||
'use strict' | ||
const events = require('events') | ||
const net = require('net') | ||
const Promise = require('bluebird') | ||
const Duplex = require('stream').Duplex | ||
const utils = require('./utils') | ||
module.exports = class Telnet extends events.EventEmitter { | ||
constructor() { | ||
super() | ||
this.socket = null | ||
this.state = null | ||
} | ||
connect(opts) { | ||
let promise | ||
return promise = new Promise((resolve, reject) => { | ||
const host = (typeof opts.host !== 'undefined' ? opts.host : '127.0.0.1') | ||
const port = (typeof opts.port !== 'undefined' ? opts.port : 23) | ||
const localAddress = (typeof opts.localAddress !== 'undefined' ? opts.localAddress : '') | ||
const socketConnectOptions = (typeof opts.socketConnectOptions !== 'undefined' ? opts.socketConnectOptions : {}) | ||
this.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 | ||
this.loginPromptReceived = false | ||
this.extSock = (typeof opts.sock !== 'undefined' ? opts.sock : undefined) | ||
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.initialLFCR = (typeof opts.initialLFCR !== 'undefined' ? opts.initialLFCR : false) | ||
this.initialCTRLC = (typeof opts.initialCTRLC !== 'undefined' ? opts.initialCTRLC : false) | ||
this.execTimeout = (typeof opts.execTimeout !== 'undefined' ? opts.execTimeout : 2000) | ||
this.sendTimeout = (typeof opts.sendTimeout !== 'undefined' ? opts.sendTimeout : 2000) | ||
this.maxBufferLength = (typeof opts.maxBufferLength !== 'undefined' ? opts.maxBufferLength : 1048576) | ||
/* if socket is provided and in good state, just reuse it */ | ||
if (this.extSock) { | ||
if (!this._checkSocket(this.extSock)) | ||
return reject(new Error('socket invalid')) | ||
this.socket = this.extSock | ||
this.state = 'ready' | ||
this.emit('ready') | ||
resolve(this.shellPrompt) | ||
} | ||
else { | ||
this.socket = net.createConnection({ | ||
port, | ||
host, | ||
localAddress, | ||
...socketConnectOptions | ||
}, () => { | ||
this.state = 'start' | ||
this.emit('connect') | ||
if (this.initialCTRLC === true) this.socket.write(Buffer.from('03', 'hex')) | ||
if (this.initialLFCR === true) this.socket.write('\r\n') | ||
if (this.negotiationMandatory === false) resolve() | ||
}) | ||
} | ||
this.inputBuffer = '' | ||
this.socket.setTimeout(this.timeout, () => { | ||
if (promise.isPending()) { | ||
/* if cannot connect, emit error and destroy */ | ||
if (this.listeners('error').length > 0) | ||
this.emit('error', 'Cannot connect') | ||
this.socket.destroy() | ||
return reject(new Error('Cannot connect')) | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Telnet = void 0; | ||
const events_1 = require("events"); | ||
const net_1 = require("net"); | ||
const utils_1 = require("./utils"); | ||
const defaultOptions = { | ||
debug: false, | ||
echoLines: 1, | ||
encoding: 'ascii', | ||
execTimeout: 2000, | ||
host: '127.0.0.1', | ||
initialCtrlC: false, | ||
initialLFCR: false, | ||
irs: '\r\n', | ||
localAddress: '', | ||
loginPrompt: /login[: ]*$/i, | ||
maxBufferLength: 1048576, | ||
maxEndWait: 250, | ||
negotiationMandatory: true, | ||
ors: '\n', | ||
pageSeparator: '---- More', | ||
password: 'guest', | ||
passwordPrompt: /password[: ]*$/i, | ||
port: 23, | ||
sendTimeout: 2000, | ||
shellPrompt: /(?:\/ )?#\s/, | ||
stripControls: false, | ||
stripShellPrompt: true, | ||
timeout: 2000, | ||
username: 'root', | ||
waitFor: false | ||
}; | ||
Object.freeze(defaultOptions); | ||
// Convert various options which can be provided as strings into regexes. | ||
function stringToRegex(opts) { | ||
['failedLoginMatch', 'loginPrompt', 'passwordPrompt', 'shellPrompt', 'waitFor'].forEach(key => { | ||
const value = opts[key]; | ||
opts[key] = typeof value === 'string' ? new RegExp(value) : value; | ||
}); | ||
} | ||
class Telnet extends events_1.EventEmitter { | ||
constructor() { | ||
super(); | ||
this.endEmitted = false; | ||
this.inputBuffer = ''; | ||
this.loginPromptReceived = false; | ||
this.opts = Object.assign({}, defaultOptions); | ||
this.pendingData = []; | ||
this.response = undefined; | ||
this.state = null; | ||
this.on('data', data => this.pushNextData(data)); | ||
this.on('end', () => { | ||
this.pushNextData(null); | ||
this.state = 'end'; | ||
}); | ||
} | ||
pushNextData(data) { | ||
if (data instanceof Buffer) | ||
data = data.toString(this.opts.encoding); | ||
else if (data != null) | ||
data = data.toString(); | ||
const chunks = data ? data.split(/(?<=\r\r\n|\r?\n)/) : [data]; | ||
if (this.dataResolver) { | ||
this.dataResolver(chunks[0]); | ||
this.dataResolver = undefined; | ||
} | ||
this.emit('timeout') | ||
return reject(new Error('timeout')) | ||
}) | ||
this.socket.on('data', data => { | ||
if (this.state === 'standby') | ||
return this.emit('data', data) | ||
this._parseData(data, (event, parsed) => { | ||
if (promise.isPending() && event === 'ready') { | ||
resolve(parsed) | ||
} | ||
}) | ||
}) | ||
this.socket.on('error', error => { | ||
if (this.listeners('error').length > 0) | ||
this.emit('error', error) | ||
if (promise.isPending()) | ||
reject(error) | ||
}) | ||
this.socket.on('end', () => { | ||
this.emit('end') | ||
if (promise.isPending()) | ||
reject(new Error('Socket ends')) | ||
}) | ||
this.socket.on('close', () => { | ||
this.emit('close') | ||
if (promise.isPending()) | ||
reject(new Error('Socket closes')) | ||
}) | ||
}) | ||
} | ||
shell(callback) { | ||
return new Promise((resolve, reject) => { | ||
resolve(new Stream(this.socket)) | ||
}).asCallback(callback) | ||
} | ||
exec(cmd, opts, callback) { | ||
if (opts && opts instanceof Function) callback = opts | ||
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 = (typeof opts.echoLines !== 'undefined' ? opts.echoLines : this.echoLines) | ||
this.maxBufferLength = opts.maxBufferLength || this.maxBufferLength | ||
} | ||
cmd += this.ors | ||
if (!this.socket.writable) | ||
return reject(new Error('socket not writable')) | ||
this.socket.write(cmd, () => { | ||
let execTimeout = null | ||
this.state = 'response' | ||
this.emit('writedone') | ||
this.once('responseready', responseHandler) | ||
this.once('bufferexceeded', buffExcHandler) | ||
if (this.execTimeout) { | ||
execTimeout = setTimeout(() => { | ||
execTimeout = null | ||
this.removeListener('responseready', responseHandler) | ||
this.removeListener('bufferexceeded', buffExcHandler) | ||
reject(new Error('response not received')) | ||
}, this.execTimeout) | ||
else | ||
this.pendingData.push(chunks[0]); | ||
if (chunks.length > 1) | ||
this.pendingData.push(...chunks.slice(1)); | ||
} | ||
nextData() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.pendingData.length > 0) | ||
return this.pendingData.splice(0, 1)[0]; | ||
else if (this.state === 'end') | ||
return null; | ||
return new Promise(resolve => this.dataResolver = resolve); | ||
}); | ||
} | ||
connect(opts) { | ||
return new Promise((resolve, reject) => { | ||
var _a; | ||
let connectionPending = true; | ||
const rejectIt = (reason) => { connectionPending = false; reject(reason); }; | ||
const resolveIt = () => { connectionPending = false; resolve(); }; | ||
Object.assign(this.opts, opts !== null && opts !== void 0 ? opts : {}); | ||
this.opts.initialCtrlC = opts.initialCtrlC && this.opts.initialCTRLC; | ||
this.opts.extSock = (_a = opts === null || opts === void 0 ? void 0 : opts.sock) !== null && _a !== void 0 ? _a : this.opts.extSock; | ||
stringToRegex(this.opts); | ||
// If socket is provided and in good state, just reuse it. | ||
if (this.opts.extSock) { | ||
if (!Telnet.checkSocket(this.opts.extSock)) | ||
return rejectIt(new Error('socket invalid')); | ||
this.socket = this.opts.extSock; | ||
this.state = 'ready'; | ||
this.emit('ready'); | ||
resolveIt(); | ||
} | ||
else { | ||
this.socket = (0, net_1.createConnection)(Object.assign({ port: this.opts.port, host: this.opts.host, localAddress: this.opts.localAddress }, this.opts.socketConnectOptions), () => { | ||
this.state = 'start'; | ||
this.emit('connect'); | ||
if (this.opts.initialCtrlC === true) | ||
this.socket.write('\x03'); | ||
if (this.opts.initialLFCR === true) | ||
this.socket.write('\r\n'); | ||
if (!this.opts.negotiationMandatory) | ||
resolveIt(); | ||
}); | ||
} | ||
this.socket.setMaxListeners(Math.max(15, this.socket.getMaxListeners())); | ||
this.socket.setTimeout(this.opts.timeout, () => { | ||
if (connectionPending) { | ||
// If cannot connect, emit error and destroy. | ||
if (this.listeners('error').length > 0) | ||
this.emit('error', 'Cannot connect'); | ||
this.socket.destroy(); | ||
return reject(new Error('Cannot connect')); | ||
} | ||
this.emit('timeout'); | ||
return reject(new Error('timeout')); | ||
}); | ||
this.socket.on('connect', () => { | ||
if (!this.opts.shellPrompt) { | ||
this.state = 'standby'; | ||
resolveIt(); | ||
} | ||
}); | ||
this.socket.on('data', data => { | ||
let emitted = false; | ||
if (this.state === 'standby' || !this.opts.negotiationMandatory) { | ||
this.emit('data', this.opts.newlineReplace ? Buffer.from(this.decode(data), this.opts.encoding) : data); | ||
emitted = true; | ||
} | ||
const isReady = []; | ||
if ((data = this.parseData(data, isReady)) && connectionPending && (isReady[0] || !this.opts.shellPrompt)) { | ||
resolveIt(); | ||
if (!this.opts.shellPrompt && !emitted) | ||
this.emit('data', data); | ||
} | ||
}); | ||
this.socket.on('error', error => { | ||
if (this.listeners('error').length > 0) | ||
this.emit('error', error); | ||
if (connectionPending) | ||
rejectIt(error); | ||
}); | ||
this.socket.on('end', () => { | ||
if (!this.endEmitted) { | ||
this.endEmitted = true; | ||
this.emit('end'); | ||
} | ||
if (connectionPending) { | ||
if (this.state === 'start') | ||
resolveIt(); | ||
else | ||
rejectIt(new Error('Socket ends')); | ||
} | ||
}); | ||
this.socket.on('close', () => { | ||
this.emit('close'); | ||
if (connectionPending) { | ||
if (this.state === 'start') | ||
resolveIt(); | ||
else | ||
rejectIt(new Error('Socket closes')); | ||
} | ||
}); | ||
this.once('failedlogin', () => { | ||
if (connectionPending) | ||
rejectIt(new Error('Failed login')); | ||
}); | ||
}); | ||
} | ||
shell(callback) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return (0, utils_1.asCallback)(new Promise(resolve => { | ||
resolve(new utils_1.Stream(this.socket)); | ||
}), callback); | ||
}); | ||
} | ||
exec(cmd, opts, callback) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (typeof opts === 'function') { | ||
callback = opts; | ||
opts = undefined; | ||
} | ||
return (0, utils_1.asCallback)(new Promise((resolve, reject) => { | ||
Object.assign(this.opts, opts || {}); | ||
cmd += this.opts.ors; | ||
if (!this.socket.writable) | ||
return reject(new Error('socket not writable')); | ||
this.socket.write(cmd, () => { | ||
let execTimeout; | ||
this.state = 'response'; | ||
this.emit('writedone'); | ||
const buffExecHandler = () => { | ||
if (execTimeout) | ||
clearTimeout(execTimeout); | ||
if (!this.inputBuffer) | ||
return reject(new Error('response not received')); | ||
resolve(this.inputBuffer); | ||
// Reset stored response. | ||
this.inputBuffer = ''; | ||
// Set state back to 'standby' for possible telnet server push data. | ||
this.state = 'standby'; | ||
}; | ||
const responseHandler = () => { | ||
if (execTimeout) | ||
clearTimeout(execTimeout); | ||
if (this.response) | ||
resolve(this.response.join(this.opts.newlineReplace || '\n')); | ||
else | ||
reject(new Error('invalid response')); | ||
// Reset stored response. | ||
this.inputBuffer = ''; | ||
// Set state back to 'standby' for possible telnet server push data. | ||
this.state = 'standby'; | ||
this.removeListener('bufferexceeded', buffExecHandler); | ||
}; | ||
this.once('responseready', responseHandler); | ||
this.once('bufferexceeded', buffExecHandler); | ||
if (this.opts.execTimeout) { | ||
execTimeout = setTimeout(() => { | ||
execTimeout = undefined; | ||
this.removeListener('responseready', responseHandler); | ||
this.removeListener('bufferexceeded', buffExecHandler); | ||
reject(new Error('response not received')); | ||
}, this.opts.execTimeout); | ||
} | ||
}); | ||
}), callback); | ||
}); | ||
} | ||
send(data, opts, callback) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (typeof opts === 'function') { | ||
callback = opts; | ||
opts = undefined; | ||
} | ||
this.opts.ors = ((_a = opts) === null || _a === void 0 ? void 0 : _a.ors) || this.opts.ors; | ||
data += this.opts.ors; | ||
return this.write(data, opts, callback); | ||
}); | ||
} | ||
write(data, opts, callback) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (typeof opts === 'function') { | ||
callback = opts; | ||
opts = undefined; | ||
} | ||
return (0, utils_1.asCallback)(new Promise((resolve, reject) => { | ||
var _a, _b; | ||
Object.assign(this.opts, opts || {}); | ||
this.opts.waitFor = (_b = (_a = opts === null || opts === void 0 ? void 0 : opts.waitFor) !== null && _a !== void 0 ? _a : opts === null || opts === void 0 ? void 0 : opts.waitfor) !== null && _b !== void 0 ? _b : false; | ||
stringToRegex(this.opts); | ||
if (this.socket.writable) { | ||
let response = ''; | ||
let sendTimer; | ||
const sendHandler = (data) => { | ||
response += this.decode(data); | ||
if (sendTimer) | ||
clearTimeout(sendTimer); | ||
if (this.opts.waitFor instanceof RegExp) { | ||
if (this.opts.waitFor.test(response)) { | ||
this.removeListener('data', sendHandler); | ||
resolve(response); | ||
} | ||
} | ||
else | ||
resolve(response); | ||
}; | ||
this.socket.on('data', sendHandler); | ||
try { | ||
this.socket.write(data, () => { | ||
if (!this.opts.waitFor || !opts) { | ||
sendTimer = setTimeout(() => { | ||
sendTimer = undefined; | ||
if (response === '') { | ||
this.socket.removeListener('data', sendHandler); | ||
reject(new Error('response not received')); | ||
return; | ||
} | ||
this.socket.removeListener('data', sendHandler); | ||
resolve(response); | ||
}, this.opts.sendTimeout); | ||
} | ||
}); | ||
} | ||
catch (e) { | ||
this.socket.removeListener('data', sendHandler); | ||
reject(new Error('send data failed')); | ||
} | ||
} | ||
else { | ||
reject(new Error('socket not writable')); | ||
} | ||
}), callback); | ||
}); | ||
} | ||
getSocket() { | ||
return this.socket; | ||
} | ||
end() { | ||
return new Promise(resolve => { | ||
let timer = setTimeout(() => { | ||
timer = undefined; | ||
if (!this.endEmitted) { | ||
this.endEmitted = true; | ||
this.emit('end'); | ||
} | ||
resolve(); | ||
}, this.opts.maxEndWait); | ||
this.socket.end(() => { | ||
if (timer) { | ||
clearTimeout(timer); | ||
timer = undefined; | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
destroy() { | ||
return new Promise(resolve => { | ||
this.socket.destroy(); | ||
resolve(); | ||
}); | ||
} | ||
parseData(chunk, isReady) { | ||
if (chunk[0] === 255 && chunk[1] !== 255) | ||
chunk = this.negotiate(chunk); | ||
if (this.state === 'start') | ||
this.state = 'getprompt'; | ||
if (this.state === 'getprompt') { | ||
const stringData = this.decode(chunk); | ||
const promptIndex = (0, utils_1.search)(stringData, this.opts.shellPrompt); | ||
if ((0, utils_1.search)(stringData, this.opts.loginPrompt) >= 0) { | ||
// Make sure we don't end up in an infinite loop. | ||
if (!this.loginPromptReceived) { | ||
this.state = 'login'; | ||
this.login('username'); | ||
this.loginPromptReceived = true; | ||
} | ||
} | ||
else if ((0, utils_1.search)(stringData, this.opts.passwordPrompt) >= 0) { | ||
this.state = 'login'; | ||
this.login('password'); | ||
} | ||
else if ((0, utils_1.search)(stringData, this.opts.failedLoginMatch) >= 0) { | ||
this.state = 'failedlogin'; | ||
this.emit('failedlogin', stringData); | ||
this.destroy().finally(); | ||
} | ||
else if (promptIndex >= 0) { | ||
const shellPrompt = this.opts.shellPrompt instanceof RegExp ? | ||
stringData.substring(promptIndex) : this.opts.shellPrompt; | ||
this.state = 'standby'; | ||
this.inputBuffer = ''; | ||
this.loginPromptReceived = false; | ||
this.emit('ready', shellPrompt); | ||
isReady === null || isReady === void 0 ? void 0 : isReady.push(true); | ||
} | ||
} | ||
function responseHandler() { | ||
if (execTimeout !== null) { | ||
clearTimeout(execTimeout) | ||
} | ||
if (this.response !== 'undefined') { | ||
resolve(this.response.join('\n')) | ||
} | ||
else reject(new Error('invalid response')) | ||
/* reset stored response */ | ||
this.inputBuffer = '' | ||
/* set state back to 'standby' for possible telnet server push data */ | ||
this.state = 'standby' | ||
this.removeListener('bufferexceeded', buffExcHandler) | ||
} | ||
function buffExcHandler() { | ||
if (execTimeout !== null) { | ||
clearTimeout(execTimeout) | ||
} | ||
if (!this.inputBuffer) return reject(new Error('response not received')) | ||
resolve(this.inputBuffer) | ||
/* reset stored response */ | ||
this.inputBuffer = '' | ||
/* set state back to 'standby' for possible telnet server push data */ | ||
this.state = 'standby' | ||
} | ||
}) | ||
}).asCallback(callback) | ||
} | ||
send(data, opts, callback) { | ||
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 | ||
this.maxBufferLength = opts.maxBufferLength || this.maxBufferLength | ||
this.waitfor = (opts.waitfor ? (opts.waitfor instanceof RegExp ? opts.waitfor : RegExp(opts.waitfor)) : false) | ||
} | ||
data += this.ors | ||
if (this.socket.writable) { | ||
this.on('data', sendHandler) | ||
let response = '' | ||
try | ||
{ | ||
this.socket.write(data, () => { | ||
this.state = 'standby' | ||
if (!this.waitfor || !opts) { | ||
setTimeout(() => { | ||
if (response === '') { | ||
this.removeListener('data', sendHandler) | ||
reject(new Error('response not received')) | ||
return | ||
else if (this.state === 'response') { | ||
if (this.inputBuffer.length >= this.opts.maxBufferLength) { | ||
this.emit('bufferexceeded'); | ||
return Buffer.from(this.inputBuffer, this.opts.encoding); | ||
} | ||
const stringData = this.decode(chunk); | ||
this.inputBuffer += stringData; | ||
const promptIndex = (0, utils_1.search)(this.inputBuffer, this.opts.shellPrompt); | ||
if (promptIndex < 0 && stringData.length > 0) { | ||
if ((0, utils_1.search)(stringData, this.opts.pageSeparator) >= 0) | ||
this.socket.write(Buffer.from('20', 'hex')); | ||
return null; | ||
} | ||
const response = this.inputBuffer.split(this.opts.irs); | ||
for (let i = response.length - 1; i >= 0; --i) { | ||
if ((0, utils_1.search)(response[i], this.opts.pageSeparator) >= 0) { | ||
response[i] = response[i].replace(this.opts.pageSeparator, ''); | ||
if (response[i].length === 0) | ||
response.splice(i, 1); | ||
} | ||
this.removeListener('data', sendHandler) | ||
resolve(response) | ||
}, this.sendTimeout) | ||
} | ||
}) | ||
if (this.opts.echoLines === 1) | ||
response.shift(); | ||
else if (this.opts.echoLines > 1) | ||
response.splice(0, this.opts.echoLines); | ||
else if (this.opts.echoLines < 0) | ||
response.splice(0, response.length - 2); | ||
// Remove prompt. | ||
if (this.opts.stripShellPrompt && response.length > 0) { | ||
const idx = response.length - 1; | ||
response[idx] = (0, utils_1.search)(response[idx], this.opts.shellPrompt) >= 0 | ||
? response[idx].replace(this.opts.shellPrompt, '') | ||
: ''; | ||
} | ||
this.response = response; | ||
chunk = null; | ||
this.emit('responseready'); | ||
} | ||
catch(e) | ||
{ | ||
this.removeListener('data', sendHandler) | ||
reject(new Error('send data failed')) | ||
} | ||
const self = this | ||
function sendHandler(data) { | ||
response += data.toString() | ||
if (self.waitfor) { | ||
if (!self.waitfor.test(response)) return | ||
self.removeListener('data', sendHandler) | ||
resolve(response) | ||
} | ||
} | ||
} else { | ||
reject(new Error('socket not writable')) | ||
} | ||
}).asCallback(callback) | ||
} | ||
getSocket() { | ||
return this.socket | ||
} | ||
end() { | ||
return new Promise(resolve => { | ||
this.socket.end() | ||
resolve() | ||
}) | ||
} | ||
destroy() { | ||
return new Promise(resolve => { | ||
this.socket.destroy() | ||
resolve() | ||
}) | ||
} | ||
_parseData(chunk, callback) { | ||
let promptIndex = '' | ||
if (chunk[0] === 255 && chunk[1] !== 255) { | ||
this.inputBuffer = '' | ||
const negReturn = this._negotiate(chunk) | ||
if (negReturn == undefined) return | ||
else chunk = negReturn | ||
return chunk; | ||
} | ||
if (this.state === 'start') { | ||
this.state = 'getprompt' | ||
} | ||
if (this.state === 'getprompt') { | ||
const stringData = chunk.toString() | ||
let promptIndex = utils.search(stringData, this.shellPrompt) | ||
if (utils.search(stringData, this.loginPrompt) !== -1) { | ||
/* make sure we don't end up in an infinite loop */ | ||
if (!this.loginPromptReceived) { | ||
this.state = 'login' | ||
this._login('username') | ||
this.loginPromptReceived = true | ||
login(handle) { | ||
if ((handle === 'username' || handle === 'password') && this.socket.writable) { | ||
this.socket.write(this.opts[handle] + this.opts.ors, () => { | ||
this.state = 'getprompt'; | ||
}); | ||
} | ||
} | ||
else if (utils.search(stringData, this.passwordPrompt) !== -1) { | ||
this.state = 'login' | ||
this._login('password') | ||
} | ||
else if (typeof this.failedLoginMatch !== 'undefined' && utils.search(stringData, this.failedLoginMatch) !== -1) { | ||
this.state = 'failedlogin' | ||
this.emit('failedlogin', stringData) | ||
this.destroy() | ||
} | ||
else if (promptIndex !== -1) { | ||
if (!(this.shellPrompt instanceof RegExp)) | ||
this.shellPrompt = stringData.substring(promptIndex) | ||
this.state = 'standby' | ||
this.inputBuffer = '' | ||
this.loginPromptReceived = false | ||
this.emit('ready', this.shellPrompt) | ||
if (callback) callback('ready', this.shellPrompt) | ||
} | ||
else return | ||
} | ||
else if (this.state === 'response') { | ||
if (this.inputBuffer.length >= this.maxBufferLength) { | ||
return this.emit('bufferexceeded') | ||
} | ||
const stringData = chunk.toString() | ||
this.inputBuffer += stringData | ||
promptIndex = utils.search(this.inputBuffer, this.shellPrompt) | ||
if (promptIndex === -1 && stringData.length !== 0) { | ||
if (utils.search(stringData, this.pageSeparator) !== -1) { | ||
this.socket.write(Buffer.from('20', 'hex')) | ||
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; | ||
let negData = chunk; | ||
let cmdData = null; | ||
for (let i = 0; i < packetLength; i += 3) { | ||
if (chunk[i] !== 255) { | ||
negData = chunk.slice(0, i); | ||
cmdData = chunk.slice(i); | ||
break; | ||
} | ||
} | ||
return | ||
} | ||
let response = this.inputBuffer.split(this.irs) | ||
for (let i = response.length - 1; i >= 0; --i) { | ||
if (utils.search(response[i], this.pageSeparator) !== -1) { | ||
response[i] = response[i].replace(this.pageSeparator, '') | ||
if (response[i].length === 0) | ||
response.splice(i, 1) | ||
const chunkHex = chunk.toString('hex'); | ||
const defaultResponse = negData.toString('hex').replace(/fd/g, 'fc').replace(/fb/g, 'fd'); | ||
let negResp = ''; | ||
if (this.opts.terminalHeight && this.opts.terminalWidth) { | ||
for (let i = 0; i < chunkHex.length; i += 6) { | ||
let w, h; | ||
switch (chunkHex.substr(i + 2, 4)) { | ||
case 'fd18': | ||
negResp += 'fffb18'; | ||
break; | ||
case 'fd1f': | ||
w = this.opts.terminalWidth.toString(16).padStart(4, '0'); | ||
h = this.opts.terminalHeight.toString(16).padStart(4, '0'); | ||
negResp += `fffb1ffffa1f${w}${h}fff0`; | ||
break; | ||
default: | ||
negResp += defaultResponse.substr(i, 6); | ||
} | ||
} | ||
} | ||
} | ||
if (this.echoLines === 1) response.shift() | ||
else if (this.echoLines > 1) response.splice(0, this.echoLines) | ||
else if (this.echoLines === -1) response.splice(0, response.length - 2) | ||
/* remove prompt */ | ||
if (this.stripShellPrompt) { | ||
const idx = response.length - 1; | ||
response[idx] = utils.search(response[idx], this.shellPrompt) > -1 | ||
? response[idx].replace(this.shellPrompt, '') | ||
: ''; | ||
} | ||
this.response = response; | ||
this.emit('responseready') | ||
else | ||
negResp = defaultResponse; | ||
if (this.socket.writable) | ||
this.socket.write(Buffer.from(negResp, 'hex')); | ||
return cmdData; | ||
} | ||
} | ||
_login(handle) { | ||
if ((handle === 'username' || handle === 'password') && this.socket.writable) { | ||
this.socket.write(this[handle] + this.ors, () => { | ||
this.state = 'getprompt' | ||
}) | ||
static checkSocket(sock) { | ||
return sock !== null && | ||
typeof sock === 'object' && | ||
typeof sock.pipe === 'function' && | ||
sock.writable !== false && | ||
typeof sock._write === 'function' && | ||
typeof sock._writableState === 'object' && | ||
sock.readable !== false && | ||
typeof sock._read === 'function' && | ||
typeof sock._readableState === 'object'; | ||
} | ||
} | ||
_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 | ||
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 | ||
} | ||
decode(chunk) { | ||
if (chunk instanceof Buffer) | ||
chunk = chunk.toString(this.opts.encoding); | ||
if (this.opts.escapeHandler) { | ||
chunk === null || chunk === void 0 ? void 0 : chunk.replace(/\x1B((\[.*?[a-z])|.)/i, seq => { | ||
const response = this.opts.escapeHandler(seq); | ||
if (response) | ||
this.socket.write(response); | ||
return seq; | ||
}); | ||
} | ||
if (this.opts.stripControls) { | ||
chunk = chunk === null || chunk === void 0 ? void 0 : chunk.replace(/\x1B((\[.*?[a-z])|.)/i, ''); // Escape sequences | ||
chunk = chunk === null || chunk === void 0 ? void 0 : chunk.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, ''); // All controls except tab, lf, and cr. | ||
} | ||
if (this.opts.newlineReplace) | ||
chunk = chunk === null || chunk === void 0 ? void 0 : chunk.replace(/\r\r\n|\r\n?/g, this.opts.newlineReplace); | ||
return chunk; | ||
} | ||
negResp = negData.toString('hex').replace(/fd/g, 'fc').replace(/fb/g, 'fd') | ||
if (this.socket.writable) this.socket.write(Buffer.from(negResp, 'hex')) | ||
if (cmdData != undefined) return cmdData | ||
else return | ||
} | ||
_checkSocket(sock) { | ||
return this.extSock !== null && | ||
typeof this.extSock === 'object' && | ||
typeof this.extSock.pipe === 'function' && | ||
this.extSock.writable !== false && | ||
typeof this.extSock._write === 'function' && | ||
typeof this.extSock._writableState === 'object' && | ||
this.extSock.readable !== false && | ||
typeof this.extSock._read === 'function' && | ||
typeof this.extSock._readableState === 'object' | ||
} | ||
} | ||
class Stream extends Duplex { | ||
constructor(source, options) { | ||
super(options) | ||
this.source = source | ||
this.source.on('data', (data) => this.push(data)) | ||
} | ||
_write(data, encoding, cb) { | ||
if (!this.source.writable) { | ||
cb(new Error('socket not writable')) | ||
} | ||
this.source.write(data, encoding, cb) | ||
} | ||
_read() {} | ||
} | ||
exports.Telnet = Telnet; | ||
//# sourceMappingURL=index.js.map |
@@ -1,4 +0,34 @@ | ||
module.exports.search = function(str, pattern) { | ||
if (pattern instanceof RegExp) return str.search(pattern) | ||
else return str.indexOf(pattern) | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Stream = exports.search = exports.asCallback = void 0; | ||
const stream_1 = require("stream"); | ||
function asCallback(promise, callback) { | ||
if (typeof callback === 'function') | ||
promise.then(result => callback(null, result)).catch(err => callback(err)); | ||
return promise; | ||
} | ||
exports.asCallback = asCallback; | ||
function search(str, pattern) { | ||
if (!str || !pattern) | ||
return -1; | ||
else if (pattern instanceof RegExp) | ||
return str.search(pattern); | ||
else | ||
return str.indexOf(pattern); | ||
} | ||
exports.search = search; | ||
class Stream extends stream_1.Duplex { | ||
constructor(source, options) { | ||
super(options); | ||
this.source = source; | ||
this.source.on('data', data => this.push(data)); | ||
} | ||
_write(data, encoding, callback) { | ||
if (!this.source.writable) | ||
callback(new Error('socket not writable')); | ||
this.source.write(data, encoding, callback); | ||
} | ||
_read() { } | ||
} | ||
exports.Stream = Stream; | ||
//# sourceMappingURL=utils.js.map |
@@ -8,3 +8,3 @@ { | ||
}, | ||
"version": "1.4.11", | ||
"version": "2.0.0-alpha.0", | ||
"main": "./lib/index.js", | ||
@@ -14,4 +14,9 @@ "types": "./lib/index.d.ts", | ||
"license": "MIT", | ||
"scripts": { | ||
"prepack": "tsc", | ||
"test": "nyc --reporter=html mocha --timeout 3000 --require ts-node/register test/**/*.ts", | ||
"coveralls": "tsc && (nyc report --reporter=text-lcov | coveralls)", | ||
"lint": "eslint 'src/**/*.ts' 'test/**/*.ts'" | ||
}, | ||
"dependencies": { | ||
"bluebird": "^3.5.4", | ||
"net": "^1.0.2", | ||
@@ -21,14 +26,26 @@ "stream": "^0.0.2" | ||
"devDependencies": { | ||
"coveralls": "^3.0.3", | ||
"@types/chai": "^4.3.0", | ||
"@types/mocha": "^9.0.0", | ||
"@types/node": "^17.0.5", | ||
"@typescript-eslint/eslint-plugin": "^5.7.0", | ||
"@typescript-eslint/parser": "^5.7.0", | ||
"chai": "^4.3.4", | ||
"coveralls": "^3.1.1", | ||
"eslint": "^8.5.0", | ||
"eslint-config-standard": "^16.0.3", | ||
"eslint-plugin-chai-friendly": "^0.7.2", | ||
"eslint-plugin-import": "^2.25.3", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-promise": "^6.0.0", | ||
"eslint-plugin-standard": "^5.0.0", | ||
"extend": "^3.0.2", | ||
"nodeunit": "^0.11.3", | ||
"telnet": "0.0.1" | ||
"mocha": "^9.1.3", | ||
"nyc": "^15.1.0", | ||
"telnet": "0.0.1", | ||
"ts-node": "^10.4.0", | ||
"typescript": "^4.5.4" | ||
}, | ||
"scripts": { | ||
"test": "nodeunit test", | ||
"coveralls": "jscoverage lib && NODETELNETCLIENT_COV=1 nodeunit --reporter=lcov test | coveralls" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/mkozjak/node-telnet-client.git" | ||
"url": "github:mkozjak/node-telnet-client.git" | ||
}, | ||
@@ -80,2 +97,6 @@ "contributors": [ | ||
"email": "ivan@sieder.xyz" | ||
}, | ||
{ | ||
"name": "Kerry Shetline", | ||
"email": "kerry@shetline.com" | ||
} | ||
@@ -82,0 +103,0 @@ ], |
159
README.md
[![GitHub license](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://github.com/mkozjak/node-telnet-client/blob/master/LICENSE) | ||
[![Build Status](https://travis-ci.org/mkozjak/node-telnet-client.svg?branch=master)](https://travis-ci.org/mkozjak/node-telnet-client) | ||
[![npm](https://img.shields.io/npm/dm/telnet-client.svg?maxAge=2592000)](https://www.npmjs.com/package/telnet-client) | ||
[![Donate Bitcoin/Altcoins](https://img.shields.io/badge/donate-coins-blue.svg)](https://paypal.me/kozjak) | ||
[![Donate Bitcoin/Altcoins](https://img.shields.io/badge/donate-coins-blue.svg)](https://paypal.me/kozjak) | ||
[![npm version](https://img.shields.io/npm/v/telnet-client.svg?style=flat)](https://www.npmjs.com/package/telnet-client) | ||
@@ -15,3 +15,3 @@ | ||
``` | ||
```bash | ||
npm install telnet-client | ||
@@ -22,14 +22,17 @@ npm install -g telnet-client | ||
## Quick start | ||
### Async/Await (Node.js >= 7.6.0) | ||
_Note: As of version 2.0.0 of this API, native ES6 promises are returned, not Bluebird promises._ | ||
```js | ||
'use strict' | ||
const Telnet = require('telnet-client') | ||
const { Telnet } = require('telnet-client') | ||
async function run() { | ||
let connection = new Telnet() | ||
(async function () { | ||
const connection = new Telnet() | ||
// these parameters are just examples and most probably won't work for your use-case. | ||
let params = { | ||
const params = { | ||
host: '127.0.0.1', | ||
@@ -43,11 +46,9 @@ port: 23, | ||
await connection.connect(params) | ||
} catch(error) { | ||
} catch (error) { | ||
// handle the throw (timeout) | ||
} | ||
let res = await connection.exec('uptime') | ||
const res = await connection.exec('uptime') | ||
console.log('async result:', res) | ||
} | ||
run() | ||
})() | ||
``` | ||
@@ -58,16 +59,15 @@ | ||
```js | ||
var Telnet = require('telnet-client') | ||
var connection = new Telnet() | ||
const { Telnet } = require('telnet-client') | ||
const connection = new Telnet() | ||
// these parameters are just examples and most probably won't work for your use-case. | ||
var params = { | ||
const params = { | ||
host: '127.0.0.1', | ||
port: 23, | ||
shellPrompt: '/ # ', // or negotiationMandatory: false | ||
timeout: 1500, | ||
// removeEcho: 4 | ||
timeout: 1500 | ||
} | ||
connection.on('ready', function(prompt) { | ||
connection.exec(cmd, function(err, response) { | ||
connection.on('ready', prompt => { | ||
connection.exec(cmd, (err, response) => { | ||
console.log(response) | ||
@@ -77,3 +77,3 @@ }) | ||
connection.on('timeout', function() { | ||
connection.on('timeout', () => { | ||
console.log('socket timeout!') | ||
@@ -83,3 +83,3 @@ connection.end() | ||
connection.on('close', function() { | ||
connection.on('close', () => { | ||
console.log('connection closed') | ||
@@ -93,27 +93,28 @@ }) | ||
_Note: As of version 2.0.0 of this API, native ES6 promises are returned, not Bluebird promises._ | ||
```js | ||
var Telnet = require('telnet-client') | ||
var connection = new Telnet() | ||
const { Telnet } = require('telnet-client') | ||
const connection = new Telnet() | ||
// these parameters are just examples and most probably won't work for your use-case. | ||
var params = { | ||
const params = { | ||
host: '127.0.0.1', | ||
port: 23, | ||
shellPrompt: '/ # ', // or negotiationMandatory: false | ||
timeout: 1500, | ||
// removeEcho: 4 | ||
timeout: 1500 | ||
} | ||
connection.connect(params) | ||
.then(function(prompt) { | ||
connection.exec(cmd) | ||
.then(function(res) { | ||
console.log('promises result:', res) | ||
.then(prompt => { | ||
connection.exec(cmd) | ||
.then(res => { | ||
console.log('promises result:', res) | ||
}) | ||
}, error => { | ||
console.log('promises reject:', error) | ||
}) | ||
}, function(error) { | ||
console.log('promises reject:', error) | ||
}) | ||
.catch(function(error) { | ||
// handle the throw (timeout) | ||
}) | ||
.catch(error => { | ||
// handle the throw (timeout) | ||
}) | ||
``` | ||
@@ -124,9 +125,9 @@ | ||
```js | ||
var co = require('co') | ||
var bluebird = require('bluebird') | ||
var Telnet = require('telnet-client') | ||
var connection = new Telnet() | ||
const co = require('co') | ||
const toBluebird = require("to-bluebird") | ||
const { Telnet } = require('telnet-client') | ||
const connection = new Telnet() | ||
// these parameters are just examples and most probably won't work for your use-case. | ||
var params = { | ||
const params = { | ||
host: '127.0.0.1', | ||
@@ -147,10 +148,10 @@ port: 23, | ||
let res = yield connection.exec(cmd) | ||
console.log('coroutine result:', res) | ||
const res = yield connection.exec(cmd) | ||
console.log('coroutine result:', const) | ||
}) | ||
// using 'bluebird' | ||
// using Promise | ||
bluebird.coroutine(function*() { | ||
try { | ||
yield connection.connect(params) | ||
yield toBluebird(connection.connect(params)) | ||
} catch (error) { | ||
@@ -160,3 +161,3 @@ // handle the throw (timeout) | ||
let res = yield connection.exec(cmd) | ||
let res = yield toBluebird(connection.exec(cmd)) | ||
console.log('coroutine result:', res) | ||
@@ -166,3 +167,3 @@ })() | ||
### Async/Await (using babeljs) | ||
### Async/Await | ||
@@ -172,13 +173,8 @@ ```js | ||
const Promise = require('bluebird') | ||
const telnet = require('telnet-client') | ||
const { Telnet } = require('telnet-client') | ||
require('babel-runtime/core-js/promise').default = Promise | ||
Promise.onPossiblyUnhandledRejection(function(error) { | ||
process.on('unhandledRejection', error => { | ||
throw error | ||
}) | ||
// also requires additional babeljs setup | ||
async function run() { | ||
@@ -219,4 +215,4 @@ let connection = new Telnet() | ||
```js | ||
var Telnet = require('telnet-client') | ||
var connection = new Telnet() | ||
const { Telnet } = require('telnet-client') | ||
const connection = new Telnet() | ||
``` | ||
@@ -235,7 +231,7 @@ | ||
of inactivity on the socket. | ||
* `shellPrompt`: Shell prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex '/(?:\/ )?#\s/'. Use `negotiationMandatory: false` if you don't need this. | ||
* `loginPrompt`: Username/login prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex '/login[: ]*$/i'. | ||
* `passwordPrompt`: Password/login prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex '/Password: /i'. | ||
* `shellPrompt`: Shell prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex `/(?:\/ )?#\s/`. Use `negotiationMandatory: false` if you don't need this.<br><br>Set `shellPrompt` to `null` if you wish to use the `send(…)` or `write(…)` methods, ignoring the returned values, and instead relying on `nextData()` or `on('data'…` for feedback. | ||
* `loginPrompt`: Username/login prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex `/login[: ]*$/i`. | ||
* `passwordPrompt`: Password/login prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex `/Password: /i`. | ||
* `failedLoginMatch`: String or regex to match if your host provides login failure messages. Defaults to undefined. | ||
* `initialCTRLC`: Flag used to determine if an initial 0x03 (CTRL+C) should be sent when connected to server. | ||
* `initialCtrlC`: Flag used to determine if an initial 0x03 (CTRL+C) should be sent when connected to server. | ||
* `initialLFCR`: Flag used to determine if an initial '\r\n' (CR+LF) should be sent when connected to server. | ||
@@ -247,3 +243,3 @@ * `username`: Username used to login. Defaults to 'root'. | ||
* `ors`: Output record separator. A separator used to execute commands (break lines on input). Defaults to '\n'. | ||
* `echoLines`: The number of lines used to cut off the response. Defaults to 1. | ||
* `echoLines`: The number of lines used to cut off the response. Defaults to 1. With a value of 0, no lines are cut off. | ||
* `stripShellPrompt`: Whether shell prompt should be excluded from the results. Defaults to true. | ||
@@ -256,3 +252,8 @@ * `pageSeparator`: The pattern used (and removed from final output) for breaking the number of lines on output. Defaults to '---- More'. | ||
* `maxBufferLength`: Maximum buffer length in bytes which can be filled with response data. Defaults to 1M. | ||
* `debug`: Enable/disable debug logs on console. Defaults to false. | ||
* `terminalWidth`, `terminalHeight`: When set to non-zero values, `telnet-client` will respond to the host command `IAC DO 0x18` (Terminal Type) with `IAC WILL 0x18`, and it will respond to `IAC DO 0x1F` with the given terminal width and height. | ||
* `newlineReplace`: If provided, incoming line breaks will be normalized to the provided character/string of characters. | ||
* `escapeHandler`: An optional function that receives escape sequences (either `'0x1B'` and the next non-`[` character, or `'0x1B['` followed by every subsequent character up to and including the first ASCII letter) from the host. The function can either return `null`, which means to take no action, or a string value to be sent to the host as a response. | ||
* `stripControls`: If set to `true`, escape sequences and control characters (except for `\t`, `\n`, and `\r`) will be stripped from incoming data. `escapeHandler` is not affected. | ||
* `maxEndWait`: The maximum time, in milliseconds, to wait for a callback from `socket.end(…)` after calling `end()`. Defaults to 250 milliseconds. | ||
* `encoding`: _(Experimental)_ The telnet protocol is designed mainly for 7-bit ASCII characters, and a default encoding used is `'ascii'`. You can attempt to use other encodings, however, such as `'utf8'` and `'latin1'`. Since the character values 0xF0-0xFF are used for telnet commands, not all characters for many encodings can be properly conveyed. `'utf8'` can work, however, for the roughly 64K characters in Unicode Basic Multilingual Plane (BMP). | ||
@@ -265,13 +266,16 @@ Resolves once the connection is ready (analogous to the ```ready``` event). | ||
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 with an error and response when the command is finally written out and the response data has been received. | ||
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. | ||
If there was no error when executing the command, 'error' as the first argument to the callback will be undefined. | ||
Command result will be passed as the second argument to the callback. | ||
__*** important notice/API change from 0.3.0 ***__ | ||
The callback argument is now called with a signature of (error, [response]) | ||
Command result will be passed as the second argument to the callback. | ||
__*** Important notice/API change from 0.3.0 ***__ | ||
The callback argument is now called with a signature of (error, [response]) | ||
Options: | ||
* `shellPrompt`: Shell prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex '/(?:\/ )?#\s/'. | ||
* `loginPrompt`: Username/login prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex '/login[: ]*$/i'. | ||
* `shellPrompt`: Shell prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex `/(?:\/ )?#\s/`. | ||
* `loginPrompt`: Username/login prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex `/login[: ]*$/i`. | ||
* `failedLoginMatch`: String or regex to match if your host provides login failure messages. Defaults to undefined. | ||
@@ -282,5 +286,5 @@ * `timeout`: Sets the socket to timeout after the specified number of milliseconds | ||
* `maxBufferLength`: Maximum buffer length in bytes which can be filled with response data. Defaults to 1M. | ||
* `irs`: Input record separator. A separator used to distinguish between lines of the response. Defaults to '\r\n'. | ||
* `ors`: Output record separator. A separator used to execute commands (break lines on input). Defaults to '\n'. | ||
* `echoLines`: The number of lines used to cut off the response. Defaults to 1. | ||
* `irs`: Input record separator. A separator used to distinguish between lines of the response. Defaults to `'\r\n'`. | ||
* `ors`: Output record separator. A separator used to execute commands (break lines on input). Defaults to `'\n'`. | ||
* `echoLines`: The number of lines used to cut off the response. Defaults to 1. With a value of 0, no lines are cut off. | ||
@@ -293,7 +297,16 @@ ### connection.send(data, [options], [callback]) -> Promise | ||
* `shellPrompt`: Shell prompt that the host is using. Can be a string or an instance of RegExp. Defaults to regex `/(?:\/ )?#\s/`. Use `negotiationMandatory: false` if you don't need this.<br><br>Set `shellPrompt` to `null` if you wish to use the `send(…)` or `write(…)` methods, ignoring the returned values, and instead relying on `nextData()` or `on('data'…` for feedback. | ||
* `ors`: Output record separator. A separator used to execute commands (break lines on input). Defaults to '\n'. | ||
* `waitfor`: Wait for the given string or RegExp before returning a response. If not defined, the timeout value will be used. | ||
* `waitFor`: Wait for the given string or RegExp before returning a response. If not defined, the timeout value will be used. | ||
* `timeout`: A timeout used to wait for a server reply when the 'send' method is used. Defaults to 2000 (ms) or to sendTimeout ('connect' method) if set. | ||
* `maxBufferLength`: Maximum buffer length in bytes which can be filled with response data. Defaults to 1M. | ||
### connection.write(data, [options], [callback]) -> Promise | ||
Same as `send(…)`, but `data` is sent without appending an output record separator. | ||
### connection.nextData() -> Promise | ||
Waits for and returns the next available data from the host, as a string value, either one line at a time, or the last-sent incomplete line. When the telnet session has ended, `nextData()` always returns a Promise that resolves to `null`. | ||
### connection.shell(callback) -> Promise | ||
@@ -326,3 +339,3 @@ | ||
Emitted when the write of given data is sent to the socket. | ||
Emitted when a write operation for given data is sent to the socket. | ||
@@ -355,5 +368,7 @@ ### Event: 'data' | ||
## Professional support | ||
I offer professional support for node-telnet-client and beyond. I have many years of expertise on building robust, scalable Node.js applications and can help you overcome issues and challenges preventing you to ship your great products. I also excel in software architecture and implementation, being able to provide you with development, planning, consulting, training and customization services. Feel free to [contact me](mailto:mario.kozjak@elpheria.com?subject=Professional%20Support) so we can discuss how to help you finish your products! | ||
## Sponsors | ||
Become a sponsor and get your logo on project's README on GitHub with a link to your site. Feel free to [contact me](mailto:mario.kozjak@elpheria.com?subject=Sponsors) for the arrangement! | ||
@@ -360,0 +375,0 @@ |
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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
63199
2
362
0
20
9
626
2
- Removedbluebird@^3.5.4
- Removedbluebird@3.7.2(transitive)