mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-03-09 15:40:18 +00:00
parent
f88d3063fe
commit
db06ec1975
37 changed files with 28174 additions and 44 deletions
540
rdp/protocol/t125/gcc.js
Normal file
540
rdp/protocol/t125/gcc.js
Normal file
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
var per = require('./per');
|
||||
|
||||
|
||||
var t124_02_98_oid = [ 0, 0, 20, 124, 0, 1 ];
|
||||
var h221_cs_key = "Duca";
|
||||
var h221_sc_key = "McDn";
|
||||
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240509.aspx
|
||||
*/
|
||||
var MessageType = {
|
||||
//server -> client
|
||||
SC_CORE : 0x0C01,
|
||||
SC_SECURITY : 0x0C02,
|
||||
SC_NET : 0x0C03,
|
||||
//client -> server
|
||||
CS_CORE : 0xC001,
|
||||
CS_SECURITY : 0xC002,
|
||||
CS_NET : 0xC003,
|
||||
CS_CLUSTER : 0xC004,
|
||||
CS_MONITOR : 0xC005
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var ColorDepth = {
|
||||
RNS_UD_COLOR_8BPP : 0xCA01,
|
||||
RNS_UD_COLOR_16BPP_555 : 0xCA02,
|
||||
RNS_UD_COLOR_16BPP_565 : 0xCA03,
|
||||
RNS_UD_COLOR_24BPP : 0xCA04
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var HighColor = {
|
||||
HIGH_COLOR_4BPP : 0x0004,
|
||||
HIGH_COLOR_8BPP : 0x0008,
|
||||
HIGH_COLOR_15BPP : 0x000f,
|
||||
HIGH_COLOR_16BPP : 0x0010,
|
||||
HIGH_COLOR_24BPP : 0x0018
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var Support = {
|
||||
RNS_UD_24BPP_SUPPORT : 0x0001,
|
||||
RNS_UD_16BPP_SUPPORT : 0x0002,
|
||||
RNS_UD_15BPP_SUPPORT : 0x0004,
|
||||
RNS_UD_32BPP_SUPPORT : 0x0008
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var CapabilityFlag = {
|
||||
RNS_UD_CS_SUPPORT_ERRINFO_PDU : 0x0001,
|
||||
RNS_UD_CS_WANT_32BPP_SESSION : 0x0002,
|
||||
RNS_UD_CS_SUPPORT_STATUSINFO_PDU : 0x0004,
|
||||
RNS_UD_CS_STRONG_ASYMMETRIC_KEYS : 0x0008,
|
||||
RNS_UD_CS_UNUSED : 0x0010,
|
||||
RNS_UD_CS_VALID_CONNECTION_TYPE : 0x0020,
|
||||
RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU : 0x0040,
|
||||
RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT : 0x0080,
|
||||
RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL : 0x0100,
|
||||
RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE : 0x0200,
|
||||
RNS_UD_CS_SUPPORT_HEARTBEAT_PDU : 0x0400
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var ConnectionType = {
|
||||
CONNECTION_TYPE_MODEM : 0x01,
|
||||
CONNECTION_TYPE_BROADBAND_LOW : 0x02,
|
||||
CONNECTION_TYPE_SATELLITE : 0x03,
|
||||
CONNECTION_TYPE_BROADBAND_HIGH : 0x04,
|
||||
CONNECTION_TYPE_WAN : 0x05,
|
||||
CONNECTION_TYPE_LAN : 0x06,
|
||||
CONNECTION_TYPE_AUTODETECT : 0x07
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var VERSION = {
|
||||
RDP_VERSION_4 : 0x00080001,
|
||||
RDP_VERSION_5_PLUS : 0x00080004
|
||||
};
|
||||
|
||||
var Sequence = {
|
||||
RNS_UD_SAS_DEL : 0xAA03
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
*/
|
||||
var EncryptionMethod = {
|
||||
ENCRYPTION_FLAG_40BIT : 0x00000001,
|
||||
ENCRYPTION_FLAG_128BIT : 0x00000002,
|
||||
ENCRYPTION_FLAG_56BIT : 0x00000008,
|
||||
FIPS_ENCRYPTION_FLAG : 0x00000010
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
*/
|
||||
var EncryptionLevel = {
|
||||
ENCRYPTION_LEVEL_NONE : 0x00000000,
|
||||
ENCRYPTION_LEVEL_LOW : 0x00000001,
|
||||
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE : 0x00000002,
|
||||
ENCRYPTION_LEVEL_HIGH : 0x00000003,
|
||||
ENCRYPTION_LEVEL_FIPS : 0x00000004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240513.aspx
|
||||
*/
|
||||
var ChannelOptions = {
|
||||
CHANNEL_OPTION_INITIALIZED : 0x80000000,
|
||||
CHANNEL_OPTION_ENCRYPT_RDP : 0x40000000,
|
||||
CHANNEL_OPTION_ENCRYPT_SC : 0x20000000,
|
||||
CHANNEL_OPTION_ENCRYPT_CS : 0x10000000,
|
||||
CHANNEL_OPTION_PRI_HIGH : 0x08000000,
|
||||
CHANNEL_OPTION_PRI_MED : 0x04000000,
|
||||
CHANNEL_OPTION_PRI_LOW : 0x02000000,
|
||||
CHANNEL_OPTION_COMPRESS_RDP : 0x00800000,
|
||||
CHANNEL_OPTION_COMPRESS : 0x00400000,
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL : 0x00200000,
|
||||
REMOTE_CONTROL_PERSISTENT : 0x00100000
|
||||
};
|
||||
|
||||
/**
|
||||
* IBM_101_102_KEYS is the most common keyboard type
|
||||
*/
|
||||
var KeyboardType = {
|
||||
IBM_PC_XT_83_KEY : 0x00000001,
|
||||
OLIVETTI : 0x00000002,
|
||||
IBM_PC_AT_84_KEY : 0x00000003,
|
||||
IBM_101_102_KEYS : 0x00000004,
|
||||
NOKIA_1050 : 0x00000005,
|
||||
NOKIA_9140 : 0x00000006,
|
||||
JAPANESE : 0x00000007
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx
|
||||
*/
|
||||
var KeyboardLayout = {
|
||||
ARABIC : 0x00000401,
|
||||
BULGARIAN : 0x00000402,
|
||||
CHINESE_US_KEYBOARD : 0x00000404,
|
||||
CZECH : 0x00000405,
|
||||
DANISH : 0x00000406,
|
||||
GERMAN : 0x00000407,
|
||||
GREEK : 0x00000408,
|
||||
US : 0x00000409,
|
||||
SPANISH : 0x0000040a,
|
||||
FINNISH : 0x0000040b,
|
||||
FRENCH : 0x0000040c,
|
||||
HEBREW : 0x0000040d,
|
||||
HUNGARIAN : 0x0000040e,
|
||||
ICELANDIC : 0x0000040f,
|
||||
ITALIAN : 0x00000410,
|
||||
JAPANESE : 0x00000411,
|
||||
KOREAN : 0x00000412,
|
||||
DUTCH : 0x00000413,
|
||||
NORWEGIAN : 0x00000414
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
*/
|
||||
var CertificateType = {
|
||||
CERT_CHAIN_VERSION_1 : 0x00000001,
|
||||
CERT_CHAIN_VERSION_2 : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {type.Type} data
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function block(data) {
|
||||
var self = {
|
||||
// type of data block
|
||||
type : new type.UInt16Le(function() {
|
||||
return self.data.obj.__TYPE__;
|
||||
}),
|
||||
// length of entire packet
|
||||
length : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
// data block
|
||||
data : data || new type.Factory(function(s){
|
||||
var options = {
|
||||
readLength : new type.CallableValue( function () {
|
||||
return self.length.value - 4;
|
||||
})
|
||||
};
|
||||
switch(self.type.value) {
|
||||
case MessageType.SC_CORE:
|
||||
self.data = serverCoreData(options).read(s);
|
||||
break;
|
||||
case MessageType.SC_SECURITY:
|
||||
self.data = serverSecurityData(options).read(s);
|
||||
break;
|
||||
case MessageType.SC_NET:
|
||||
self.data = serverNetworkData(null, options).read(s);
|
||||
break;
|
||||
case MessageType.CS_CORE:
|
||||
self.data = clientCoreData(options).read(s);
|
||||
break;
|
||||
case MessageType.CS_SECURITY:
|
||||
self.data = clientSecurityData(options).read(s);
|
||||
break;
|
||||
case MessageType.CS_NET:
|
||||
self.data = clientNetworkData(null, options).read(s);
|
||||
break;
|
||||
default:
|
||||
log.debug("unknown gcc block type " + self.type.value);
|
||||
self.data = new type.BinaryString(null, options).read(s);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main client informations
|
||||
* keyboard
|
||||
* screen definition
|
||||
* color depth
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientCoreData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_CORE,
|
||||
rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS),
|
||||
desktopWidth : new type.UInt16Le(1280),
|
||||
desktopHeight : new type.UInt16Le(800),
|
||||
colorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP),
|
||||
sasSequence : new type.UInt16Le(Sequence.RNS_UD_SAS_DEL),
|
||||
kbdLayout : new type.UInt32Le(KeyboardLayout.FRENCH),
|
||||
clientBuild : new type.UInt32Le(3790),
|
||||
clientName : new type.BinaryString(Buffer.from('node-rdpjs\x00\x00\x00\x00\x00\x00', 'ucs2'), { readLength : new type.CallableValue(32) }),
|
||||
keyboardType : new type.UInt32Le(KeyboardType.IBM_101_102_KEYS),
|
||||
keyboardSubType : new type.UInt32Le(0),
|
||||
keyboardFnKeys : new type.UInt32Le(12),
|
||||
imeFileName : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00')), { readLength : new type.CallableValue(64), optional : true }),
|
||||
postBeta2ColorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, { optional : true }),
|
||||
clientProductId : new type.UInt16Le(1, { optional : true }),
|
||||
serialNumber : new type.UInt32Le(0, { optional : true }),
|
||||
highColorDepth : new type.UInt16Le(HighColor.HIGH_COLOR_24BPP, { optional : true }),
|
||||
supportedColorDepths : new type.UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, { optional : true }),
|
||||
earlyCapabilityFlags : new type.UInt16Le(CapabilityFlag.RNS_UD_CS_SUPPORT_ERRINFO_PDU, { optional : true }),
|
||||
clientDigProductId : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00')), { optional : true, readLength : new type.CallableValue(64) }),
|
||||
connectionType : new type.UInt8(0, { optional : true }),
|
||||
pad1octet : new type.UInt8(0, { optional : true }),
|
||||
serverSelectedProtocol : new type.UInt32Le(0, { optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240517.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverCoreData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_CORE,
|
||||
rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS),
|
||||
clientRequestedProtocol : new type.UInt32Le(null, { optional : true }),
|
||||
earlyCapabilityFlags : new type.UInt32Le(null, { optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientSecurityData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_SECURITY,
|
||||
encryptionMethods : new type.UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT),
|
||||
extEncryptionMethods : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use for SSL (RDP security layer TODO)
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverSecurityData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_SECURITY,
|
||||
encryptionMethod : new type.UInt32Le(),
|
||||
encryptionLevel : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel definition
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function channelDef (opt) {
|
||||
var self = {
|
||||
name : new type.BinaryString(null, { readLength : new type.CallableValue(8) }),
|
||||
options : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional channel requests (sound, clipboard ...)
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientNetworkData(channelDefArray, opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_NET,
|
||||
channelCount : new type.UInt32Le( function () {
|
||||
return self.channelDefArray.obj.length;
|
||||
}),
|
||||
channelDefArray : channelDefArray || new type.Factory( function (s) {
|
||||
self.channelDefArray = new type.Component([]);
|
||||
|
||||
for (var i = 0; i < self.channelCount.value; i++) {
|
||||
self.channelDefArray.obj.push(channelDef().read(s));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param channelIds {type.Component} list of available channels
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverNetworkData (channelIds, opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_NET,
|
||||
MCSChannelId : new type.UInt16Le(1003, { constant : true }),
|
||||
channelCount : new type.UInt16Le(function () {
|
||||
return self.channelIdArray.obj.length;
|
||||
}),
|
||||
channelIdArray : channelIds || new type.Factory( function (s) {
|
||||
self.channelIdArray = new type.Component([]);
|
||||
for (var i = 0; i < self.channelCount.value; i++) {
|
||||
self.channelIdArray.obj.push(new type.UInt16Le().read(s));
|
||||
}
|
||||
}),
|
||||
pad : new type.UInt16Le(null, { conditional : function () {
|
||||
return (self.channelCount.value % 2) === 1;
|
||||
}})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client or server GCC settings block
|
||||
* @param blocks {type.Component} array of gcc blocks
|
||||
* @param opt {object} options to component type
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function settings(blocks, opt) {
|
||||
var self = {
|
||||
blocks : blocks || new type.Factory(function(s) {
|
||||
self.blocks = new type.Component([]);
|
||||
// read until end of stream
|
||||
while(s.availableLength() > 0) {
|
||||
self.blocks.obj.push(block().read(s));
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read GCC response from server
|
||||
* @param s {type.Stream} current stream
|
||||
* @returns {Array(type.Component)} list of server block
|
||||
*/
|
||||
function readConferenceCreateResponse(s) {
|
||||
per.readChoice(s);
|
||||
|
||||
if(!per.readObjectIdentifier(s, t124_02_98_oid)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124');
|
||||
}
|
||||
|
||||
per.readLength(s);
|
||||
per.readChoice(s);
|
||||
per.readInteger16(s, 1001);
|
||||
per.readInteger(s);
|
||||
per.readEnumerates(s);
|
||||
per.readNumberOfSet(s);
|
||||
per.readChoice(s);
|
||||
|
||||
if (!per.readOctetStream(s, h221_sc_key, 4)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY');
|
||||
}
|
||||
|
||||
length = per.readLength(s);
|
||||
serverSettings = settings(null, { readLength : new type.CallableValue(length) });
|
||||
|
||||
// Object magic
|
||||
return serverSettings.read(s).obj.blocks.obj.map(function(e) {
|
||||
return e.obj.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Read GCC request
|
||||
* @param s {type.Stream}
|
||||
* @returns {Array(type.Component)} list of client block
|
||||
*/
|
||||
function readConferenceCreateRequest (s) {
|
||||
per.readChoice(s);
|
||||
if (!per.readObjectIdentifier(s, t124_02_98_oid)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY');
|
||||
}
|
||||
per.readLength(s);
|
||||
per.readChoice(s);
|
||||
per.readSelection(s);
|
||||
per.readNumericString(s, 1);
|
||||
per.readPadding(s, 1);
|
||||
|
||||
if (per.readNumberOfSet(s) !== 1) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_SET');
|
||||
}
|
||||
|
||||
if (per.readChoice(s) !== 0xc0) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_CHOICE');
|
||||
}
|
||||
|
||||
per.readOctetStream(s, h221_cs_key, 4);
|
||||
|
||||
length = per.readLength(s);
|
||||
var clientSettings = settings(null, { readLength : new type.CallableValue(length) });
|
||||
|
||||
// Object magic
|
||||
return clientSettings.read(s).obj.blocks.obj.map(function(e) {
|
||||
return e.obj.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Built {type.Componen} from gcc user data
|
||||
* @param userData {type.Component} GCC data from client
|
||||
* @returns {type.Component} GCC encoded client user data
|
||||
*/
|
||||
function writeConferenceCreateRequest (userData) {
|
||||
var userDataStream = new type.Stream(userData.size());
|
||||
userData.write(userDataStream);
|
||||
|
||||
return new type.Component([
|
||||
per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(userData.size() + 14), per.writeChoice(0),
|
||||
per.writeSelection(0x08), per.writeNumericString("1", 1), per.writePadding(1),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(Buffer.from(h221_cs_key), 4), per.writeOctetStream(userDataStream.getValue())
|
||||
]);
|
||||
}
|
||||
|
||||
function writeConferenceCreateResponse (userData) {
|
||||
var userDataStream = new type.Stream(userData.size());
|
||||
userData.write(userDataStream);
|
||||
|
||||
return new type.Component([
|
||||
per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(userData.size() + 14), per.writeChoice(0x14),
|
||||
per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(0),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(Buffer.from(h221_sc_key), 4), per.writeOctetStream(userDataStream.getValue())
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
MessageType : MessageType,
|
||||
VERSION : VERSION,
|
||||
KeyboardLayout : KeyboardLayout,
|
||||
block : block,
|
||||
clientCoreData : clientCoreData,
|
||||
clientNetworkData : clientNetworkData,
|
||||
clientSecurityData : clientSecurityData,
|
||||
serverCoreData : serverCoreData,
|
||||
serverSecurityData : serverSecurityData,
|
||||
serverNetworkData : serverNetworkData,
|
||||
readConferenceCreateResponse : readConferenceCreateResponse,
|
||||
readConferenceCreateRequest : readConferenceCreateRequest,
|
||||
writeConferenceCreateRequest : writeConferenceCreateRequest,
|
||||
writeConferenceCreateResponse : writeConferenceCreateResponse
|
||||
};
|
26
rdp/protocol/t125/index.js
Normal file
26
rdp/protocol/t125/index.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var mcs = require('./mcs');
|
||||
var gcc = require('./gcc');
|
||||
|
||||
module.exports = {
|
||||
mcs : mcs,
|
||||
gcc : gcc
|
||||
};
|
490
rdp/protocol/t125/mcs.js
Normal file
490
rdp/protocol/t125/mcs.js
Normal file
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
var gcc = require('./gcc');
|
||||
var per = require('./per');
|
||||
var asn1 = require('../../asn1');
|
||||
|
||||
var Message = {
|
||||
MCS_TYPE_CONNECT_INITIAL : 0x65,
|
||||
MCS_TYPE_CONNECT_RESPONSE : 0x66
|
||||
};
|
||||
|
||||
var DomainMCSPDU = {
|
||||
ERECT_DOMAIN_REQUEST : 1,
|
||||
DISCONNECT_PROVIDER_ULTIMATUM : 8,
|
||||
ATTACH_USER_REQUEST : 10,
|
||||
ATTACH_USER_CONFIRM : 11,
|
||||
CHANNEL_JOIN_REQUEST : 14,
|
||||
CHANNEL_JOIN_CONFIRM : 15,
|
||||
SEND_DATA_REQUEST : 25,
|
||||
SEND_DATA_INDICATION : 26
|
||||
};
|
||||
|
||||
var Channel = {
|
||||
MCS_GLOBAL_CHANNEL : 1003,
|
||||
MCS_USERCHANNEL_BASE : 1001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function DomainParameters(maxChannelIds, maxUserIds, maxTokenIds,
|
||||
numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion) {
|
||||
return new asn1.univ.Sequence({
|
||||
maxChannelIds : new asn1.univ.Integer(maxChannelIds),
|
||||
maxUserIds : new asn1.univ.Integer(maxUserIds),
|
||||
maxTokenIds : new asn1.univ.Integer(maxTokenIds),
|
||||
numPriorities : new asn1.univ.Integer(numPriorities),
|
||||
minThoughput : new asn1.univ.Integer(minThoughput),
|
||||
maxHeight : new asn1.univ.Integer(maxHeight),
|
||||
maxMCSPDUsize : new asn1.univ.Integer(maxMCSPDUsize),
|
||||
protocolVersion : new asn1.univ.Integer(protocolVersion)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @param userData {Buffer}
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function ConnectInitial (userData) {
|
||||
return new asn1.univ.Sequence({
|
||||
callingDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
|
||||
calledDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
|
||||
upwardFlag : new asn1.univ.Boolean(true),
|
||||
targetParameters : DomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2),
|
||||
minimumParameters : DomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2),
|
||||
maximumParameters : DomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2),
|
||||
userData : new asn1.univ.OctetString(userData)
|
||||
}).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 101));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function ConnectResponse (userData) {
|
||||
return new asn1.univ.Sequence({
|
||||
result : new asn1.univ.Enumerate(0),
|
||||
calledConnectId : new asn1.univ.Integer(0),
|
||||
domainParameters : DomainParameters(22, 3, 0, 1, 0, 1,0xfff8, 2),
|
||||
userData : new asn1.univ.OctetString(userData)
|
||||
}).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 102));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format MCS PDU header packet
|
||||
* @param mcsPdu {integer}
|
||||
* @param options {integer}
|
||||
* @returns {type.UInt8} headers
|
||||
*/
|
||||
function writeMCSPDUHeader(mcsPdu, options) {
|
||||
options = options || 0;
|
||||
return new type.UInt8((mcsPdu << 2) | options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read MCS PDU header
|
||||
* @param opcode
|
||||
* @param mcsPdu
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function readMCSPDUHeader(opcode, mcsPdu) {
|
||||
return (opcode >> 2) === mcsPdu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-Channel Services
|
||||
* @param transport {events.EventEmitter} transport layer listen (connect, data) events
|
||||
* @param recvOpCode {DomainMCSPDU} opcode use in receive automata
|
||||
* @param sendOpCode {DomainMCSPDU} opcode use to send message
|
||||
*/
|
||||
function MCS(transport, recvOpCode, sendOpCode) {
|
||||
this.transport = transport;
|
||||
this.recvOpCode = recvOpCode;
|
||||
this.sendOpCode = sendOpCode;
|
||||
this.channels = [{id : Channel.MCS_GLOBAL_CHANNEL, name : 'global'}];
|
||||
this.channels.find = function(callback) {
|
||||
for(var i in this) {
|
||||
if(callback(this[i])) return this[i];
|
||||
};
|
||||
};
|
||||
|
||||
// bind events
|
||||
var self = this;
|
||||
this.transport.on('close', function () {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(MCS, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send message to a specific channel
|
||||
* @param channelName {string} name of channel
|
||||
* @param data {type.*} message to send
|
||||
*/
|
||||
MCS.prototype.send = function(channelName, data) {
|
||||
var channelId = this.channels.find(function(element) {
|
||||
if (element.name === channelName) return true;
|
||||
}).id;
|
||||
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(this.sendOpCode),
|
||||
per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
new type.UInt8(0x70),
|
||||
per.writeLength(data.size()),
|
||||
data
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Main receive function
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
MCS.prototype.recv = function(s) {
|
||||
opcode = new type.UInt8().read(s).value;
|
||||
|
||||
if (readMCSPDUHeader(opcode, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM)) {
|
||||
log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM");
|
||||
this.transport.close();
|
||||
return
|
||||
}
|
||||
else if(!readMCSPDUHeader(opcode, this.recvOpCode)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_RECEIVE_OPCODE');
|
||||
}
|
||||
|
||||
per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
|
||||
var channelId = per.readInteger16(s);
|
||||
|
||||
per.readEnumerates(s);
|
||||
per.readLength(s);
|
||||
|
||||
var channelName = this.channels.find(function(e) {
|
||||
if (e.id === channelId) return true;
|
||||
}).name;
|
||||
|
||||
this.emit(channelName, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Only main channels handle actually
|
||||
* @param transport {event.EventEmitter} bind connect and data events
|
||||
* @returns
|
||||
*/
|
||||
function Client(transport) {
|
||||
MCS.call(this, transport, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST);
|
||||
|
||||
// channel context automata
|
||||
this.channelsConnected = 0;
|
||||
|
||||
// init gcc information
|
||||
this.clientCoreData = gcc.clientCoreData();
|
||||
this.clientNetworkData = gcc.clientNetworkData(new type.Component([]));
|
||||
this.clientSecurityData = gcc.clientSecurityData();
|
||||
|
||||
// must be readed from protocol
|
||||
this.serverCoreData = null;
|
||||
this.serverSecurityData = null;
|
||||
this.serverNetworkData = null;
|
||||
|
||||
var self = this;
|
||||
this.transport.on('connect', function(s) {
|
||||
self.connect(s);
|
||||
});
|
||||
}
|
||||
|
||||
inherits(Client, MCS);
|
||||
|
||||
/**
|
||||
* Connect event layer
|
||||
*/
|
||||
Client.prototype.connect = function(selectedProtocol) {
|
||||
this.clientCoreData.obj.serverSelectedProtocol.value = selectedProtocol;
|
||||
this.sendConnectInitial();
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connect response (server->client)
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvConnectResponse = function(s) {
|
||||
var userData = new type.Stream(ConnectResponse().decode(s, asn1.ber).value.userData.value);
|
||||
var serverSettings = gcc.readConferenceCreateResponse(userData);
|
||||
// record server gcc block
|
||||
for(var i in serverSettings) {
|
||||
if(!serverSettings[i].obj) {
|
||||
continue;
|
||||
}
|
||||
switch(serverSettings[i].obj.__TYPE__) {
|
||||
case gcc.MessageType.SC_CORE:
|
||||
this.serverCoreData = serverSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.SC_SECURITY:
|
||||
this.serverSecurityData = serverSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.SC_NET:
|
||||
this.serverNetworkData = serverSettings[i];
|
||||
break;
|
||||
default:
|
||||
log.warn('unhandle server gcc block : ' + serverSettings[i].obj.__TYPE__);
|
||||
}
|
||||
}
|
||||
|
||||
// send domain request
|
||||
this.sendErectDomainRequest();
|
||||
// send attach user request
|
||||
this.sendAttachUserRequest();
|
||||
// now wait user confirm from server
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvAttachUserConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvAttachUserConfirm = function(s) {
|
||||
if (!readMCSPDUHeader(new type.UInt8().read(s).value, DomainMCSPDU.ATTACH_USER_CONFIRM)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER');
|
||||
}
|
||||
|
||||
if (per.readEnumerates(s) !== 0) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER');
|
||||
}
|
||||
|
||||
this.userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
//ask channel for specific user
|
||||
this.channels.push({ id : this.userId, name : 'user' });
|
||||
// channel connect automata
|
||||
this.connectChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
* Last state in channel connection automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvChannelJoinConfirm = function(s) {
|
||||
var opcode = new type.UInt8().read(s).value;
|
||||
|
||||
if (!readMCSPDUHeader(opcode, DomainMCSPDU.CHANNEL_JOIN_CONFIRM)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM');
|
||||
}
|
||||
|
||||
var confirm = per.readEnumerates(s);
|
||||
|
||||
var userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
if (this.userId !== userId) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID');
|
||||
}
|
||||
|
||||
var channelId = per.readInteger16(s);
|
||||
|
||||
if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
|
||||
}
|
||||
|
||||
this.connectChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
* First MCS message
|
||||
*/
|
||||
Client.prototype.sendConnectInitial = function() {
|
||||
|
||||
var ccReq = gcc.writeConferenceCreateRequest(new type.Component([
|
||||
gcc.block(this.clientCoreData),
|
||||
gcc.block(this.clientNetworkData),
|
||||
gcc.block(this.clientSecurityData)
|
||||
])).toStream().getValue();
|
||||
|
||||
this.transport.send(ConnectInitial(ccReq).encode(asn1.ber));
|
||||
|
||||
// next event is connect response
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvConnectResponse(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
*/
|
||||
Client.prototype.sendErectDomainRequest = function() {
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST),
|
||||
per.writeInteger(0),
|
||||
per.writeInteger(0)
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
*/
|
||||
Client.prototype.sendAttachUserRequest = function() {
|
||||
this.transport.send(writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a channel join request
|
||||
* @param channelId {integer} channel id
|
||||
*/
|
||||
Client.prototype.sendChannelJoinRequest = function(channelId) {
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST),
|
||||
per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId)
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect channels automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.connectChannels = function(s) {
|
||||
if(this.channelsConnected == this.channels.length) {
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recv(s);
|
||||
});
|
||||
|
||||
// send client and sever gcc informations
|
||||
this.emit('connect',
|
||||
{
|
||||
core : this.clientCoreData.obj,
|
||||
security : this.clientSecurityData.obj,
|
||||
net : this.clientNetworkData.obj
|
||||
},
|
||||
{
|
||||
core : this.serverCoreData.obj,
|
||||
security : this.serverSecurityData.obj
|
||||
}, this.userId, this.channels);
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendChannelJoinRequest(this.channels[this.channelsConnected++].id);
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvChannelJoinConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Server side of MCS layer
|
||||
* @param transport
|
||||
*/
|
||||
function Server (transport) {
|
||||
MCS.call(this, transport, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION);
|
||||
|
||||
// must be readed from protocol
|
||||
this.clientCoreData = null;
|
||||
this.clientNetworkData = null;
|
||||
this.clientSecurityData = null;
|
||||
|
||||
// init gcc information
|
||||
this.serverCoreData = gcc.serverCoreData();
|
||||
this.serverSecurityData = gcc.serverSecurityData();
|
||||
this.serverNetworkData = gcc.serverNetworkData(new type.Component([]));
|
||||
|
||||
var self = this;
|
||||
this.transport.on('connect', function (selectedProtocol) {
|
||||
self.serverCoreData.obj.clientRequestedProtocol.value = selectedProtocol;
|
||||
}).once('data', function (s) {
|
||||
self.recvConnectInitial(s);
|
||||
});
|
||||
}
|
||||
|
||||
inherits(Server, MCS);
|
||||
|
||||
/**
|
||||
* First state of server automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Server.prototype.recvConnectInitial = function (s) {
|
||||
var userData = new type.Stream(ConnectInitial().decode(s, asn1.ber).value.userData.value);
|
||||
var clientSettings = gcc.readConferenceCreateRequest(userData);
|
||||
// record server gcc block
|
||||
for(var i in clientSettings) {
|
||||
if(!clientSettings[i].obj) {
|
||||
continue;
|
||||
}
|
||||
switch(clientSettings[i].obj.__TYPE__) {
|
||||
case gcc.MessageType.CS_CORE:
|
||||
this.clientCoreData = clientSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.CS_SECURITY:
|
||||
this.clientSecurityData = clientSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.CS_NET:
|
||||
this.clientNetworkData = clientSettings[i];
|
||||
for (var i = 0; i < this.clientNetworkData.obj.channelCount.value; i++) {
|
||||
this.serverNetworkData.obj.channelIdArray.obj.push(new type.UInt16Le( i + 1 + Channel.MCS_GLOBAL_CHANNEL));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.debug('unhandle client gcc block : ' + clientSettings[i].obj.__TYPE__);
|
||||
}
|
||||
}
|
||||
|
||||
this.sendConnectResponse();
|
||||
};
|
||||
|
||||
/**
|
||||
* State 2 in mcs server connetion automata
|
||||
*/
|
||||
Server.prototype.sendConnectResponse = function () {
|
||||
var ccReq = gcc.writeConferenceCreateResponse(new type.Component([
|
||||
gcc.block(this.serverCoreData),
|
||||
gcc.block(this.serverSecurityData),
|
||||
gcc.block(this.serverNetworkData),
|
||||
])).toStream().getValue();
|
||||
|
||||
this.transport.send(ConnectResponse(ccReq).encode(asn1.ber));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client,
|
||||
Server : Server
|
||||
};
|
338
rdp/protocol/t125/per.js
Normal file
338
rdp/protocol/t125/per.js
Normal file
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var error = require('../../core').error;
|
||||
|
||||
/**
|
||||
* @param s {type.Stream} read value from stream
|
||||
* @returns read length from per format
|
||||
*/
|
||||
function readLength(s) {
|
||||
var byte = new type.UInt8().read(s).value;
|
||||
var size = 0;
|
||||
if(byte & 0x80) {
|
||||
byte &= ~0x80;
|
||||
size = byte << 8;
|
||||
size += new type.UInt8().read(s).value;
|
||||
}
|
||||
else {
|
||||
size = byte;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {raw} value to convert to per format
|
||||
* @returns type objects per encoding value
|
||||
*/
|
||||
function writeLength(value) {
|
||||
if(value > 0x7f) {
|
||||
return new type.UInt16Be(value | 0x8000);
|
||||
}
|
||||
else {
|
||||
return new type.UInt8(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} choice decoding from per encoding
|
||||
*/
|
||||
function readChoice(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param choice {integer}
|
||||
* @returns {type.UInt8} choice per encoded
|
||||
*/
|
||||
function writeChoice(choice) {
|
||||
return new type.UInt8(choice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} number represent selection
|
||||
*/
|
||||
function readSelection(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selection {integer}
|
||||
* @returns {type.UInt8} per encoded selection
|
||||
*/
|
||||
function writeSelection(selection) {
|
||||
return new type.UInt8(selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} number of sets
|
||||
*/
|
||||
function readNumberOfSet(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numberOfSet {integer}
|
||||
* @returns {type.UInt8} per encoded nuimber of sets
|
||||
*/
|
||||
function writeNumberOfSet(numberOfSet) {
|
||||
return new type.UInt8(numberOfSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} enumerates number
|
||||
*/
|
||||
function readEnumerates(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enumerate {integer}
|
||||
* @returns {type.UInt8} per encoded enumerate
|
||||
*/
|
||||
function writeEnumerates(enumerate) {
|
||||
return new type.UInt8(enumerate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} integer per decoded
|
||||
*/
|
||||
function readInteger(s) {
|
||||
var result;
|
||||
var size = readLength(s);
|
||||
switch(size) {
|
||||
case 1:
|
||||
result = new type.UInt8();
|
||||
break;
|
||||
case 2:
|
||||
result = new type.UInt16Be();
|
||||
break;
|
||||
case 4:
|
||||
result = new type.UInt32Be();
|
||||
break;
|
||||
default:
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_PER_BAD_INTEGER_LENGTH');
|
||||
}
|
||||
return result.read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {integer}
|
||||
* @returns {type.Component} per encoded integer
|
||||
*/
|
||||
function writeInteger(value) {
|
||||
if(value <= 0xff) {
|
||||
return new type.Component([writeLength(1), new type.UInt8(value)]);
|
||||
}
|
||||
else if(value < 0xffff) {
|
||||
return new type.Component([writeLength(2), new type.UInt16Be(value)]);
|
||||
}
|
||||
else {
|
||||
return new type.Component([writeLength(4), new type.UInt32Be(value)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param minimum {integer} increment (default 0)
|
||||
* @returns {integer} per decoded integer 16 bits
|
||||
*/
|
||||
function readInteger16(s, minimum) {
|
||||
return new type.UInt16Be().read(s).value + (minimum || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {integer}
|
||||
* @param minimum {integer} decrement (default 0)
|
||||
* @returns {type.UInt16Be} per encoded integer 16 bits
|
||||
*/
|
||||
function writeInteger16(value, minimum) {
|
||||
return new type.UInt16Be(value - (minimum || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check object identifier
|
||||
* @param s {type.Stream}
|
||||
* @param oid {array} object identifier to check
|
||||
*/
|
||||
function readObjectIdentifier(s, oid) {
|
||||
var size = readLength(s);
|
||||
if(size !== 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var a_oid = [0, 0, 0, 0, 0, 0];
|
||||
var t12 = new type.UInt8().read(s).value;
|
||||
a_oid[0] = t12 >> 4;
|
||||
a_oid[1] = t12 & 0x0f;
|
||||
a_oid[2] = new type.UInt8().read(s).value;
|
||||
a_oid[3] = new type.UInt8().read(s).value;
|
||||
a_oid[4] = new type.UInt8().read(s).value;
|
||||
a_oid[5] = new type.UInt8().read(s).value;
|
||||
|
||||
for(var i in oid) {
|
||||
if(oid[i] !== a_oid[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oid {array} oid to write
|
||||
* @returns {type.Component} per encoded object identifier
|
||||
*/
|
||||
function writeObjectIdentifier(oid) {
|
||||
return new type.Component([new type.UInt8(5), new type.UInt8((oid[0] << 4) & (oid[1] & 0x0f)), new type.UInt8(oid[2]), new type.UInt8(oid[3]), new type.UInt8(oid[4]), new type.UInt8(oid[5])]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read as padding...
|
||||
* @param s {type.Stream}
|
||||
* @param minValue
|
||||
*/
|
||||
function readNumericString(s, minValue) {
|
||||
var length = readLength(s);
|
||||
length = (length + minValue + 1) / 2;
|
||||
s.readPadding(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nStr {String}
|
||||
* @param minValue {integer}
|
||||
* @returns {type.Component} per encoded numeric string
|
||||
*/
|
||||
function writeNumericString(nStr, minValue) {
|
||||
var length = nStr.length;
|
||||
var mlength = minValue;
|
||||
if(length - minValue >= 0) {
|
||||
mlength = length - minValue;
|
||||
}
|
||||
|
||||
var result = [];
|
||||
|
||||
for(var i = 0; i < length; i += 2) {
|
||||
var c1 = nStr.charCodeAt(i);
|
||||
var c2 = 0;
|
||||
if(i + 1 < length) {
|
||||
c2 = nStr.charCodeAt(i + 1);
|
||||
}
|
||||
else {
|
||||
c2 = 0x30;
|
||||
}
|
||||
c1 = (c1 - 0x30) % 10;
|
||||
c2 = (c2 - 0x30) % 10;
|
||||
|
||||
result[result.length] = new type.UInt8((c1 << 4) | c2);
|
||||
}
|
||||
|
||||
return new type.Component([writeLength(mlength), new type.Component(result)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param length {integer} length of padding
|
||||
*/
|
||||
function readPadding(s, length) {
|
||||
s.readPadding(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param length {integer} length of padding
|
||||
* @returns {type.BinaryString} per encoded padding
|
||||
*/
|
||||
function writePadding(length) {
|
||||
return new type.BinaryString(Buffer.from(Array(length + 1).join("\x00")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param octetStream {String}
|
||||
* @param minValue {integer} default 0
|
||||
* @returns {Boolean} true if read octectStream is equal to octetStream
|
||||
*/
|
||||
function readOctetStream(s, octetStream, minValue) {
|
||||
var size = readLength(s) + (minValue || 0);
|
||||
if(size !== octetStream.length) {
|
||||
return false;
|
||||
}
|
||||
for(var i = 0; i < size; i++) {
|
||||
var c = new type.UInt8().read(s);
|
||||
if(octetStream.charCodeAt(i) !== c.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oStr {String}
|
||||
* @param minValue {integer} default 0
|
||||
* @returns {type.Component} per encoded octet stream
|
||||
*/
|
||||
function writeOctetStream(oStr, minValue) {
|
||||
minValue = minValue || 0;
|
||||
var length = oStr.length;
|
||||
var mlength = minValue;
|
||||
|
||||
if(length - minValue >= 0) {
|
||||
mlength = length - minValue;
|
||||
}
|
||||
|
||||
result = [];
|
||||
for(var i = 0; i < length; i++) {
|
||||
result[result.length] = new type.UInt8(oStr[i]);
|
||||
}
|
||||
|
||||
return new type.Component([writeLength(mlength), new type.Component(result)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
readLength : readLength,
|
||||
writeLength : writeLength,
|
||||
readChoice : readChoice,
|
||||
writeChoice : writeChoice,
|
||||
readSelection : readSelection,
|
||||
writeSelection : writeSelection,
|
||||
readNumberOfSet : readNumberOfSet,
|
||||
writeNumberOfSet : writeNumberOfSet,
|
||||
readEnumerates : readEnumerates,
|
||||
writeEnumerates : writeEnumerates,
|
||||
readInteger : readInteger,
|
||||
writeInteger : writeInteger,
|
||||
readInteger16 : readInteger16,
|
||||
writeInteger16 : writeInteger16,
|
||||
readObjectIdentifier : readObjectIdentifier,
|
||||
writeObjectIdentifier : writeObjectIdentifier,
|
||||
readNumericString : readNumericString,
|
||||
writeNumericString : writeNumericString,
|
||||
readPadding : readPadding,
|
||||
writePadding : writePadding,
|
||||
readOctetStream : readOctetStream,
|
||||
writeOctetStream : writeOctetStream
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue