Pardon the noise. More tab to space conversion!

This commit is contained in:
Bryan Ashby 2018-06-22 21:26:46 -06:00
parent c3635bb26b
commit 1d8be6b014
128 changed files with 8017 additions and 8017 deletions

View file

@ -1,166 +1,166 @@
/* jslint node: true */
'use strict';
// ENiGMA½
const baseClient = require('../../client.js');
const Log = require('../../logger.js').log;
const LoginServerModule = require('../../login_server_module.js');
const Config = require('../../config.js').get;
const EnigAssert = require('../../enigma_assert.js');
const { stringFromNullTermBuffer } = require('../../string_util.js');
// ENiGMA½
const baseClient = require('../../client.js');
const Log = require('../../logger.js').log;
const LoginServerModule = require('../../login_server_module.js');
const Config = require('../../config.js').get;
const EnigAssert = require('../../enigma_assert.js');
const { stringFromNullTermBuffer } = require('../../string_util.js');
// deps
const net = require('net');
const buffers = require('buffers');
const { Parser } = require('binary-parser');
const util = require('util');
// deps
const net = require('net');
const buffers = require('buffers');
const { Parser } = require('binary-parser');
const util = require('util');
//var debug = require('debug')('telnet');
//var debug = require('debug')('telnet');
const ModuleInfo = exports.moduleInfo = {
name : 'Telnet',
desc : 'Telnet Server',
author : 'NuSkooler',
isSecure : false,
packageName : 'codes.l33t.enigma.telnet.server',
name : 'Telnet',
desc : 'Telnet Server',
author : 'NuSkooler',
isSecure : false,
packageName : 'codes.l33t.enigma.telnet.server',
};
exports.TelnetClient = TelnetClient;
exports.TelnetClient = TelnetClient;
//
// Telnet Protocol Resources
// * http://pcmicro.com/netfoss/telnet.html
// * http://mud-dev.wikidot.com/telnet:negotiation
// Telnet Protocol Resources
// * http://pcmicro.com/netfoss/telnet.html
// * http://mud-dev.wikidot.com/telnet:negotiation
//
/*
TODO:
* Document COMMANDS -- add any missing
* Document OPTIONS -- add any missing
* Internally handle OPTIONS:
* Some should be emitted generically
* Some should be handled internally -- denied, handled, etc.
*
TODO:
* Document COMMANDS -- add any missing
* Document OPTIONS -- add any missing
* Internally handle OPTIONS:
* Some should be emitted generically
* Some should be handled internally -- denied, handled, etc.
*
* Allow term (ttype) to be set by environ sub negotiation
* Allow term (ttype) to be set by environ sub negotiation
* Process terms in loop.... research needed
* Process terms in loop.... research needed
* Handle will/won't
* Handle do's, ..
* Some won't should close connection
* Handle will/won't
* Handle do's, ..
* Some won't should close connection
* Options/Commands we don't understand shouldn't crash the server!!
* Options/Commands we don't understand shouldn't crash the server!!
*/
const COMMANDS = {
SE : 240, // End of Sub-Negotation Parameters
NOP : 241, // No Operation
DM : 242, // Data Mark
BRK : 243, // Break
IP : 244, // Interrupt Process
AO : 245, // Abort Output
AYT : 246, // Are You There?
EC : 247, // Erase Character
EL : 248, // Erase Line
GA : 249, // Go Ahead
SB : 250, // Start Sub-Negotiation Parameters
WILL : 251, //
WONT : 252,
DO : 253,
DONT : 254,
IAC : 255, // (Data Byte)
SE : 240, // End of Sub-Negotation Parameters
NOP : 241, // No Operation
DM : 242, // Data Mark
BRK : 243, // Break
IP : 244, // Interrupt Process
AO : 245, // Abort Output
AYT : 246, // Are You There?
EC : 247, // Erase Character
EL : 248, // Erase Line
GA : 249, // Go Ahead
SB : 250, // Start Sub-Negotiation Parameters
WILL : 251, //
WONT : 252,
DO : 253,
DONT : 254,
IAC : 255, // (Data Byte)
};
//
// Resources:
// * http://www.faqs.org/rfcs/rfc1572.html
// Resources:
// * http://www.faqs.org/rfcs/rfc1572.html
//
const SB_COMMANDS = {
IS : 0,
SEND : 1,
INFO : 2,
IS : 0,
SEND : 1,
INFO : 2,
};
//
// Telnet Options
// Telnet Options
//
// Resources
// * http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html
// * http://www.networksorcery.com/enp/protocol/telnet.htm
// Resources
// * http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html
// * http://www.networksorcery.com/enp/protocol/telnet.htm
//
const OPTIONS = {
TRANSMIT_BINARY : 0, // http://tools.ietf.org/html/rfc856
ECHO : 1, // http://tools.ietf.org/html/rfc857
// RECONNECTION : 2
SUPPRESS_GO_AHEAD : 3, // aka 'SGA': RFC 858 @ http://tools.ietf.org/html/rfc858
//APPROX_MESSAGE_SIZE : 4
STATUS : 5, // http://tools.ietf.org/html/rfc859
TIMING_MARK : 6, // http://tools.ietf.org/html/rfc860
//RC_TRANS_AND_ECHO : 7, // aka 'RCTE' @ http://www.rfc-base.org/txt/rfc-726.txt
//OUPUT_LINE_WIDTH : 8,
//OUTPUT_PAGE_SIZE : 9, //
//OUTPUT_CARRIAGE_RETURN_DISP : 10, // RFC 652
//OUTPUT_HORIZ_TABSTOPS : 11, // RFC 653
//OUTPUT_HORIZ_TAB_DISP : 12, // RFC 654
//OUTPUT_FORMFEED_DISP : 13, // RFC 655
//OUTPUT_VERT_TABSTOPS : 14, // RFC 656
//OUTPUT_VERT_TAB_DISP : 15, // RFC 657
//OUTPUT_LF_DISP : 16, // RFC 658
//EXTENDED_ASCII : 17, // RFC 659
//LOGOUT : 18, // RFC 727
//BYTE_MACRO : 19, // RFC 753
//DATA_ENTRY_TERMINAL : 20, // RFC 1043
//SUPDUP : 21, // RFC 736
//SUPDUP_OUTPUT : 22, // RFC 749
SEND_LOCATION : 23, // RFC 779
TERMINAL_TYPE : 24, // aka 'TTYPE': RFC 1091 @ http://tools.ietf.org/html/rfc1091
//END_OF_RECORD : 25, // RFC 885
//TACACS_USER_ID : 26, // RFC 927
//OUTPUT_MARKING : 27, // RFC 933
//TERMINCAL_LOCATION_NUMBER : 28, // RFC 946
//TELNET_3270_REGIME : 29, // RFC 1041
WINDOW_SIZE : 31, // aka 'NAWS': RFC 1073 @ http://tools.ietf.org/html/rfc1073
TERMINAL_SPEED : 32, // RFC 1079 @ http://tools.ietf.org/html/rfc1079
REMOTE_FLOW_CONTROL : 33, // RFC 1072 @ http://tools.ietf.org/html/rfc1372
LINEMODE : 34, // RFC 1184 @ http://tools.ietf.org/html/rfc1184
X_DISPLAY_LOCATION : 35, // aka 'XDISPLOC': RFC 1096 @ http://tools.ietf.org/html/rfc1096
NEW_ENVIRONMENT_DEP : 36, // aka 'NEW-ENVIRON': RFC 1408 @ http://tools.ietf.org/html/rfc1408 (note: RFC 1572 is an update to this)
AUTHENTICATION : 37, // RFC 2941 @ http://tools.ietf.org/html/rfc2941
ENCRYPT : 38, // RFC 2946 @ http://tools.ietf.org/html/rfc2946
NEW_ENVIRONMENT : 39, // aka 'NEW-ENVIRON': RFC 1572 @ http://tools.ietf.org/html/rfc1572 (note: update to RFC 1408)
//TN3270E : 40, // RFC 2355
//XAUTH : 41,
//CHARSET : 42, // RFC 2066
//REMOTE_SERIAL_PORT : 43,
//COM_PORT_CONTROL : 44, // RFC 2217
//SUPRESS_LOCAL_ECHO : 45,
//START_TLS : 46,
//KERMIT : 47, // RFC 2840
//SEND_URL : 48,
//FORWARD_X : 49,
TRANSMIT_BINARY : 0, // http://tools.ietf.org/html/rfc856
ECHO : 1, // http://tools.ietf.org/html/rfc857
// RECONNECTION : 2
SUPPRESS_GO_AHEAD : 3, // aka 'SGA': RFC 858 @ http://tools.ietf.org/html/rfc858
//APPROX_MESSAGE_SIZE : 4
STATUS : 5, // http://tools.ietf.org/html/rfc859
TIMING_MARK : 6, // http://tools.ietf.org/html/rfc860
//RC_TRANS_AND_ECHO : 7, // aka 'RCTE' @ http://www.rfc-base.org/txt/rfc-726.txt
//OUPUT_LINE_WIDTH : 8,
//OUTPUT_PAGE_SIZE : 9, //
//OUTPUT_CARRIAGE_RETURN_DISP : 10, // RFC 652
//OUTPUT_HORIZ_TABSTOPS : 11, // RFC 653
//OUTPUT_HORIZ_TAB_DISP : 12, // RFC 654
//OUTPUT_FORMFEED_DISP : 13, // RFC 655
//OUTPUT_VERT_TABSTOPS : 14, // RFC 656
//OUTPUT_VERT_TAB_DISP : 15, // RFC 657
//OUTPUT_LF_DISP : 16, // RFC 658
//EXTENDED_ASCII : 17, // RFC 659
//LOGOUT : 18, // RFC 727
//BYTE_MACRO : 19, // RFC 753
//DATA_ENTRY_TERMINAL : 20, // RFC 1043
//SUPDUP : 21, // RFC 736
//SUPDUP_OUTPUT : 22, // RFC 749
SEND_LOCATION : 23, // RFC 779
TERMINAL_TYPE : 24, // aka 'TTYPE': RFC 1091 @ http://tools.ietf.org/html/rfc1091
//END_OF_RECORD : 25, // RFC 885
//TACACS_USER_ID : 26, // RFC 927
//OUTPUT_MARKING : 27, // RFC 933
//TERMINCAL_LOCATION_NUMBER : 28, // RFC 946
//TELNET_3270_REGIME : 29, // RFC 1041
WINDOW_SIZE : 31, // aka 'NAWS': RFC 1073 @ http://tools.ietf.org/html/rfc1073
TERMINAL_SPEED : 32, // RFC 1079 @ http://tools.ietf.org/html/rfc1079
REMOTE_FLOW_CONTROL : 33, // RFC 1072 @ http://tools.ietf.org/html/rfc1372
LINEMODE : 34, // RFC 1184 @ http://tools.ietf.org/html/rfc1184
X_DISPLAY_LOCATION : 35, // aka 'XDISPLOC': RFC 1096 @ http://tools.ietf.org/html/rfc1096
NEW_ENVIRONMENT_DEP : 36, // aka 'NEW-ENVIRON': RFC 1408 @ http://tools.ietf.org/html/rfc1408 (note: RFC 1572 is an update to this)
AUTHENTICATION : 37, // RFC 2941 @ http://tools.ietf.org/html/rfc2941
ENCRYPT : 38, // RFC 2946 @ http://tools.ietf.org/html/rfc2946
NEW_ENVIRONMENT : 39, // aka 'NEW-ENVIRON': RFC 1572 @ http://tools.ietf.org/html/rfc1572 (note: update to RFC 1408)
//TN3270E : 40, // RFC 2355
//XAUTH : 41,
//CHARSET : 42, // RFC 2066
//REMOTE_SERIAL_PORT : 43,
//COM_PORT_CONTROL : 44, // RFC 2217
//SUPRESS_LOCAL_ECHO : 45,
//START_TLS : 46,
//KERMIT : 47, // RFC 2840
//SEND_URL : 48,
//FORWARD_X : 49,
//PRAGMA_LOGON : 138,
//SSPI_LOGON : 139,
//PRAGMA_HEARTBEAT : 140
//PRAGMA_LOGON : 138,
//SSPI_LOGON : 139,
//PRAGMA_HEARTBEAT : 140
ARE_YOU_THERE : 246, // aka 'AYT' RFC 854 @ https://tools.ietf.org/html/rfc854
ARE_YOU_THERE : 246, // aka 'AYT' RFC 854 @ https://tools.ietf.org/html/rfc854
EXTENDED_OPTIONS_LIST : 255, // RFC 861 (STD 32)
EXTENDED_OPTIONS_LIST : 255, // RFC 861 (STD 32)
};
// Commands used within NEW_ENVIRONMENT[_DEP]
// Commands used within NEW_ENVIRONMENT[_DEP]
const NEW_ENVIRONMENT_COMMANDS = {
VAR : 0,
VALUE : 1,
ESC : 2,
USERVAR : 3,
VAR : 0,
VALUE : 1,
ESC : 2,
USERVAR : 3,
};
const IAC_BUF = Buffer.from([ COMMANDS.IAC ]);
const IAC_SE_BUF = Buffer.from([ COMMANDS.IAC, COMMANDS.SE ]);
const IAC_BUF = Buffer.from([ COMMANDS.IAC ]);
const IAC_SE_BUF = Buffer.from([ COMMANDS.IAC, COMMANDS.SE ]);
const COMMAND_NAMES = Object.keys(COMMANDS).reduce(function(names, name) {
names[COMMANDS[name]] = name.toLowerCase();
@ -178,9 +178,9 @@ const COMMAND_IMPLS = {};
};
});
// :TODO: See TooTallNate's telnet.js: Handle COMMAND_IMPL for IAC in binary mode
// :TODO: See TooTallNate's telnet.js: Handle COMMAND_IMPL for IAC in binary mode
// Create option names such as 'transmit binary' -> OPTIONS.TRANSMIT_BINARY
// Create option names such as 'transmit binary' -> OPTIONS.TRANSMIT_BINARY
const OPTION_NAMES = Object.keys(OPTIONS).reduce(function(names, name) {
names[OPTIONS[name]] = name.toLowerCase().replace(/_/g, ' ');
return names;
@ -193,19 +193,19 @@ function unknownOption(bufs, i, event) {
}
const OPTION_IMPLS = {};
// :TODO: fill in the rest...
OPTION_IMPLS.NO_ARGS =
OPTION_IMPLS[OPTIONS.ECHO] =
OPTION_IMPLS[OPTIONS.STATUS] =
OPTION_IMPLS[OPTIONS.LINEMODE] =
OPTION_IMPLS[OPTIONS.TRANSMIT_BINARY] =
OPTION_IMPLS[OPTIONS.AUTHENTICATION] =
OPTION_IMPLS[OPTIONS.TERMINAL_SPEED] =
OPTION_IMPLS[OPTIONS.REMOTE_FLOW_CONTROL] =
OPTION_IMPLS[OPTIONS.X_DISPLAY_LOCATION] =
OPTION_IMPLS[OPTIONS.SEND_LOCATION] =
OPTION_IMPLS[OPTIONS.ARE_YOU_THERE] =
OPTION_IMPLS[OPTIONS.SUPPRESS_GO_AHEAD] = function(bufs, i, event) {
// :TODO: fill in the rest...
OPTION_IMPLS.NO_ARGS =
OPTION_IMPLS[OPTIONS.ECHO] =
OPTION_IMPLS[OPTIONS.STATUS] =
OPTION_IMPLS[OPTIONS.LINEMODE] =
OPTION_IMPLS[OPTIONS.TRANSMIT_BINARY] =
OPTION_IMPLS[OPTIONS.AUTHENTICATION] =
OPTION_IMPLS[OPTIONS.TERMINAL_SPEED] =
OPTION_IMPLS[OPTIONS.REMOTE_FLOW_CONTROL] =
OPTION_IMPLS[OPTIONS.X_DISPLAY_LOCATION] =
OPTION_IMPLS[OPTIONS.SEND_LOCATION] =
OPTION_IMPLS[OPTIONS.ARE_YOU_THERE] =
OPTION_IMPLS[OPTIONS.SUPPRESS_GO_AHEAD] = function(bufs, i, event) {
event.buf = bufs.splice(0, i).toBuffer();
return event;
};
@ -214,12 +214,12 @@ OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) {
if(event.commandCode !== COMMANDS.SB) {
OPTION_IMPLS.NO_ARGS(bufs, i, event);
} else {
// We need 4 bytes header + data + IAC SE
// We need 4 bytes header + data + IAC SE
if(bufs.length < 7) {
return MORE_DATA_REQUIRED;
}
const end = bufs.indexOf(IAC_SE_BUF, 5); // look past header bytes
const end = bufs.indexOf(IAC_SE_BUF, 5); // look past header bytes
if(-1 === end) {
return MORE_DATA_REQUIRED;
}
@ -232,10 +232,10 @@ OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) {
.uint8('opt')
.uint8('is')
.array('ttype', {
type : 'uint8',
readUntil : b => 255 === b, // 255=COMMANDS.IAC
type : 'uint8',
readUntil : b => 255 === b, // 255=COMMANDS.IAC
})
// note we read iac2 above
// note we read iac2 above
.uint8('se')
.parse(bufs.toBuffer());
} catch(e) {
@ -248,10 +248,10 @@ OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) {
EnigAssert(OPTIONS.TERMINAL_TYPE === ttypeCmd.opt);
EnigAssert(SB_COMMANDS.IS === ttypeCmd.is);
EnigAssert(ttypeCmd.ttype.length > 0);
// note we found IAC_SE above
// note we found IAC_SE above
// some terminals such as NetRunner provide a NULL-terminated buffer
// slice to remove IAC
// some terminals such as NetRunner provide a NULL-terminated buffer
// slice to remove IAC
event.ttype = stringFromNullTermBuffer(ttypeCmd.ttype.slice(0, -1), 'ascii');
bufs.splice(0, end);
@ -264,7 +264,7 @@ OPTION_IMPLS[OPTIONS.WINDOW_SIZE] = function(bufs, i, event) {
if(event.commandCode !== COMMANDS.SB) {
OPTION_IMPLS.NO_ARGS(bufs, i, event);
} else {
// we need 9 bytes
// we need 9 bytes
if(bufs.length < 9) {
return MORE_DATA_REQUIRED;
}
@ -291,39 +291,39 @@ OPTION_IMPLS[OPTIONS.WINDOW_SIZE] = function(bufs, i, event) {
EnigAssert(COMMANDS.IAC === nawsCmd.iac2);
EnigAssert(COMMANDS.SE === nawsCmd.se);
event.cols = event.columns = event.width = nawsCmd.width;
event.rows = event.height = nawsCmd.height;
event.cols = event.columns = event.width = nawsCmd.width;
event.rows = event.height = nawsCmd.height;
}
return event;
};
// Build an array of delimiters for parsing NEW_ENVIRONMENT[_DEP]
// Build an array of delimiters for parsing NEW_ENVIRONMENT[_DEP]
const NEW_ENVIRONMENT_DELIMITERS = [];
Object.keys(NEW_ENVIRONMENT_COMMANDS).forEach(function onKey(k) {
NEW_ENVIRONMENT_DELIMITERS.push(NEW_ENVIRONMENT_COMMANDS[k]);
});
// Handle the deprecated RFC 1408 & the updated RFC 1572:
OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT_DEP] =
OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
// Handle the deprecated RFC 1408 & the updated RFC 1572:
OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT_DEP] =
OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
if(event.commandCode !== COMMANDS.SB) {
OPTION_IMPLS.NO_ARGS(bufs, i, event);
} else {
//
// We need 4 bytes header + <optional payload> + IAC SE
// Many terminals send a empty list:
// IAC SB NEW-ENVIRON IS IAC SE
// We need 4 bytes header + <optional payload> + IAC SE
// Many terminals send a empty list:
// IAC SB NEW-ENVIRON IS IAC SE
//
if(bufs.length < 6) {
return MORE_DATA_REQUIRED;
}
let end = bufs.indexOf(IAC_SE_BUF, 4); // look past header bytes
let end = bufs.indexOf(IAC_SE_BUF, 4); // look past header bytes
if(-1 === end) {
return MORE_DATA_REQUIRED;
}
// :TODO: It's likely that we could do all the env name/value parsing directly in Parser.
// :TODO: It's likely that we could do all the env name/value parsing directly in Parser.
let envCmd;
try {
@ -331,12 +331,12 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
.uint8('iac1')
.uint8('sb')
.uint8('opt')
.uint8('isOrInfo') // IS=initial, INFO=updates
.uint8('isOrInfo') // IS=initial, INFO=updates
.array('envBlock', {
type : 'uint8',
readUntil : b => 255 === b, // 255=COMMANDS.IAC
readUntil : b => 255 === b, // 255=COMMANDS.IAC
})
// note we consume IAC above
// note we consume IAC above
.uint8('se')
.parse(bufs.splice(0, bufs.length).toBuffer());
} catch(e) {
@ -350,34 +350,34 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
EnigAssert(SB_COMMANDS.IS === envCmd.isOrInfo || SB_COMMANDS.INFO === envCmd.isOrInfo);
if(OPTIONS.NEW_ENVIRONMENT_DEP === envCmd.opt) {
// :TODO: we should probably support this for legacy clients?
// :TODO: we should probably support this for legacy clients?
Log.warn('Handling deprecated RFC 1408 NEW-ENVIRON');
}
const envBuf = envCmd.envBlock.slice(0, -1); // remove IAC
const envBuf = envCmd.envBlock.slice(0, -1); // remove IAC
if(envBuf.length < 4) { // TYPE + single char name + sep + single char value
// empty env block
if(envBuf.length < 4) { // TYPE + single char name + sep + single char value
// empty env block
return event;
}
const States = {
Name : 1,
Value : 2,
Name : 1,
Value : 2,
};
let state = States.Name;
const setVars = {};
const delVars = [];
let varName;
// :TODO: handle ESC type!!!
// :TODO: handle ESC type!!!
while(envBuf.length) {
switch(state) {
case States.Name :
{
const type = parseInt(envBuf.splice(0, 1));
if(![ NEW_ENVIRONMENT_COMMANDS.VAR, NEW_ENVIRONMENT_COMMANDS.USERVAR, NEW_ENVIRONMENT_COMMANDS.ESC ].includes(type)) {
return event; // fail :(
return event; // fail :(
}
let nameEnd = envBuf.indexOf(NEW_ENVIRONMENT_COMMANDS.VALUE);
@ -387,7 +387,7 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
varName = envBuf.splice(0, nameEnd);
if(!varName) {
return event; // something is wrong.
return event; // something is wrong.
}
varName = Buffer.from(varName).toString('ascii');
@ -397,7 +397,7 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
state = States.Value;
} else {
state = States.Name;
delVars.push(varName); // no value; del this var
delVars.push(varName); // no value; del this var
}
}
break;
@ -423,15 +423,15 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
}
}
// :TODO: Handle deleting previously set vars via delVars
event.type = envCmd.isOrInfo;
event.envVars = setVars;
// :TODO: Handle deleting previously set vars via delVars
event.type = envCmd.isOrInfo;
event.envVars = setVars;
}
return event;
};
const MORE_DATA_REQUIRED = 0xfeedface;
const MORE_DATA_REQUIRED = 0xfeedface;
function parseBufs(bufs) {
EnigAssert(bufs.length >= 2);
@ -440,16 +440,16 @@ function parseBufs(bufs) {
}
function parseCommand(bufs, i, event) {
const command = bufs.get(i); // :TODO: fix deprecation... [i] is not the same
event.commandCode = command;
event.command = COMMAND_NAMES[command];
const command = bufs.get(i); // :TODO: fix deprecation... [i] is not the same
event.commandCode = command;
event.command = COMMAND_NAMES[command];
const handler = COMMAND_IMPLS[command];
if(handler) {
return handler(bufs, i + 1, event);
} else {
if(2 !== bufs.length) {
Log.warn( { bufsLength : bufs.length }, 'Expected bufs length of 2'); // expected: IAC + COMMAND
Log.warn( { bufsLength : bufs.length }, 'Expected bufs length of 2'); // expected: IAC + COMMAND
}
event.buf = bufs.splice(0, 2).toBuffer();
@ -458,9 +458,9 @@ function parseCommand(bufs, i, event) {
}
function parseOption(bufs, i, event) {
const option = bufs.get(i); // :TODO: fix deprecation... [i] is not the same
event.optionCode = option;
event.option = OPTION_NAMES[option];
const option = bufs.get(i); // :TODO: fix deprecation... [i] is not the same
event.optionCode = option;
event.option = OPTION_NAMES[option];
const handler = OPTION_IMPLS[option];
return handler ? handler(bufs, i + 1, event) : unknownOption(bufs, i + 1, event);
@ -470,20 +470,20 @@ function parseOption(bufs, i, event) {
function TelnetClient(input, output) {
baseClient.Client.apply(this, arguments);
const self = this;
const self = this;
let bufs = buffers();
this.bufs = bufs;
let bufs = buffers();
this.bufs = bufs;
this.sentDont = {}; // DON'T's we've already sent
this.sentDont = {}; // DON'T's we've already sent
this.setInputOutput(input, output);
this.negotiationsComplete = false; // are we in the 'negotiation' phase?
this.didReady = false; // have we emit the 'ready' event?
this.negotiationsComplete = false; // are we in the 'negotiation' phase?
this.didReady = false; // have we emit the 'ready' event?
this.subNegotiationState = {
newEnvironRequested : false,
newEnvironRequested : false,
};
this.dataHandler = function(b) {
@ -498,7 +498,7 @@ function TelnetClient(input, output) {
while((i = bufs.indexOf(IAC_BUF)) >= 0) {
//
// Some clients will send even IAC separate from data
// Some clients will send even IAC separate from data
//
if(bufs.length <= (i + 1)) {
i = MORE_DATA_REQUIRED;
@ -517,7 +517,7 @@ function TelnetClient(input, output) {
break;
} else if(i) {
if(i.option) {
self.emit(i.option, i); // "transmit binary", "echo", ...
self.emit(i.option, i); // "transmit binary", "echo", ...
}
self.handleTelnetEvent(i);
@ -530,8 +530,8 @@ function TelnetClient(input, output) {
if(MORE_DATA_REQUIRED !== i && bufs.length > 0) {
//
// Standard data payload. This can still be "non-user" data
// such as ANSI control, but we don't handle that here.
// Standard data payload. This can still be "non-user" data
// such as ANSI control, but we don't handle that here.
//
self.emit('data', bufs.splice(0).toBuffer());
}
@ -576,7 +576,7 @@ function TelnetClient(input, output) {
util.inherits(TelnetClient, baseClient.Client);
///////////////////////////////////////////////////////////////////////////////
// Telnet Command/Option handling
// Telnet Command/Option handling
///////////////////////////////////////////////////////////////////////////////
TelnetClient.prototype.handleTelnetEvent = function(evt) {
@ -584,14 +584,14 @@ TelnetClient.prototype.handleTelnetEvent = function(evt) {
return this.connectionWarn( { evt : evt }, 'No command for event');
}
// handler name e.g. 'handleWontCommand'
// handler name e.g. 'handleWontCommand'
const handlerName = `handle${evt.command.charAt(0).toUpperCase()}${evt.command.substr(1)}Command`;
if(this[handlerName]) {
// specialized
// specialized
this[handlerName](evt);
} else {
// generic-ish
// generic-ish
this.handleMiscCommand(evt);
}
};
@ -599,16 +599,16 @@ TelnetClient.prototype.handleTelnetEvent = function(evt) {
TelnetClient.prototype.handleWillCommand = function(evt) {
if('terminal type' === evt.option) {
//
// See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html
// See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html
//
this.requestTerminalType();
} else if('new environment' === evt.option) {
//
// See RFC 1572 @ http://www.faqs.org/rfcs/rfc1572.html
// See RFC 1572 @ http://www.faqs.org/rfcs/rfc1572.html
//
this.requestNewEnvironment();
} else {
// :TODO: temporary:
// :TODO: temporary:
this.connectionTrace(evt, 'WILL');
}
};
@ -628,20 +628,20 @@ TelnetClient.prototype.handleWontCommand = function(evt) {
};
TelnetClient.prototype.handleDoCommand = function(evt) {
// :TODO: handle the rest, e.g. echo nd the like
// :TODO: handle the rest, e.g. echo nd the like
if('linemode' === evt.option) {
//
// Client wants to enable linemode editing. Denied.
// Client wants to enable linemode editing. Denied.
//
this.wont.linemode();
} else if('encrypt' === evt.option) {
//
// Client wants to enable encryption. Denied.
// Client wants to enable encryption. Denied.
//
this.wont.encrypt();
} else {
// :TODO: temporary:
// :TODO: temporary:
this.connectionTrace(evt, 'DO');
}
};
@ -655,33 +655,33 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
if('terminal type' === evt.option) {
//
// See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html
// See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html
//
// :TODO: According to RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html
// We should keep asking until we see a repeat. From there, determine the best type/etc.
// :TODO: According to RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html
// We should keep asking until we see a repeat. From there, determine the best type/etc.
self.setTermType(evt.ttype);
self.negotiationsComplete = true; // :TODO: throw in a array of what we've taken care. Complete = array satisified or timeout
self.negotiationsComplete = true; // :TODO: throw in a array of what we've taken care. Complete = array satisified or timeout
self.readyNow();
} else if('new environment' === evt.option) {
//
// Handling is as follows:
// * Map 'TERM' -> 'termType' and only update if ours is 'unknown'
// * Map COLUMNS -> 'termWidth' and only update if ours is 0
// * Map ROWS -> 'termHeight' and only update if ours is 0
// * Add any new variables, ignore any existing
// Handling is as follows:
// * Map 'TERM' -> 'termType' and only update if ours is 'unknown'
// * Map COLUMNS -> 'termWidth' and only update if ours is 0
// * Map ROWS -> 'termHeight' and only update if ours is 0
// * Add any new variables, ignore any existing
//
Object.keys(evt.envVars || {} ).forEach(function onEnv(name) {
if('TERM' === name && 'unknown' === self.term.termType) {
self.setTermType(evt.envVars[name]);
} else if('COLUMNS' === name && 0 === self.term.termWidth) {
self.term.termWidth = parseInt(evt.envVars[name]);
self.clearMciCache(); // term size changes = invalidate cache
self.clearMciCache(); // term size changes = invalidate cache
self.connectionDebug({ termWidth : self.term.termWidth, source : 'NEW-ENVIRON'}, 'Window width updated');
} else if('ROWS' === name && 0 === self.term.termHeight) {
self.term.termHeight = parseInt(evt.envVars[name]);
self.clearMciCache(); // term size changes = invalidate cache
self.clearMciCache(); // term size changes = invalidate cache
self.connectionDebug({ termHeight : self.term.termHeight, source : 'NEW-ENVIRON'}, 'Window height updated');
} else {
if(name in self.term.env) {
@ -704,11 +704,11 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
} else if('window size' === evt.option) {
//
// Update termWidth & termHeight.
// Set LINES and COLUMNS environment variables as well.
// Update termWidth & termHeight.
// Set LINES and COLUMNS environment variables as well.
//
self.term.termWidth = evt.width;
self.term.termHeight = evt.height;
self.term.termWidth = evt.width;
self.term.termHeight = evt.height;
if(evt.width > 0) {
self.term.env.COLUMNS = evt.height;
@ -718,7 +718,7 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
self.term.env.ROWS = evt.height;
}
self.clearMciCache(); // term size changes = invalidate cache
self.clearMciCache(); // term size changes = invalidate cache
self.connectionDebug({ termWidth : evt.width , termHeight : evt.height, source : 'NAWS' }, 'Window size updated');
} else {
@ -736,11 +736,11 @@ TelnetClient.prototype.handleMiscCommand = function(evt) {
EnigAssert(evt.command !== 'undefined' && evt.command.length > 0);
//
// See:
// * RFC 854 @ http://tools.ietf.org/html/rfc854
// See:
// * RFC 854 @ http://tools.ietf.org/html/rfc854
//
if('ip' === evt.command) {
// Interrupt Process (IP)
// Interrupt Process (IP)
this.log.debug('Interrupt Process (IP) - Ending');
this.input.end();
@ -817,33 +817,33 @@ TelnetClient.prototype.banner = function() {
};
function Command(command, client) {
this.command = COMMANDS[command.toUpperCase()];
this.client = client;
this.command = COMMANDS[command.toUpperCase()];
this.client = client;
}
// Create Command objects with echo, transmit_binary, ...
// Create Command objects with echo, transmit_binary, ...
Object.keys(OPTIONS).forEach(function(name) {
const code = OPTIONS[name];
Command.prototype[name.toLowerCase()] = function() {
const buf = Buffer.alloc(3);
buf[0] = COMMANDS.IAC;
buf[1] = this.command;
buf[2] = code;
buf[0] = COMMANDS.IAC;
buf[1] = this.command;
buf[2] = code;
return this.client.output.write(buf);
};
});
// Create do, dont, etc. methods on Client
// Create do, dont, etc. methods on Client
['do', 'dont', 'will', 'wont'].forEach(function(command) {
const get = function() {
return new Command(command, this);
};
Object.defineProperty(TelnetClient.prototype, command, {
get : get,
enumerable : true,
configurable : true
get : get,
enumerable : true,
configurable : true
});
});
@ -861,9 +861,9 @@ exports.getModule = class TelnetServerModule extends LoginServerModule {
this.handleNewClient(client, sock, ModuleInfo);
//
// Set a timeout and attempt to proceed even if we don't know
// the term type yet, which is the preferred trigger
// for moving along
// Set a timeout and attempt to proceed even if we don't know
// the term type yet, which is the preferred trigger
// for moving along
//
setTimeout( () => {
if(!client.didReady) {