Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

telnet-stream

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

telnet-stream - npm Package Compare versions

Comparing version 0.0.4 to 1.0.3

CHANGELOG.md

2

index.js

@@ -1,1 +0,1 @@

module.exports = require('./lib/telnetStream');
module.exports = require("./lib/telnetStream");

@@ -1,17 +0,35 @@

// Generated by CoffeeScript 1.7.1
// Generated by CoffeeScript 2.0.2
(function() {
var SUBNEG_BUFFER_SIZE, TELNET_COMMAND, TELNET_DATA, TELNET_DO, TELNET_DONT, TELNET_IAC, TELNET_OPTION, TELNET_SUBNEG, TELNET_SUBNEG_COMMAND, TELNET_SUB_BEGIN, TELNET_SUB_END, TELNET_WILL, TELNET_WONT, TelnetInput, Transform, util;
// telnetInput.coffee
// Copyright 2017 Patrick Meade.
SUBNEG_BUFFER_SIZE = 8192;
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
TELNET_COMMAND = 'TELNET_COMMAND';
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
TELNET_DATA = 'TELNET_DATA';
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------
var DEFAULT_SUBNEGOTIATION_BUFFER_SIZE, DEFAULT_SUBNEGOTIATION_ERROR_POLICY, TELNET_COMMAND, TELNET_DATA, TELNET_DO, TELNET_DONT, TELNET_IAC, TELNET_OPTION, TELNET_SUBNEG, TELNET_SUBNEG_COMMAND, TELNET_SUB_BEGIN, TELNET_SUB_END, TELNET_WILL, TELNET_WONT, TelnetInput, Transform;
TELNET_OPTION = 'TELNET_OPTION';
DEFAULT_SUBNEGOTIATION_BUFFER_SIZE = 8192;
TELNET_SUBNEG = 'TELNET_SUBNEG';
DEFAULT_SUBNEGOTIATION_ERROR_POLICY = "keepBoth";
TELNET_SUBNEG_COMMAND = 'TELNET_SUBNEG_COMMAND';
TELNET_COMMAND = "TELNET_COMMAND";
TELNET_DATA = "TELNET_DATA";
TELNET_OPTION = "TELNET_OPTION";
TELNET_SUBNEG = "TELNET_SUBNEG";
TELNET_SUBNEG_COMMAND = "TELNET_SUBNEG_COMMAND";
TELNET_DO = 253;

@@ -31,19 +49,22 @@

Transform = require('stream').Transform;
({Transform} = require("stream"));
util = require('util');
TelnetInput = class TelnetInput extends Transform {
constructor(opt) {
var options;
options = opt || {};
super(options);
this.state = TELNET_DATA;
this.subBufSize = options.bufferSize || DEFAULT_SUBNEGOTIATION_BUFFER_SIZE;
this.subBuf = Buffer.alloc(this.subBufSize);
this.errorPolicy = options.errorPolicy || DEFAULT_SUBNEGOTIATION_ERROR_POLICY;
}
TelnetInput = function(options) {
if ((this instanceof TelnetInput) === false) {
return new TelnetInput(options);
}
Transform.call(this, options);
this.state = TELNET_DATA;
this.subBuf = new Buffer(SUBNEG_BUFFER_SIZE);
this._transform = function(chunk, encoding, done) {
var i, _i, _ref;
this.dataBuf = new Buffer(chunk.length);
_transform(chunk, encoding, callback) {
var byte, i, len;
this.dataBuf = Buffer.alloc(chunk.length * 2);
this.dataBufIndex = 0;
for (i = _i = 0, _ref = chunk.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
this.handle(chunk[i]);
for (i = 0, len = chunk.length; i < len; i++) {
byte = chunk[i];
this._handle(byte);
}

@@ -53,5 +74,6 @@ if (this.dataBufIndex > 0) {

}
return done();
};
this.handle = function(chunkData) {
return callback();
}
_handle(chunkData) {
switch (this.state) {

@@ -82,3 +104,3 @@ case TELNET_DATA:

this.state = TELNET_DATA;
return this.emit('command', chunkData);
return this.emit("command", chunkData);
}

@@ -90,16 +112,17 @@ break;

this.state = TELNET_DATA;
return this.emit('do', chunkData);
return this.emit("do", chunkData);
case TELNET_DONT:
this.state = TELNET_DATA;
return this.emit('dont', chunkData);
return this.emit("dont", chunkData);
case TELNET_WILL:
this.state = TELNET_DATA;
return this.emit('will', chunkData);
return this.emit("will", chunkData);
case TELNET_WONT:
this.state = TELNET_DATA;
return this.emit('wont', chunkData);
return this.emit("wont", chunkData);
case TELNET_SUB_BEGIN:
this.state = TELNET_SUBNEG;
this.option = chunkData;
return this.subBufIndex = 0;
this.subBufIndex = 0;
return this.subOverflowEmit = false;
}

@@ -112,4 +135,3 @@ break;

default:
this.subBuf[this.subBufIndex] = chunkData;
return this.subBufIndex++;
return this._handleSub(chunkData);
}

@@ -121,19 +143,42 @@ break;

this.state = TELNET_SUBNEG;
this.subBuf[this.subBufIndex] = TELNET_IAC;
return this.subBufIndex++;
return this._handleSub(TELNET_IAC);
case TELNET_SUB_END:
this.state = TELNET_DATA;
return this.emit('sub', this.option, this.subBuf.slice(0, this.subBufIndex));
return this.emit("sub", this.option, this.subBuf.slice(0, this.subBufIndex));
default:
return this.state = TELNET_SUBNEG;
this.state = TELNET_SUBNEG;
this.emit("error", new Error("expected IAC or SE"));
switch (this.errorPolicy) {
case "discardBoth":
break;
case "keepData":
return this._handleSub(chunkData);
default:
// "keepBoth"
this._handleSub(TELNET_IAC);
return this._handleSub(chunkData);
}
}
}
};
return this;
}
_handleSub(subByte) {
if (this.subBufIndex >= this.subBufSize) {
if (!this.subOverflowEmit) {
this.subOverflowEmit = true;
this.emit("error", new Error("subnegotiation buffer overflow"));
}
return;
}
this.subBuf[this.subBufIndex] = subByte;
return this.subBufIndex++;
}
};
util.inherits(TelnetInput, Transform);
exports.TelnetInput = TelnetInput;
//----------------------------------------------------------------------------
// end of telnetInput.coffee
}).call(this);

@@ -1,5 +0,21 @@

// Generated by CoffeeScript 1.7.1
// Generated by CoffeeScript 2.0.2
(function() {
var TELNET_DO, TELNET_DONT, TELNET_IAC, TELNET_SUB_BEGIN, TELNET_SUB_END, TELNET_WILL, TELNET_WONT, TelnetOutput, Transform, duplicateIAC, util;
// telnetOutput.coffee
// Copyright 2017 Patrick Meade.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------
var TELNET_DO, TELNET_DONT, TELNET_IAC, TELNET_SUB_BEGIN, TELNET_SUB_END, TELNET_WILL, TELNET_WONT, TelnetOutput, Transform;
TELNET_DO = 253;

@@ -19,89 +35,84 @@

Transform = require('stream').Transform;
({Transform} = require("stream"));
util = require('util');
TelnetOutput = class TelnetOutput extends Transform {
constructor(options) {
super(options);
}
duplicateIAC = function(buffer) {
var bufferIndex, data, xlateBuf, xlateIndex;
bufferIndex = 0;
xlateIndex = 0;
xlateBuf = new Buffer(buffer.length * 2);
while (bufferIndex < buffer.length) {
data = buffer[bufferIndex];
bufferIndex++;
xlateBuf.writeUInt8(data, xlateIndex);
xlateIndex++;
if (data === TELNET_IAC) {
xlateBuf.writeUInt8(data, xlateIndex);
_transform(chunk, encoding, done) {
this.push(this._duplicateIAC(chunk));
return done();
}
_duplicateIAC(buffer) {
var byte, i, len, xlateBuf, xlateIndex;
xlateIndex = 0;
xlateBuf = Buffer.alloc(buffer.length * 2);
for (i = 0, len = buffer.length; i < len; i++) {
byte = buffer[i];
xlateBuf[xlateIndex] = byte;
xlateIndex++;
if (byte === TELNET_IAC) {
xlateBuf[xlateIndex] = byte;
xlateIndex++;
}
}
return xlateBuf.slice(0, xlateIndex);
}
return xlateBuf.slice(0, xlateIndex);
};
TelnetOutput = function(options) {
if ((this instanceof TelnetOutput) === false) {
return new TelnetOutput(options);
_writeOption(command, option) {
var cmdBuf;
cmdBuf = Buffer.alloc(3);
cmdBuf[0] = TELNET_IAC;
cmdBuf[1] = command;
cmdBuf[2] = option;
return this.push(cmdBuf);
}
Transform.call(this, options);
return this;
};
util.inherits(TelnetOutput, Transform);
writeCommand(command) {
var cmdBuf;
cmdBuf = Buffer.alloc(2);
cmdBuf[0] = TELNET_IAC;
cmdBuf[1] = command;
return this.push(cmdBuf);
}
TelnetOutput.prototype._transform = function(chunk, encoding, done) {
this.push(duplicateIAC(chunk));
return done();
};
writeDo(option) {
return this._writeOption(TELNET_DO, option);
}
TelnetOutput.prototype._writeOption = function(command, option) {
var cmdBuf;
cmdBuf = new Buffer(3);
cmdBuf[0] = TELNET_IAC;
cmdBuf[1] = command;
cmdBuf[2] = option;
return this.push(cmdBuf);
};
writeDont(option) {
return this._writeOption(TELNET_DONT, option);
}
TelnetOutput.prototype.writeCommand = function(command) {
var cmdBuf;
cmdBuf = new Buffer(2);
cmdBuf[0] = TELNET_IAC;
cmdBuf[1] = command;
return this.push(cmdBuf);
};
writeSub(option, buffer) {
var negBuf, subBegin, subBuf, subEnd;
negBuf = this._duplicateIAC(buffer);
subBegin = Buffer.alloc(3);
subBegin[0] = TELNET_IAC;
subBegin[1] = TELNET_SUB_BEGIN;
subBegin[2] = option;
subEnd = Buffer.alloc(2);
subEnd[0] = TELNET_IAC;
subEnd[1] = TELNET_SUB_END;
subBuf = Buffer.concat([subBegin, negBuf, subEnd], negBuf.length + 5);
return this.push(subBuf);
}
TelnetOutput.prototype.writeDo = function(option) {
return this._writeOption(TELNET_DO, option);
};
writeWill(option) {
return this._writeOption(TELNET_WILL, option);
}
TelnetOutput.prototype.writeDont = function(option) {
return this._writeOption(TELNET_DONT, option);
};
TelnetOutput.prototype.writeSub = function(option, buffer) {
var i, negBuf, subBuf, _i, _ref;
negBuf = duplicateIAC(buffer);
subBuf = new Buffer(negBuf.length + 5);
subBuf[0] = TELNET_IAC;
subBuf[1] = TELNET_SUB_BEGIN;
subBuf[2] = option;
for (i = _i = 0, _ref = negBuf.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
subBuf[i + 3] = negBuf[i];
writeWont(option) {
return this._writeOption(TELNET_WONT, option);
}
subBuf[negBuf.length + 3] = TELNET_IAC;
subBuf[negBuf.length + 4] = TELNET_SUB_END;
return this.push(subBuf);
};
TelnetOutput.prototype.writeWill = function(option) {
return this._writeOption(TELNET_WILL, option);
};
TelnetOutput.prototype.writeWont = function(option) {
return this._writeOption(TELNET_WONT, option);
};
exports.TelnetOutput = TelnetOutput;
//----------------------------------------------------------------------------
// end of telnetOutput.coffee
}).call(this);

@@ -1,7 +0,28 @@

// Generated by CoffeeScript 1.7.1
// Generated by CoffeeScript 2.0.2
(function() {
exports.TelnetInput = require('./telnetInput').TelnetInput;
// telnetStream.coffee
// Copyright 2017 Patrick Meade.
exports.TelnetOutput = require('./telnetOutput').TelnetOutput;
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------
exports.TelnetInput = require("./telnetInput").TelnetInput;
exports.TelnetOutput = require("./telnetOutput").TelnetOutput;
exports.TelnetSocket = require("./telnetSocket").TelnetSocket;
//----------------------------------------------------------------------------
// end of telnetStream.coffee
}).call(this);
{
"name": "telnet-stream",
"version": "0.0.4",
"description": "Transform streams for TELNET protocol",
"version": "1.0.3",
"description": "TELNET protocol support library",
"keywords": [

@@ -17,2 +17,3 @@ "do",

"rfc855",
"socket",
"stream",

@@ -29,5 +30,8 @@ "TELNET",

"files": [
"CHANGELOG.md",
"index.js",
"lib",
"package.json"
"LICENSE",
"package.json",
"README.md"
],

@@ -40,6 +44,7 @@ "main": "telnet-stream",

"devDependencies": {
"coffee-script": "1.7.x",
"mocha": "1.19.x",
"should": "3.3.x"
"coffeescript": "2.0.2",
"istanbul": "0.4.5",
"mocha": "4.0.1",
"should": "13.1.3"
}
}

@@ -5,49 +5,41 @@ # telnet-stream

## Motivation
Although venerable, the [TELNET](https://en.wikipedia.org/wiki/Telnet)
protocol is still in use by some services and expected by some clients.
If you need to connect to something that "speaks TELNET", this module
offers a pair of bare-bones Transform streams for that purpose.
offers some simple objects for that purpose.
## Simple Solution
## Example 0: A Simple Solution
If you need to connect to something that speaks TELNET, but you don't
care about options or negotiations, then simply use the stream as-is.
It will filter out all the "TELNET stuff" and pass the rest on to you.
care about options or negotiations, then simply use TelnetSocket to
decorate a regular socket. It will filter out all the "TELNET stuff"
and pass the remaining data on to you.
Because TelnetInput and TelnetOutput are Node.js Transform streams,
they support all the same operations that regular streams do. See the
[Node.js Stream API](http://nodejs.org/api/stream.html) for more details.
// get references to the required stuff
var net = require('net');
var TelnetInput = require('telnet-stream').TelnetInput;
var TelnetOutput = require('telnet-stream').TelnetOutput;
var TelnetSocket = require('telnet-stream').TelnetSocket;
// create a Socket connection
var socket = net.createConnection(3000, 'godwars2.org');
// decorate the Socket connection as a TelnetSocket
var tSocket = new TelnetSocket(socket);
// send the TelnetSocket output to the standard output (terminal)
tSocket.pipe(process.stdout);
// send the standard input (keyboard) to the TelnetSocket
process.stdin.pipe(tSocket);
var socket = net.createConnection(3000, 'godwars2.org', function() {
var telnetInput = new TelnetInput();
var telnetOutput = new TelnetOutput();
socket.pipe(telnetInput).pipe(process.stdout);
process.stdin.pipe(telnetOutput).pipe(socket);
});
## Usage
Maybe you have more complex needs. Perhaps you need certain options
to be turned on or off, or have important information to pull from
a subnegotiation. telnet-stream has you covered in those situations.
a subnegotiation. This is pretty easy to do with TelnetSocket.
### TelnetInput
### TelnetSocket input
TelnetSocket is a decorator for a [net.Socket](https://nodejs.org/api/net.html)
object. Incoming TELNET commands, options, and negotiations are emitted as
events. Non-TELNET data is passed through without changes.
TelnetInput is a Transform stream for the input side of TELNET.
TELNET commands, options, and negotiations are emitted as events.
Non-TELNET data is passed transparently as input data.
#### Event: 'command'
When the remote system issues a TELNET command that is not option
negotiation, TelnetInput will emit a 'command' event.
negotiation, TelnetSocket will emit a 'command' event.
var telnetIn = new TelnetInput();
telnetIn.on('command', function(command) {
var tSocket = new TelnetSocket(socket);
tSocket.on('command', function(command) {
// Received: IAC <command> - See RFC 854

@@ -57,9 +49,8 @@ });

#### Event: 'do'
When the remote system wants to request that the local system
perform some function or obey some protocol, TelnetInput will
perform some function or obey some protocol, TelnetSocket will
emit a 'do' event:
var telnetIn = new TelnetInput();
telnetIn.on('do', function(option) {
var tSocket = new TelnetSocket(socket);
tSocket.on('do', function(option) {
// Received: IAC DO <option> - See RFC 854

@@ -69,9 +60,8 @@ });

#### Event: 'dont'
When the remote system wants to request that the local system
NOT perform some function or NOT obey some protocol, TelnetInput
NOT perform some function or NOT obey some protocol, TelnetSocket
will emit a 'dont' event:
var telnetIn = new TelnetInput();
telnetIn.on('dont', function(option) {
var tSocket = new TelnetSocket(socket);
tSocket.on('dont', function(option) {
// Received: IAC DONT <option> - See RFC 854

@@ -81,3 +71,2 @@ });

#### Event: 'sub'
After negotiating an option, either the local or remote system

@@ -88,4 +77,4 @@ may engage in a more complex subnegotiation. For example, the

var telnetIn = new TelnetInput();
telnetIn.on('sub', function(option, buffer) {
var tSocket = new TelnetSocket(socket);
tSocket.on('sub', function(option, buffer) {
// Received: IAC SB <option> <buffer> IAC SE - See RFC 855

@@ -95,9 +84,8 @@ });

#### Event: 'will'
When the remote system wants to offer that it will perform some
function or obey some protocol for the local system, TelnetInput
function or obey some protocol for the local system, TelnetSocket
will emit a 'will' event:
var telnetIn = new TelnetInput();
telnetIn.on('will', function(option) {
var tSocket = new TelnetSocket(socket);
tSocket.on('will', function(option) {
// Received: IAC WILL <option> - See RFC 854

@@ -107,41 +95,36 @@ });

#### Event: 'wont'
When the remote system wants to refuse to perform some function
or obey some protocol for the local system, TelnetInput will
or obey some protocol for the local system, TelnetSocket will
emit a 'wont' event:
var telnetIn = new TelnetInput();
telnetIn.on('wont', function(option) {
var tSocket = new TelnetSocket(socket);
tSocket.on('wont', function(option) {
// Received: IAC WONT <option> - See RFC 854
});
### TelnetOutput
### TelnetSocket output
TelnetSocket is a decorator for a [net.Socket](https://nodejs.org/api/net.html)
object. Outgoing data is properly escaped where it might be confused
for a TELNET command. There are also support functions to allow sending
TELNET commands, options, and negotiations as well.
TelnetOutput is a Transform stream for the output side of TELNET.
Data written to TelnetOutput is properly escaped to ensure that
it isn't interpreted as a TELNET command. It also has methods for
sending TELNET option negotiations and subnegotiations.
#### IAC escape
TELNET commands start with the Interpret as Command (IAC) octet.
In order to send a literal IAC octet (one that is intended as data,
not as a TELNET command), it must be sent as IAC IAC. TelnetOutput
TELNET commands start with the Interpret as Command (IAC) byte.
In order to send a literal IAC byte (one that is intended as data,
not as a TELNET command), it must be sent as IAC IAC. TelnetSocket
takes care of this transformation automatically.
#### writeCommand(command)
* command - The command byte to send
* command - The command octet to send
Call this method to send a TELNET command to the remote system.
var NOP = 241; // No operation. -- See RFC 854
var telnetOut = new TelnetOutput();
var tSocket = new TelnetSocket(socket);
// Sends: IAC NOP
telnetOut.writeCommand(NOP);
tSocket.writeCommand(NOP);
#### writeDo(option)
* option - The option byte to request of the remote system
* option - The option octet to request of the remote system
Call this method to send a TELNET DO option negotiation to the remote

@@ -152,10 +135,9 @@ system. A DO request is sent when the local system wants the remote

var NAWS = 31; // Negotiate About Window Size -- See RFC 1073
var telnetOut = new TelnetOutput();
var tSocket = new TelnetSocket(socket);
// Sends: IAC DO NAWS
telnetOut.writeDo(NAWS);
tSocket.writeDo(NAWS);
#### writeDont(option)
* option - The option byte to request of the remote system
* option - The option octet to request of the remote system
Call this method to send a TELNET DONT option negotiation to the remote

@@ -166,14 +148,13 @@ system. A DONT request is sent when the local system wants the remote

var NAWS = 31; // Negotiate About Window Size -- See RFC 1073
var telnetOut = new TelnetOutput();
var tSocket = new TelnetSocket(socket);
// Sends: IAC DONT NAWS
telnetOut.writeDont(NAWS);
tSocket.writeDont(NAWS);
#### writeSub(option, buffer)
* option - The option octet; identifies what the subnegotiation is about
* option - The option byte; identifies what the subnegotiation is about
* buffer - The buffer containing the subnegotiation data to send
Call this method to send a TELNET subnegotiation to the remote system.
After the local and remote system have negotiated an option, then
subnegotiation information can be sent.
After the local and remote system have negotiated and agreed to use
an option, then subnegotiation information can be sent.

@@ -183,5 +164,4 @@ See Example #2: Negotiate About Window Size (NAWS) below.

#### writeWill(option)
* option - The option byte to offer to the remote system
* option - The option octet to offer to the remote system
Call this method to send a TELNET WILL option negotiation to the remote

@@ -192,10 +172,9 @@ system. A WILL offer is sent when the local system wants to inform the

var NAWS = 31; // Negotiate About Window Size -- See RFC 1073
var telnetOut = new TelnetOutput();
var tSocket = new TelnetSocket(socket);
// Sends: IAC WILL NAWS
telnetOut.writeWill(NAWS);
tSocket.writeWill(NAWS);
#### writeWont(option)
* option - The option byte to refuse to the remote system
* option - The option octet to refuse to the remote system
Call this method to send a TELNET WONT option negotiation to the remote

@@ -207,8 +186,7 @@ system. A WONT refusal is sent when the remote system has requested that

var NAWS = 31; // Negotiate About Window Size -- See RFC 1073
var telnetOut = new TelnetOutput();
var tSocket = new TelnetSocket(socket);
// Sends: IAC WONT NAWS
telnetOut.writeWont(NAWS);
tSocket.writeWont(NAWS);
### Example 1: Options Actively Refused
The simple example above provided a simple TELNET client.

@@ -225,20 +203,21 @@ However, all TELNET commands were filtered and ignored.

var net = require('net');
var TelnetInput = require('telnet-stream').TelnetInput;
var TelnetOutput = require('telnet-stream').TelnetOutput;
var TelnetSocket = require('telnet-stream').TelnetSocket;
var socket = net.createConnection(3000, 'godwars2.org');
var tSocket = new TelnetSocket(socket);
var socket = net.createConnection(3000, 'godwars2.org', function() {
var telnetInput = new TelnetInput();
var telnetOutput = new TelnetOutput();
// tell remote we WONT do anything we're asked to DO
tSocket.on('do', function(option) {
tSocket.writeWont(option);
});
telnetInput.on('do', function(option) {
telnetOutput.writeWont(option);
});
// tell the remote DONT do whatever they WILL offer
tSocket.on('will', function(option) {
tSocket.writeDont(option);
});
telnetInput.on('will', function(option) {
telnetOutput.writeDont(option);
});
tSocket.pipe(process.stdout);
process.stdin.pipe(tSocket);
socket.pipe(telnetInput).pipe(process.stdout);
process.stdin.pipe(telnetOutput).pipe(socket);
});
This code is mostly the same as Example 0 except that we respond
to incoming 'do' and 'will' events sent by the remote side.

@@ -251,3 +230,2 @@ Note that incoming 'dont' and 'wont' events are ignored.

### Example 2: Negotiate About Window Size (NAWS)
There is a TELNET option called "Negotiate About Window Size" (NAWS)

@@ -260,3 +238,2 @@ that allows the server to learn the dimensions of the client's output

#### Server Side
This code implements a simple TELNET server that listens for

@@ -266,29 +243,31 @@ NAWS subnegotiations and reports the client's window size

// setup stuff -- see the other examples for details
var NAWS = 31; // Negotiate About Window Size -- See RFC 1073
var net = require('net');
var TelnetInput = require('telnet-stream').TelnetInput;
var TelnetOutput = require('telnet-stream').TelnetOutput;
var TelnetSocket = require('telnet-stream').TelnetSocket;
var serverSocket = net.createServer(function(connection) {
var telnetInput = new TelnetInput();
var telnetOutput = new TelnetOutput();
// create a service to listen for incoming connections
var server = net.createServer(function(socket) {
// wrap the socket as a TelnetSocket
var tSocket = new TelnetSocket(socket);
telnetInput.on('sub', function(option, buffer) {
// if they send us a subnegotiation
tSocket.on('sub', function(option, buffer) {
// if they are telling us their window size
if(option === NAWS) {
// display it to the console
var width = buffer.readInt16BE(0);
var height = buffer.readInt16BE(2);
console.log( 'Client window: ' + width + 'x' + height);
console.log('Client window: ' + width + 'x' + height);
}
});
connection.pipe(telnetInput).pipe(process.stdout);
process.stdin.pipe(telnetOutput).pipe(connection);
telnetOutput.writeDo(NAWS);
// tell the client to send window size subnegotiations
tSocket.writeDo(NAWS);
});
serverSocket.listen(3000);
// start our server listening on port 3000
server.listen(3000);
#### Client Side
This code implements a simple TELNET client that sends NAWS

@@ -299,62 +278,100 @@ subnegotiations when the output window is resized. Note that

// setup stuff -- see the other examples for details
var NAWS = 31; // Negotiate About Window Size -- See RFC 1073
var net = require('net');
var TelnetInput = require('telnet-stream').TelnetInput;
var TelnetOutput = require('telnet-stream').TelnetOutput;
var TelnetSocket = require('telnet-stream').TelnetSocket;
var socket = net.createConnection(3000);
var tSocket = new TelnetSocket(socket);
var socket = net.createConnection(3000, function() {
var telnetInput = new TelnetInput();
var telnetOutput = new TelnetOutput();
var serverNawsOk = false;
// flag to indicate if its OK to send window size
// subnegotiations to the server
var serverNawsOk = false;
var sendWindowSize = function() {
var nawsBuffer = new Buffer(4);
nawsBuffer.writeInt16BE(process.stdout.columns, 0);
nawsBuffer.writeInt16BE(process.stdout.rows, 2);
telnetOutput.writeSub(NAWS, nawsBuffer);
};
// function: send window size to the server
var sendWindowSize = function() {
// create a buffer
var nawsBuffer = new Buffer(4);
// fill the buffer up with our window dimensions
nawsBuffer.writeInt16BE(process.stdout.columns, 0);
nawsBuffer.writeInt16BE(process.stdout.rows, 2);
// send that buffer as a subnegotiation to the server
tSocket.writeSub(NAWS, nawsBuffer);
};
telnetInput.on('do', function(option) {
if(option === NAWS) {
serverNawsOk = true;
telnetOutput.writeWill(NAWS);
sendWindowSize();
}
});
// if the server sends us a DO negotiation
telnetInput.on('do', function(option) {
// if that negotiation is about window size
if(option === NAWS) {
// set the flag indicating that the server has
// told us it's OK to send our window size
serverNawsOk = true;
// tell the server that we WILL send window size
telnetOutput.writeWill(NAWS);
// send our current window size to the server
sendWindowSize();
}
});
process.stdout.on('resize', function() {
if(serverNawsOk) {
sendWindowSize();
}
});
// if the terminal window is resized
process.stdout.on('resize', function() {
// if we're OK to send our window size to the server
if(serverNawsOk) {
// send the new window size to the server
sendWindowSize();
}
});
socket.pipe(telnetInput).pipe(process.stdout);
process.stdin.pipe(telnetOutput).pipe(socket);
// setup stuff -- see the other examples for details
tSocket.pipe(process.stdout);
process.stdin.pipe(tSocket);
telnetOutput.writeWill(NAWS);
});
Run this program and it should immediately send the current
size of the terminal window to the server. After that, you
can resize your terminal window in order to make the client
program to send the new window size to the server.
## Limitations
## Advanced Use Cases
This section covers advanced use-cases. If you need to use the
TELNET protocol outside of a Socket context, or if you need to
modify some aspects of the protocol handling, this is the
section for you.
telnet-stream is not 100% faithful to the TELNET specification.
### TelnetSocket options
The TelnetSocket constructor takes an optional `options` parameter:
### Subnegotiations
`new TelnetSocket(socket, [options])`
* `socket` - Required: net.Socket to be decorated as a TELNET socket
* `options` - Optional: Options configuration
* `bufferSize` - The size of the subnegotiation buffer
* `errorPolicy` - How to handle subnegotiation command errors
#### Buffer Size
#### bufferSize
After a TELNET option is negotiated between local and remote,
either side may send subnegotiation data to the other. The
TELNET protocol itself specifies no limit to this data.
The input buffer size for TELNET subnegotiations is 8192 bytes. For most
cases, this buffer should suffice. If you are running millions of
simultaneous connections, you may need to reduce it. If your subnegotiations
are HUGE then you may need to increase it.
Practical considerations dictate placing a reasonable limit
on the amount of data buffered. Most services should NOT buffer
an unlimited amount of data. Malicious clients may be able to
cause a Denial of Service attack by forcing the server to
allocate too much memory in response their requests.
The size of the buffer is defined at the top of telnetInput.coffee:
By default, TelnetSocket will buffer to up 8192 (8K = 8 * 1024)
bytes of subnegotiation data. After this, it will emit an `error`
event to indicate an overflow in the subnegotiation buffer. These
additional bytes will be discarded.
SUBNEG_BUFFER_SIZE = 8192
In order to modify the size of the buffer, one can specify an
options object with the `bufferSize` option:
#### Interpret As Command (IAC)
// this TelnetSocket can handle 16K subnegotiations!
var tSocket = new TelnetSocket(socket, { bufferSize: 16384 });
The default of 8K should sufficient for most use-cases.
#### errorPolicy
During a subnegotiation, there are two valid sequences that begin with
IAC. One is to escape another IAC intended as a literal data octet:
IAC. One is to escape another IAC intended as a literal data byte:
IAC IAC // this is a literal IAC [Hex 0xFF, Dec 255] octet
IAC IAC // this is a literal IAC [Hex 0xFF, Dec 255] byte

@@ -368,16 +385,37 @@ The other is to end the ongoing subnegotiation:

telnet-stream will silently consume both the IAC and following
octet and return to consuming subnegotiation data. The final
effect is as if those two octets never existed in the stream.
If an unknown sequence is detected; IAC followed by something
that isn't IAC or SE, then an `error` event will be emitted.
The `errorPolicy` option can set a policy for what will happen
to the two erroneous bytes.
This error might occur when talking to faulty TELNET implementations
that fail to properly escape IAC bytes as IAC IAC in subnegotiations.
##### "keepBoth"
By default, it is assumed that a faulty sequence starting with
IAC is a failure to properly escape a data IAC byte as IAC IAC.
TelnetSocket will keep both bytes (the IAC and the following
data byte) and continue the subnegotiation.
##### "keepData"
If you want TelnetSocket to keep the data byte (the byte
following the IAC), but discard the IAC, the error policy
`keepData` will do this. The data byte will be added to the
subnegotiation and the subnegotiation will continue.
// filter out erroneous IAC bytes
var tSocket = new TelnetSocket(socket, { errorPolicy: "keepData" });
##### "discardBoth"
If you want TelnetSocket to discard both the IAC and the
data byte that follows it, the error policy "discardBoth"
will do this. The subnegotiation will continue, containing
neither of the two erroneous bytes.
// filter out erroneous IAC <data> bytes
var tSocket = new TelnetSocket(socket, { errorPolicy: "discardBoth" });
### Network Virtual Terminal (NVT)
In addition to TELNET negotiation, RFC 854 specifies a Network Virtual
Terminal (NVT). Among other things in the NVT specification,
a Carriage Return (CR) [Hex 0x0C, Dec 13] octet must be followed by
either a Line Feed (LF) [Hex 0x0A, Dec 10] octet or a Null (NUL) [Hex 0x00,
Dec 0] octet. It says "the CR character must be avoided in other contexts".
a Carriage Return (CR) [Hex 0x0C, Dec 13] byte must be followed by
either a Line Feed (LF) [Hex 0x0A, Dec 10] byte or a Null (NUL) [Hex 0x00,
Dec 0] byte. It says "the CR character must be avoided in other contexts".

@@ -389,8 +427,63 @@ Furthermore, it goes on to specify: "Even though it may be known in some

telnet-stream DOES NOT respect this part of the specification. The character
following a CR in the data stream is not modified in any way. If you want
or need this behavior, you will need to modify telnet-stream.
telnet-stream DOES NOT respect this part of the specification. The
character following a CR in the data stream is never modified in any
way. If you want or need this behavior, please open an issue on GitHub.
The author would be very curious to discover a use-case where this
behavior is both expected and necessary.
### TelnetInput and TelnetOutput
TelnetSocket is built on lower level Transform streams. These
transform streams do the real work of managing the TELNET protocol,
where TelnetSocket is simply a convenience wrapper over a Socket.
If you need TELNET handling outside of a Socket context; for example
filtering TELNET codes from a raw log, you may be interested in these
transform stream components.
Because TelnetInput and TelnetOutput are Node.js Transform streams,
they support all the same operations that regular streams do. See the
[Node.js Stream API](http://nodejs.org/api/stream.html) for more details.
#### TelnetInput
TelnetInput is a Transform stream for the input side of TELNET.
TELNET commands, options, and negotiations are emitted as events.
Non-TELNET data is passed transparently as input data.
See: Event handlers ('command', 'do', 'dont', 'sub', 'will', 'wont')
Like TelnetSocket, the TelnetInput constructor takes an optional
`options` object supporting options `bufferSize` and `errorPolicy`.
`new TelnetInput([options])`
* `options` - Optional: Options configuration
* `bufferSize` - The size of the subnegotiation buffer
* `errorPolicy` - How to handle subnegotiation command errors
#### TelnetOutput
TelnetOutput is a Transform stream for the output side of TELNET.
Data written to TelnetOutput is properly escaped to ensure that it
isn't interpreted as a TELNET command. It also has methods for sending
TELNET option negotiations and subnegotiations.
See: Helper functions (`writeCommand`, `writeDo`, `writeDont`,
`writeSub`, `writeWill`, `writeWont`)
#### Example 0 rewritten with transform streams
This code is equivalent to Example 0, but instead of using TelnetSocket
to decorate the provided Socket, the readable and writable sides
are handled individually by Transform stream objects.
var net = require('net');
var TelnetInput = require('telnet-stream').TelnetInput;
var TelnetOutput = require('telnet-stream').TelnetOutput;
var socket = net.createConnection(3000, 'godwars2.org', function() {
var telnetInput = new TelnetInput();
var telnetOutput = new TelnetOutput();
socket.pipe(telnetInput).pipe(process.stdout);
process.stdin.pipe(telnetOutput).pipe(socket);
});
## Development
In order to make modifications to telnet-stream, you'll need to

@@ -400,11 +493,17 @@ establish a development environment:

git clone https://github.com/blinkdog/telnet-stream.git
cd telnet-stream
npm install
cake rebuild
node_modules/.bin/cake rebuild
The source files are located in src/coffee
The source files are located in `src/main/coffee`.
The test source files are located in `src/test/coffee`.
You can see a coverage report by invoking the `coverage` target:
node_modules/.bin/cake coverage
## License
telnet-stream
Copyright 2013-2017 Patrick Meade.
telnet-stream is Copyright 2013 Patrick Meade.
This program is free software: you can redistribute it and/or modify

@@ -420,3 +519,3 @@ it under the terms of the GNU Affero General Public License as

You should have received a copy of the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.txt)
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc