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
704
rdp/protocol/pdu/caps.js
Normal file
704
rdp/protocol/pdu/caps.js
Normal file
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
|
||||
*/
|
||||
var CapsType = {
|
||||
CAPSTYPE_GENERAL : 0x0001,
|
||||
CAPSTYPE_BITMAP : 0x0002,
|
||||
CAPSTYPE_ORDER : 0x0003,
|
||||
CAPSTYPE_BITMAPCACHE : 0x0004,
|
||||
CAPSTYPE_CONTROL : 0x0005,
|
||||
CAPSTYPE_ACTIVATION : 0x0007,
|
||||
CAPSTYPE_POINTER : 0x0008,
|
||||
CAPSTYPE_SHARE : 0x0009,
|
||||
CAPSTYPE_COLORCACHE : 0x000A,
|
||||
CAPSTYPE_SOUND : 0x000C,
|
||||
CAPSTYPE_INPUT : 0x000D,
|
||||
CAPSTYPE_FONT : 0x000E,
|
||||
CAPSTYPE_BRUSH : 0x000F,
|
||||
CAPSTYPE_GLYPHCACHE : 0x0010,
|
||||
CAPSTYPE_OFFSCREENCACHE : 0x0011,
|
||||
CAPSTYPE_BITMAPCACHE_HOSTSUPPORT : 0x0012,
|
||||
CAPSTYPE_BITMAPCACHE_REV2 : 0x0013,
|
||||
CAPSTYPE_VIRTUALCHANNEL : 0x0014,
|
||||
CAPSTYPE_DRAWNINEGRIDCACHE : 0x0015,
|
||||
CAPSTYPE_DRAWGDIPLUS : 0x0016,
|
||||
CAPSTYPE_RAIL : 0x0017,
|
||||
CAPSTYPE_WINDOW : 0x0018,
|
||||
CAPSETTYPE_COMPDESK : 0x0019,
|
||||
CAPSETTYPE_MULTIFRAGMENTUPDATE : 0x001A,
|
||||
CAPSETTYPE_LARGE_POINTER : 0x001B,
|
||||
CAPSETTYPE_SURFACE_COMMANDS : 0x001C,
|
||||
CAPSETTYPE_BITMAP_CODECS : 0x001D,
|
||||
CAPSSETTYPE_FRAME_ACKNOWLEDGE : 0x001E
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var MajorType = {
|
||||
OSMAJORTYPE_UNSPECIFIED : 0x0000,
|
||||
OSMAJORTYPE_WINDOWS : 0x0001,
|
||||
OSMAJORTYPE_OS2 : 0x0002,
|
||||
OSMAJORTYPE_MACINTOSH : 0x0003,
|
||||
OSMAJORTYPE_UNIX : 0x0004,
|
||||
OSMAJORTYPE_IOS : 0x0005,
|
||||
OSMAJORTYPE_OSX : 0x0006,
|
||||
OSMAJORTYPE_ANDROID : 0x0007
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var MinorType = {
|
||||
OSMINORTYPE_UNSPECIFIED : 0x0000,
|
||||
OSMINORTYPE_WINDOWS_31X : 0x0001,
|
||||
OSMINORTYPE_WINDOWS_95 : 0x0002,
|
||||
OSMINORTYPE_WINDOWS_NT : 0x0003,
|
||||
OSMINORTYPE_OS2_V21 : 0x0004,
|
||||
OSMINORTYPE_POWER_PC : 0x0005,
|
||||
OSMINORTYPE_MACINTOSH : 0x0006,
|
||||
OSMINORTYPE_NATIVE_XSERVER : 0x0007,
|
||||
OSMINORTYPE_PSEUDO_XSERVER : 0x0008,
|
||||
OSMINORTYPE_WINDOWS_RT : 0x0009
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var GeneralExtraFlag = {
|
||||
FASTPATH_OUTPUT_SUPPORTED : 0x0001,
|
||||
NO_BITMAP_COMPRESSION_HDR : 0x0400,
|
||||
LONG_CREDENTIALS_SUPPORTED : 0x0004,
|
||||
AUTORECONNECT_SUPPORTED : 0x0008,
|
||||
ENC_SALTED_CHECKSUM : 0x0010
|
||||
};
|
||||
|
||||
var Boolean = {
|
||||
FALSE : 0x00,
|
||||
TRUE : 0x01
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
*/
|
||||
var OrderFlag = {
|
||||
NEGOTIATEORDERSUPPORT : 0x0002,
|
||||
ZEROBOUNDSDELTASSUPPORT : 0x0008,
|
||||
COLORINDEXSUPPORT : 0x0020,
|
||||
SOLIDPATTERNBRUSHONLY : 0x0040,
|
||||
ORDERFLAGS_EXTRA_FLAGS : 0x0080
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
*/
|
||||
var Order = {
|
||||
TS_NEG_DSTBLT_INDEX : 0x00,
|
||||
TS_NEG_PATBLT_INDEX : 0x01,
|
||||
TS_NEG_SCRBLT_INDEX : 0x02,
|
||||
TS_NEG_MEMBLT_INDEX : 0x03,
|
||||
TS_NEG_MEM3BLT_INDEX : 0x04,
|
||||
TS_NEG_DRAWNINEGRID_INDEX : 0x07,
|
||||
TS_NEG_LINETO_INDEX : 0x08,
|
||||
TS_NEG_MULTI_DRAWNINEGRID_INDEX : 0x09,
|
||||
TS_NEG_SAVEBITMAP_INDEX : 0x0B,
|
||||
TS_NEG_MULTIDSTBLT_INDEX : 0x0F,
|
||||
TS_NEG_MULTIPATBLT_INDEX : 0x10,
|
||||
TS_NEG_MULTISCRBLT_INDEX : 0x11,
|
||||
TS_NEG_MULTIOPAQUERECT_INDEX : 0x12,
|
||||
TS_NEG_FAST_INDEX_INDEX : 0x13,
|
||||
TS_NEG_POLYGON_SC_INDEX : 0x14,
|
||||
TS_NEG_POLYGON_CB_INDEX : 0x15,
|
||||
TS_NEG_POLYLINE_INDEX : 0x16,
|
||||
TS_NEG_FAST_GLYPH_INDEX : 0x18,
|
||||
TS_NEG_ELLIPSE_SC_INDEX : 0x19,
|
||||
TS_NEG_ELLIPSE_CB_INDEX : 0x1A,
|
||||
TS_NEG_INDEX_INDEX : 0x1B
|
||||
};
|
||||
|
||||
var OrderEx = {
|
||||
ORDERFLAGS_EX_CACHE_BITMAP_REV3_SUPPORT : 0x0002,
|
||||
ORDERFLAGS_EX_ALTSEC_FRAME_MARKER_SUPPORT : 0x0004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240563.aspx
|
||||
*/
|
||||
var InputFlags = {
|
||||
INPUT_FLAG_SCANCODES : 0x0001,
|
||||
INPUT_FLAG_MOUSEX : 0x0004,
|
||||
INPUT_FLAG_FASTPATH_INPUT : 0x0008,
|
||||
INPUT_FLAG_UNICODE : 0x0010,
|
||||
INPUT_FLAG_FASTPATH_INPUT2 : 0x0020,
|
||||
INPUT_FLAG_UNUSED1 : 0x0040,
|
||||
INPUT_FLAG_UNUSED2 : 0x0080,
|
||||
TS_INPUT_FLAG_MOUSE_HWHEEL : 0x0100
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240564.aspx
|
||||
*/
|
||||
var BrushSupport = {
|
||||
BRUSH_DEFAULT : 0x00000000,
|
||||
BRUSH_COLOR_8x8 : 0x00000001,
|
||||
BRUSH_COLOR_FULL : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240565.aspx
|
||||
*/
|
||||
var GlyphSupport = {
|
||||
GLYPH_SUPPORT_NONE : 0x0000,
|
||||
GLYPH_SUPPORT_PARTIAL : 0x0001,
|
||||
GLYPH_SUPPORT_FULL : 0x0002,
|
||||
GLYPH_SUPPORT_ENCODE : 0x0003
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240550.aspx
|
||||
*/
|
||||
var OffscreenSupportLevel = {
|
||||
FALSE : 0x00000000,
|
||||
TRUE : 0x00000001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240551.aspx
|
||||
*/
|
||||
var VirtualChannelCompressionFlag = {
|
||||
VCCAPS_NO_COMPR : 0x00000000,
|
||||
VCCAPS_COMPR_SC : 0x00000001,
|
||||
VCCAPS_COMPR_CS_8K : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240552.aspx
|
||||
*/
|
||||
var SoundFlag = {
|
||||
NONE : 0x0000,
|
||||
SOUND_BEEPS_FLAG : 0x0001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function generalCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_GENERAL,
|
||||
osMajorType : new type.UInt16Le(),
|
||||
osMinorType : new type.UInt16Le(),
|
||||
protocolVersion : new type.UInt16Le(0x0200, {constant : true}),
|
||||
pad2octetsA : new type.UInt16Le(),
|
||||
generalCompressionTypes : new type.UInt16Le(0, {constant : true}),
|
||||
extraFlags : new type.UInt16Le(),
|
||||
updateCapabilityFlag : new type.UInt16Le(0, {constant : true}),
|
||||
remoteUnshareFlag : new type.UInt16Le(0, {constant : true}),
|
||||
generalCompressionLevel : new type.UInt16Le(0, {constant : true}),
|
||||
refreshRectSupport : new type.UInt8(),
|
||||
suppressOutputSupport : new type.UInt8()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240554.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function bitmapCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BITMAP,
|
||||
preferredBitsPerPixel : new type.UInt16Le(),
|
||||
receive1BitPerPixel : new type.UInt16Le(0x0001),
|
||||
receive4BitsPerPixel : new type.UInt16Le(0x0001),
|
||||
receive8BitsPerPixel : new type.UInt16Le(0x0001),
|
||||
desktopWidth : new type.UInt16Le(),
|
||||
desktopHeight : new type.UInt16Le(),
|
||||
pad2octets : new type.UInt16Le(),
|
||||
desktopResizeFlag : new type.UInt16Le(),
|
||||
bitmapCompressionFlag : new type.UInt16Le(0x0001, {constant : true}),
|
||||
highColorFlags : new type.UInt8(0),
|
||||
drawingFlags : new type.UInt8(),
|
||||
multipleRectangleSupport : new type.UInt16Le(0x0001, {constant : true}),
|
||||
pad2octetsB : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
* @param orders {type.BinaryString|null} list of available orders
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function orderCapability(orders, opt) {
|
||||
if(orders && orders.size() !== 32) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_CAPS_BAD_ORDERS_SIZE');
|
||||
}
|
||||
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_ORDER,
|
||||
terminalDescriptor : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(16)}),
|
||||
pad4octetsA : new type.UInt32Le(0),
|
||||
desktopSaveXGranularity : new type.UInt16Le(1),
|
||||
desktopSaveYGranularity : new type.UInt16Le(20),
|
||||
pad2octetsA : new type.UInt16Le(0),
|
||||
maximumOrderLevel : new type.UInt16Le(1),
|
||||
numberFonts : new type.UInt16Le(),
|
||||
orderFlags : new type.UInt16Le(OrderFlag.NEGOTIATEORDERSUPPORT),
|
||||
orderSupport : orders || new type.Factory(function(s) {
|
||||
self.orderSupport = new type.BinaryString(null, {readLength : new type.CallableValue(32)}).read(s);
|
||||
}),
|
||||
textFlags : new type.UInt16Le(),
|
||||
orderSupportExFlags : new type.UInt16Le(),
|
||||
pad4octetsB : new type.UInt32Le(),
|
||||
desktopSaveSize : new type.UInt32Le(480 * 480),
|
||||
pad2octetsC : new type.UInt16Le(),
|
||||
pad2octetsD : new type.UInt16Le(),
|
||||
textANSICodePage : new type.UInt16Le(0),
|
||||
pad2octetsE : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240559.aspx
|
||||
* @param opt type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function bitmapCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BITMAPCACHE,
|
||||
pad1 : new type.UInt32Le(),
|
||||
pad2 : new type.UInt32Le(),
|
||||
pad3 : new type.UInt32Le(),
|
||||
pad4 : new type.UInt32Le(),
|
||||
pad5 : new type.UInt32Le(),
|
||||
pad6 : new type.UInt32Le(),
|
||||
cache0Entries : new type.UInt16Le(),
|
||||
cache0MaximumCellSize : new type.UInt16Le(),
|
||||
cache1Entries : new type.UInt16Le(),
|
||||
cache1MaximumCellSize : new type.UInt16Le(),
|
||||
cache2Entries : new type.UInt16Le(),
|
||||
cache2MaximumCellSize : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param isServer {boolean} true if in server mode
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function pointerCapability(isServer, opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_POINTER,
|
||||
colorPointerFlag : new type.UInt16Le(),
|
||||
colorPointerCacheSize : new type.UInt16Le(20),
|
||||
//old version of rdp doesn't support ...
|
||||
pointerCacheSize : new type.UInt16Le(null, {conditional : function() {
|
||||
return isServer || false;
|
||||
}})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240563.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function inputCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_INPUT,
|
||||
inputFlags : new type.UInt16Le(),
|
||||
pad2octetsA : new type.UInt16Le(),
|
||||
// same value as gcc.ClientCoreSettings.kbdLayout
|
||||
keyboardLayout : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardType
|
||||
keyboardType : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardSubType
|
||||
keyboardSubType : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardFnKeys
|
||||
keyboardFunctionKey : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettingrrs.imeFileName
|
||||
imeFileName : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(64)})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240564.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function brushCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BRUSH,
|
||||
brushSupportLevel : new type.UInt32Le(BrushSupport.BRUSH_DEFAULT)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240566.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function cacheEntry(opt) {
|
||||
var self = {
|
||||
cacheEntries : new type.UInt16Le(),
|
||||
cacheMaximumCellSize : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240565.aspx
|
||||
* @param entries {type.Component} cache entries
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function glyphCapability(entries, opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_GLYPHCACHE,
|
||||
glyphCache : entries || new type.Factory(function(s) {
|
||||
self.glyphCache = new type.Component([]);
|
||||
for(var i = 0; i < 10; i++) {
|
||||
self.glyphCache.obj.push(cacheEntry().read(s));
|
||||
}
|
||||
}),
|
||||
fragCache : new type.UInt32Le(),
|
||||
// all fonts are sent with bitmap format (very expensive)
|
||||
glyphSupportLevel : new type.UInt16Le(GlyphSupport.GLYPH_SUPPORT_NONE),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240550.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function offscreenBitmapCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_OFFSCREENCACHE,
|
||||
offscreenSupportLevel : new type.UInt32Le(OffscreenSupportLevel.FALSE),
|
||||
offscreenCacheSize : new type.UInt16Le(),
|
||||
offscreenCacheEntries : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240551.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function virtualChannelCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_VIRTUALCHANNEL,
|
||||
flags : new type.UInt32Le(VirtualChannelCompressionFlag.VCCAPS_NO_COMPR),
|
||||
VCChunkSize : new type.UInt32Le(null, {optional : true})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240552.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function soundCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_SOUND,
|
||||
soundFlags : new type.UInt16Le(SoundFlag.NONE),
|
||||
pad2octetsA : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240568.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function controlCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_CONTROL,
|
||||
controlFlags : new type.UInt16Le(),
|
||||
remoteDetachFlag : new type.UInt16Le(),
|
||||
controlInterest : new type.UInt16Le(0x0002),
|
||||
detachInterest : new type.UInt16Le(0x0002)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240569.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function windowActivationCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_ACTIVATION,
|
||||
helpKeyFlag : new type.UInt16Le(),
|
||||
helpKeyIndexFlag : new type.UInt16Le(),
|
||||
helpExtendedKeyFlag : new type.UInt16Le(),
|
||||
windowManagerKeyFlag : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240571.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function fontCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_FONT,
|
||||
fontSupportFlags : new type.UInt16Le(0x0001),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241564.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function colorCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_COLORCACHE,
|
||||
colorTableCacheSize : new type.UInt16Le(0x0006),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240570.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function shareCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_SHARE,
|
||||
nodeId : new type.UInt16Le(),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240649.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function multiFragmentUpdate(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE,
|
||||
MaxRequestSize : new type.UInt32Le(0)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capability wrapper packet
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
|
||||
* @param cap {type.Component}
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function capability(cap, opt) {
|
||||
var self = {
|
||||
capabilitySetType : new type.UInt16Le(function() {
|
||||
return self.capability.obj.__TYPE__;
|
||||
}),
|
||||
lengthCapability : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
capability : cap || new type.Factory(function(s) {
|
||||
switch(self.capabilitySetType.value) {
|
||||
case CapsType.CAPSTYPE_GENERAL:
|
||||
self.capability = generalCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BITMAP:
|
||||
self.capability = bitmapCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_ORDER:
|
||||
self.capability = orderCapability(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BITMAPCACHE:
|
||||
self.capability = bitmapCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_POINTER:
|
||||
self.capability = pointerCapability(false, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_INPUT:
|
||||
self.capability = inputCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BRUSH:
|
||||
self.capability = brushCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_GLYPHCACHE:
|
||||
self.capability = glyphCapability(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_OFFSCREENCACHE:
|
||||
self.capability = offscreenBitmapCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_VIRTUALCHANNEL:
|
||||
self.capability = virtualChannelCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_SOUND:
|
||||
self.capability = soundCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_CONTROL:
|
||||
self.capability = controlCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_ACTIVATION:
|
||||
self.capability = windowActivationCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_FONT:
|
||||
self.capability = fontCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_COLORCACHE:
|
||||
self.capability = colorCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_SHARE:
|
||||
self.capability = shareCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE:
|
||||
self.capability = multiFragmentUpdate({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
default:
|
||||
log.debug('unknown capability ' + self.capabilitySetType.value);
|
||||
self.capability = new type.BinaryString(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
CapsType : CapsType,
|
||||
MajorType : MajorType,
|
||||
MinorType : MinorType,
|
||||
GeneralExtraFlag : GeneralExtraFlag,
|
||||
Boolean : Boolean,
|
||||
OrderFlag : OrderFlag,
|
||||
Order : Order,
|
||||
OrderEx : OrderEx,
|
||||
InputFlags : InputFlags,
|
||||
BrushSupport : BrushSupport,
|
||||
GlyphSupport : GlyphSupport,
|
||||
OffscreenSupportLevel : OffscreenSupportLevel,
|
||||
VirtualChannelCompressionFlag : VirtualChannelCompressionFlag,
|
||||
SoundFlag : SoundFlag,
|
||||
generalCapability : generalCapability,
|
||||
bitmapCapability : bitmapCapability,
|
||||
orderCapability : orderCapability,
|
||||
bitmapCacheCapability : bitmapCacheCapability,
|
||||
pointerCapability : pointerCapability,
|
||||
inputCapability : inputCapability,
|
||||
brushCapability : brushCapability,
|
||||
cacheEntry : cacheEntry,
|
||||
glyphCapability : glyphCapability,
|
||||
offscreenBitmapCacheCapability : offscreenBitmapCacheCapability,
|
||||
virtualChannelCapability : virtualChannelCapability,
|
||||
soundCapability : soundCapability,
|
||||
controlCapability : controlCapability,
|
||||
windowActivationCapability : windowActivationCapability,
|
||||
fontCapability : fontCapability,
|
||||
colorCacheCapability : colorCacheCapability,
|
||||
shareCapability : shareCapability,
|
||||
multiFragmentUpdate : multiFragmentUpdate,
|
||||
capability : capability
|
||||
};
|
1151
rdp/protocol/pdu/data.js
Normal file
1151
rdp/protocol/pdu/data.js
Normal file
File diff suppressed because it is too large
Load diff
402
rdp/protocol/pdu/global.js
Normal file
402
rdp/protocol/pdu/global.js
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* 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 caps = require('./caps');
|
||||
var data = require('./data');
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
|
||||
/**
|
||||
* Global channel for all graphic updates
|
||||
* capabilities exchange and input handles
|
||||
*/
|
||||
function Global(transport, fastPathTransport) {
|
||||
this.transport = transport;
|
||||
this.fastPathTransport = fastPathTransport;
|
||||
// must be init via connect event
|
||||
this.userId = 0;
|
||||
this.serverCapabilities = [];
|
||||
this.clientCapabilities = [];
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Global, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send formated PDU message
|
||||
* @param message {type.Component} PDU message
|
||||
*/
|
||||
Global.prototype.sendPDU = function(message) {
|
||||
this.transport.send(data.pdu(this.userId, message));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send formated Data PDU message
|
||||
* @param message {type.Component} PDU message
|
||||
*/
|
||||
Global.prototype.sendDataPDU = function(message) {
|
||||
this.sendPDU(data.dataPDU(message, this.shareId));
|
||||
};
|
||||
|
||||
/**
|
||||
* Client side of Global channel automata
|
||||
* @param transport
|
||||
*/
|
||||
function Client(transport, fastPathTransport) {
|
||||
Global.call(this, transport, fastPathTransport);
|
||||
var self = this;
|
||||
this.transport.once('connect', function(core, userId, channelId) {
|
||||
self.connect(core, userId, channelId);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
if (this.fastPathTransport) {
|
||||
this.fastPathTransport.on('fastPathData', function (secFlag, s) {
|
||||
self.recvFastPath(secFlag, s);
|
||||
});
|
||||
}
|
||||
|
||||
// init client capabilities
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL] = caps.generalCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP] = caps.bitmapCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER] = caps.orderCapability(
|
||||
new type.Component([
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0)
|
||||
]));
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAPCACHE] = caps.bitmapCacheCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_POINTER] = caps.pointerCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT] = caps.inputCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BRUSH] = caps.brushCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_GLYPHCACHE] = caps.glyphCapability(
|
||||
new type.Component([
|
||||
caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(),
|
||||
caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry()
|
||||
]));
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_OFFSCREENCACHE] = caps.offscreenBitmapCacheCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_VIRTUALCHANNEL] = caps.virtualChannelCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_SOUND] = caps.soundCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate();
|
||||
}
|
||||
|
||||
// inherit from Layer
|
||||
inherits(Client, Global);
|
||||
|
||||
/**
|
||||
* connect function
|
||||
* @param gccCore {type.Component(clientCoreData)}
|
||||
*/
|
||||
Client.prototype.connect = function(gccCore, userId, channelId) {
|
||||
this.gccCore = gccCore;
|
||||
this.userId = userId;
|
||||
this.channelId = channelId;
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive capabilities from server
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvDemandActivePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if (pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DEMANDACTIVEPDU) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// store share id
|
||||
this.shareId = pdu.obj.pduMessage.obj.shareId.value;
|
||||
|
||||
// store server capabilities
|
||||
for(var i in pdu.obj.pduMessage.obj.capabilitySets.obj) {
|
||||
var cap = pdu.obj.pduMessage.obj.capabilitySets.obj[i].obj.capability;
|
||||
if(!cap.obj) {
|
||||
continue;
|
||||
}
|
||||
this.serverCapabilities[cap.obj.__TYPE__] = cap;
|
||||
}
|
||||
|
||||
this.transport.enableSecureCheckSum = !!(this.serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj.extraFlags.value & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM);
|
||||
|
||||
this.sendConfirmActivePDU();
|
||||
this.sendClientFinalizeSynchronizePDU();
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerSynchronizePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerSynchronizePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_SYNCHRONIZE) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerSynchronizePDU(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlCooperatePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerControlCooperatePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
|
||||
|| pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_COOPERATE) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlCooperatePDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlGrantedPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerControlGrantedPDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
|
||||
|| pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_GRANTED_CONTROL) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlGrantedPDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerFontMapPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerFontMapPDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_FONTMAP) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerFontMapPDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
this.emit('connect');
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Main reveive fast path
|
||||
* @param secFlag {integer}
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvFastPath = function (secFlag, s) {
|
||||
while (s.availableLength() > 0) {
|
||||
var pdu = data.fastPathUpdatePDU().read(s);
|
||||
switch (pdu.obj.updateHeader.value & 0xf) {
|
||||
case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
|
||||
this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvPDU = function(s) {
|
||||
while (s.availableLength() > 0) {
|
||||
var pdu = data.pdu().read(s);
|
||||
switch(pdu.obj.shareControlHeader.obj.pduType.value) {
|
||||
case data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
|
||||
var self = this;
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
break;
|
||||
case data.PDUType.PDUTYPE_DATAPDU:
|
||||
this.readDataPDU(pdu.obj.pduMessage)
|
||||
break;
|
||||
default:
|
||||
log.debug('ignore pdu type ' + pdu.obj.shareControlHeader.obj.pduType.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* main receive for data PDU packet
|
||||
* @param dataPDU {data.dataPDU}
|
||||
*/
|
||||
Client.prototype.readDataPDU = function (dataPDU) {
|
||||
switch(dataPDU.obj.shareDataHeader.obj.pduType2.value) {
|
||||
case data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
|
||||
this.transport.close();
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
|
||||
this.emit('session');
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_UPDATE:
|
||||
this.readUpdateDataPDU(dataPDU.obj.pduData)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main upadate pdu receive function
|
||||
* @param updateDataPDU
|
||||
*/
|
||||
Client.prototype.readUpdateDataPDU = function (updateDataPDU) {
|
||||
switch(updateDataPDU.obj.updateType.value) {
|
||||
case data.UpdateType.UPDATETYPE_BITMAP:
|
||||
this.emit('bitmap', updateDataPDU.obj.updateData.obj.rectangles.obj)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* send all client capabilities
|
||||
*/
|
||||
Client.prototype.sendConfirmActivePDU = function () {
|
||||
var generalCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj;
|
||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS;
|
||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT;
|
||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED
|
||||
| caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
|
||||
| caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM
|
||||
| caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED;
|
||||
|
||||
var bitmapCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].obj;
|
||||
bitmapCapability.preferredBitsPerPixel.value = this.gccCore.highColorDepth.value;
|
||||
bitmapCapability.desktopWidth.value = this.gccCore.desktopWidth.value;
|
||||
bitmapCapability.desktopHeight.value = this.gccCore.desktopHeight.value;
|
||||
|
||||
var orderCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].obj;
|
||||
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT;
|
||||
|
||||
var inputCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].obj;
|
||||
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE;
|
||||
inputCapability.keyboardLayout = this.gccCore.kbdLayout;
|
||||
inputCapability.keyboardType = this.gccCore.keyboardType;
|
||||
inputCapability.keyboardSubType = this.gccCore.keyboardSubType;
|
||||
inputCapability.keyboardrFunctionKey = this.gccCore.keyboardFnKeys;
|
||||
inputCapability.imeFileName = this.gccCore.imeFileName;
|
||||
|
||||
var capabilities = new type.Component([]);
|
||||
for(var i in this.clientCapabilities) {
|
||||
capabilities.obj.push(caps.capability(this.clientCapabilities[i]));
|
||||
}
|
||||
|
||||
var confirmActivePDU = data.confirmActivePDU(capabilities, this.shareId);
|
||||
|
||||
this.sendPDU(confirmActivePDU);
|
||||
};
|
||||
|
||||
/**
|
||||
* send synchronize PDU
|
||||
*/
|
||||
Client.prototype.sendClientFinalizeSynchronizePDU = function() {
|
||||
this.sendDataPDU(data.synchronizeDataPDU(this.channelId));
|
||||
this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_COOPERATE));
|
||||
this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL));
|
||||
this.sendDataPDU(data.fontListDataPDU());
|
||||
};
|
||||
|
||||
/**
|
||||
* Send input event as slow path input
|
||||
* @param inputEvents {array}
|
||||
*/
|
||||
Client.prototype.sendInputEvents = function (inputEvents) {
|
||||
var pdu = data.clientInputEventPDU(new type.Component(inputEvents.map(function (e) {
|
||||
return data.slowPathInputEvent(e);
|
||||
})));
|
||||
|
||||
this.sendDataPDU(pdu);
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client
|
||||
};
|
30
rdp/protocol/pdu/index.js
Normal file
30
rdp/protocol/pdu/index.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 lic = require('./lic');
|
||||
var sec = require('./sec');
|
||||
var global = require('./global');
|
||||
var data = require('./data');
|
||||
|
||||
module.exports = {
|
||||
lic : lic,
|
||||
sec : sec,
|
||||
global : global,
|
||||
data : data
|
||||
};
|
309
rdp/protocol/pdu/lic.js
Normal file
309
rdp/protocol/pdu/lic.js
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* 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 MessageType = {
|
||||
LICENSE_REQUEST : 0x01,
|
||||
PLATFORM_CHALLENGE : 0x02,
|
||||
NEW_LICENSE : 0x03,
|
||||
UPGRADE_LICENSE : 0x04,
|
||||
LICENSE_INFO : 0x12,
|
||||
NEW_LICENSE_REQUEST : 0x13,
|
||||
PLATFORM_CHALLENGE_RESPONSE : 0x15,
|
||||
ERROR_ALERT : 0xFF
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
*/
|
||||
var ErrorCode = {
|
||||
ERR_INVALID_SERVER_CERTIFICATE : 0x00000001,
|
||||
ERR_NO_LICENSE : 0x00000002,
|
||||
ERR_INVALID_SCOPE : 0x00000004,
|
||||
ERR_NO_LICENSE_SERVER : 0x00000006,
|
||||
STATUS_VALID_CLIENT : 0x00000007,
|
||||
ERR_INVALID_CLIENT : 0x00000008,
|
||||
ERR_INVALID_PRODUCTID : 0x0000000B,
|
||||
ERR_INVALID_MESSAGE_LEN : 0x0000000C,
|
||||
ERR_INVALID_MAC : 0x00000003
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
*/
|
||||
var StateTransition = {
|
||||
ST_TOTAL_ABORT : 0x00000001,
|
||||
ST_NO_TRANSITION : 0x00000002,
|
||||
ST_RESET_PHASE_TO_START : 0x00000003,
|
||||
ST_RESEND_LAST_MESSAGE : 0x00000004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
*/
|
||||
var BinaryBlobType = {
|
||||
BB_ANY_BLOB : 0x0000,
|
||||
BB_DATA_BLOB : 0x0001,
|
||||
BB_RANDOM_BLOB : 0x0002,
|
||||
BB_CERTIFICATE_BLOB : 0x0003,
|
||||
BB_ERROR_BLOB : 0x0004,
|
||||
BB_ENCRYPTED_DATA_BLOB : 0x0009,
|
||||
BB_KEY_EXCHG_ALG_BLOB : 0x000D,
|
||||
BB_SCOPE_BLOB : 0x000E,
|
||||
BB_CLIENT_USER_NAME_BLOB : 0x000F,
|
||||
BB_CLIENT_MACHINE_NAME_BLOB : 0x0010
|
||||
};
|
||||
|
||||
var Preambule = {
|
||||
PREAMBLE_VERSION_2_0 : 0x2,
|
||||
PREAMBLE_VERSION_3_0 : 0x3,
|
||||
EXTENDED_ERROR_MSG_SUPPORTED : 0x80
|
||||
};
|
||||
|
||||
/**
|
||||
* Binary blob to emcompass license information
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
* @param blobType {BinaryBlobType.*}
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licenseBinaryBlob(blobType) {
|
||||
blobType = blobType || BinaryBlobType.BB_ANY_BLOB;
|
||||
var self = {
|
||||
wBlobType : new type.UInt16Le(blobType, { constant : (blobType === BinaryBlobType.BB_ANY_BLOB)?false:true }),
|
||||
wBlobLen : new type.UInt16Le(function() {
|
||||
return self.blobData.size();
|
||||
}),
|
||||
blobData : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.wBlobLen.value;
|
||||
})})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error message in license PDU automata
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licensingErrorMessage(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.ERROR_ALERT,
|
||||
dwErrorCode : new type.UInt32Le(),
|
||||
dwStateTransition : new type.UInt32Le(),
|
||||
blob : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* License product informations
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241915.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function productInformation() {
|
||||
var self = {
|
||||
dwVersion : new type.UInt32Le(),
|
||||
cbCompanyName : new type.UInt32Le(function() {
|
||||
return self.pbCompanyName.size();
|
||||
}),
|
||||
// may contain "Microsoft Corporation" from server microsoft
|
||||
pbCompanyName : new type.BinaryString(Buffer.from('Microsoft Corporation', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbCompanyName.value;
|
||||
})}),
|
||||
cbProductId : new type.UInt32Le(function() {
|
||||
return self.pbProductId.size();
|
||||
}),
|
||||
// may contain "A02" from microsoft license server
|
||||
pbProductId : new type.BinaryString(Buffer.from('A02', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbProductId.value;
|
||||
})})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use in license negotiation
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241917.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function scope() {
|
||||
var self = {
|
||||
scope : licenseBinaryBlob(BinaryBlobType.BB_SCOPE_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241916.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function scopeList() {
|
||||
var self = {
|
||||
scopeCount : new type.UInt32Le(function() {
|
||||
return self.scopeArray.length;
|
||||
}),
|
||||
scopeArray : new type.Factory(function(s) {
|
||||
self.scopeArray = new type.Component([]);
|
||||
for(var i = 0; i < self.scopeCount.value; i++) {
|
||||
self.scopeArray.obj.push(scope().read(s));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241914.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverLicenseRequest(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.LICENSE_REQUEST,
|
||||
serverRandom : new type.BinaryString(Buffer.from(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) } ),
|
||||
productInfo : productInformation(),
|
||||
keyExchangeList : licenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB),
|
||||
serverCertificate : licenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB),
|
||||
scopeList : scopeList()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientNewLicenseRequest(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.NEW_LICENSE_REQUEST,
|
||||
preferredKeyExchangeAlg : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
// pure microsoft client ;-)
|
||||
// http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
|
||||
platformId : new type.UInt32Le(0x04000000 | 0x00010000),
|
||||
clientRandom : new type.BinaryString(Buffer.from(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) }),
|
||||
encryptedPreMasterSecret : licenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB),
|
||||
ClientUserName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB),
|
||||
ClientMachineName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241921.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverPlatformChallenge(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.PLATFORM_CHALLENGE,
|
||||
connectFlags : new type.UInt32Le(),
|
||||
encryptedPlatformChallenge : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB),
|
||||
MACData : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00')), { readLength : new type.CallableValue(16) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241922.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientPLatformChallengeResponse(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.PLATFORM_CHALLENGE_RESPONSE,
|
||||
encryptedPlatformChallengeResponse : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB),
|
||||
encryptedHWID : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB),
|
||||
MACData : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00'), 'binary'), { readLength : new type.CallableValue(16) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Global license packet
|
||||
* @param packet {type.* | null} send packet
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licensePacket(message) {
|
||||
var self = {
|
||||
bMsgtype : new type.UInt8(function() {
|
||||
return self.licensingMessage.obj.__TYPE__;
|
||||
}),
|
||||
flag : new type.UInt8(Preambule.PREAMBLE_VERSION_3_0),
|
||||
wMsgSize : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
licensingMessage : message || new type.Factory(function(s) {
|
||||
switch(self.bMsgtype.value) {
|
||||
case MessageType.ERROR_ALERT:
|
||||
self.licensingMessage = licensingErrorMessage({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.LICENSE_REQUEST:
|
||||
self.licensingMessage = serverLicenseRequest({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.NEW_LICENSE_REQUEST:
|
||||
self.licensingMessage = clientNewLicenseRequest({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.PLATFORM_CHALLENGE:
|
||||
self.licensingMessage = serverPlatformChallenge({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.PLATFORM_CHALLENGE_RESPONSE:
|
||||
self.licensingMessage = clientPLatformChallengeResponse({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
default:
|
||||
log.error('unknown license message type ' + self.bMsgtype.value);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
MessageType : MessageType,
|
||||
ErrorCode : ErrorCode,
|
||||
StateTransition : StateTransition,
|
||||
licensePacket : licensePacket,
|
||||
clientNewLicenseRequest : clientNewLicenseRequest,
|
||||
clientPLatformChallengeResponse : clientPLatformChallengeResponse
|
||||
};
|
519
rdp/protocol/pdu/sec.js
Normal file
519
rdp/protocol/pdu/sec.js
Normal file
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* 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 crypto = require('crypto');
|
||||
var events = require('events');
|
||||
var type = require('../../core').type;
|
||||
var error = require('../../core').error;
|
||||
var log = require('../../core').log;
|
||||
var gcc = require('../t125/gcc');
|
||||
var lic = require('./lic');
|
||||
var cert = require('../cert');
|
||||
var rsa = require('../../security').rsa;
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240579.aspx
|
||||
*/
|
||||
var SecurityFlag = {
|
||||
SEC_EXCHANGE_PKT : 0x0001,
|
||||
SEC_TRANSPORT_REQ : 0x0002,
|
||||
RDP_SEC_TRANSPORT_RSP : 0x0004,
|
||||
SEC_ENCRYPT : 0x0008,
|
||||
SEC_RESET_SEQNO : 0x0010,
|
||||
SEC_IGNORE_SEQNO : 0x0020,
|
||||
SEC_INFO_PKT : 0x0040,
|
||||
SEC_LICENSE_PKT : 0x0080,
|
||||
SEC_LICENSE_ENCRYPT_CS : 0x0200,
|
||||
SEC_LICENSE_ENCRYPT_SC : 0x0200,
|
||||
SEC_REDIRECTION_PKT : 0x0400,
|
||||
SEC_SECURE_CHECKSUM : 0x0800,
|
||||
SEC_AUTODETECT_REQ : 0x1000,
|
||||
SEC_AUTODETECT_RSP : 0x2000,
|
||||
SEC_HEARTBEAT : 0x4000,
|
||||
SEC_FLAGSHI_VALID : 0x8000
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240475.aspx
|
||||
*/
|
||||
var InfoFlag = {
|
||||
INFO_MOUSE : 0x00000001,
|
||||
INFO_DISABLECTRLALTDEL : 0x00000002,
|
||||
INFO_AUTOLOGON : 0x00000008,
|
||||
INFO_UNICODE : 0x00000010,
|
||||
INFO_MAXIMIZESHELL : 0x00000020,
|
||||
INFO_LOGONNOTIFY : 0x00000040,
|
||||
INFO_COMPRESSION : 0x00000080,
|
||||
INFO_ENABLEWINDOWSKEY : 0x00000100,
|
||||
INFO_REMOTECONSOLEAUDIO : 0x00002000,
|
||||
INFO_FORCE_ENCRYPTED_CS_PDU : 0x00004000,
|
||||
INFO_RAIL : 0x00008000,
|
||||
INFO_LOGONERRORS : 0x00010000,
|
||||
INFO_MOUSE_HAS_WHEEL : 0x00020000,
|
||||
INFO_PASSWORD_IS_SC_PIN : 0x00040000,
|
||||
INFO_NOAUDIOPLAYBACK : 0x00080000,
|
||||
INFO_USING_SAVED_CREDS : 0x00100000,
|
||||
INFO_AUDIOCAPTURE : 0x00200000,
|
||||
INFO_VIDEO_DISABLE : 0x00400000,
|
||||
INFO_CompressionTypeMask : 0x00001E00
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
|
||||
*/
|
||||
var AfInet = {
|
||||
AfInet : 0x00002,
|
||||
AF_INET6 : 0x0017
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
|
||||
*/
|
||||
var PerfFlag = {
|
||||
PERF_DISABLE_WALLPAPER : 0x00000001,
|
||||
PERF_DISABLE_FULLWINDOWDRAG : 0x00000002,
|
||||
PERF_DISABLE_MENUANIMATIONS : 0x00000004,
|
||||
PERF_DISABLE_THEMING : 0x00000008,
|
||||
PERF_DISABLE_CURSOR_SHADOW : 0x00000020,
|
||||
PERF_DISABLE_CURSORSETTINGS : 0x00000040,
|
||||
PERF_ENABLE_FONT_SMOOTHING : 0x00000080,
|
||||
PERF_ENABLE_DESKTOP_COMPOSITION : 0x00000100
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
|
||||
* @param input {Buffer} Binary data
|
||||
* @param salt {Buffer} salt for context call
|
||||
* @param salt1 {Buffer} another salt (ex : client random)
|
||||
* @param salt2 {Buffer} another salt (ex : server random)
|
||||
* @return {Buffer}
|
||||
*/
|
||||
function saltedHash(input, salt, salt1, salt2) {
|
||||
var sha1Digest = crypto.createHash('sha1');
|
||||
sha1Digest.update(input);
|
||||
sha1Digest.update(salt.slice(0, 48));
|
||||
sha1Digest.update(salt1);
|
||||
sha1Digest.update(salt2);
|
||||
|
||||
var sha1Sig = sha1Digest.digest();
|
||||
|
||||
var md5Digest = crypto.createHash('md5');
|
||||
md5Digest.update(salt.slice(0, 48));
|
||||
md5Digest.update(sha1Sig);
|
||||
return md5Digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {Buffer} secret
|
||||
* @param random1 {Buffer} client random
|
||||
* @param random2 {Buffer} server random
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function finalHash (key, random1, random2) {
|
||||
var md5Digest = crypto.createHash('md5');
|
||||
md5Digest.update(key);
|
||||
md5Digest.update(random1);
|
||||
md5Digest.update(random2);
|
||||
return md5Digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
|
||||
* @param secret {Buffer} secret
|
||||
* @param random1 {Buffer} client random
|
||||
* @param random2 {Buffer} server random
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function masterSecret (secret, random1, random2) {
|
||||
var sh1 = saltedHash(Buffer.from('A'), secret, random1, random2);
|
||||
var sh2 = saltedHash(Buffer.from('BB'), secret, random1, random2);
|
||||
var sh3 = saltedHash(Buffer.from('CCC'), secret, random1, random2);
|
||||
|
||||
var ms = Buffer.alloc(sh1.length + sh2.length + sh3.length);
|
||||
sh1.copy(ms);
|
||||
sh2.copy(ms, sh1.length);
|
||||
sh3.copy(ms, sh1.length + sh2.length);
|
||||
return ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241995.aspx
|
||||
* @param macSaltKey {Buffer} key
|
||||
* @param data {Buffer} data
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function macData(macSaltKey, data) {
|
||||
var salt1 = Buffer.alloc(40);
|
||||
salt1.fill(0x36);
|
||||
|
||||
var salt2 = Buffer.alloc(48);
|
||||
salt2.fill(0x5c);
|
||||
|
||||
var dataLength = new type.UInt32Le(data.length).toStream().buffer;
|
||||
|
||||
var sha1 = crypto.createHash('sha1');
|
||||
sha1.update(macSaltKey);
|
||||
sha1.update(salt1);
|
||||
sha1.update(dataLength);
|
||||
sha1.update(data);
|
||||
var sha1Digest = sha1.digest();
|
||||
|
||||
var md5 = crypto.createHash('md5');
|
||||
md5.update(macSaltKey);
|
||||
md5.update(salt2);
|
||||
md5.update(sha1Digest);
|
||||
|
||||
return md5.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* RDP client informations
|
||||
* @param extendedInfoConditional {boolean} true if RDP5+
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function rdpInfos(extendedInfoConditional) {
|
||||
var self = {
|
||||
codePage : new type.UInt32Le(),
|
||||
flag : new type.UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY),
|
||||
cbDomain : new type.UInt16Le(function() {
|
||||
return self.domain.size() - 2;
|
||||
}),
|
||||
cbUserName : new type.UInt16Le(function() {
|
||||
return self.userName.size() - 2;
|
||||
}),
|
||||
cbPassword : new type.UInt16Le(function() {
|
||||
return self.password.size() - 2;
|
||||
}),
|
||||
cbAlternateShell : new type.UInt16Le(function() {
|
||||
return self.alternateShell.size() - 2;
|
||||
}),
|
||||
cbWorkingDir : new type.UInt16Le(function() {
|
||||
return self.workingDir.size() - 2;
|
||||
}),
|
||||
domain : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
|
||||
return self.cbDomain.value + 2;
|
||||
})}),
|
||||
userName : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbUserName.value + 2;
|
||||
})}),
|
||||
password : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function () {
|
||||
return self.cbPassword.value + 2;
|
||||
})}),
|
||||
alternateShell : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbAlternateShell.value + 2;
|
||||
})}),
|
||||
workingDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbWorkingDir.value + 2;
|
||||
})}),
|
||||
extendedInfo : rdpExtendedInfos({ conditional : extendedInfoConditional })
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* RDP client extended informations present in RDP5+
|
||||
* @param opt
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function rdpExtendedInfos(opt) {
|
||||
var self = {
|
||||
clientAddressFamily : new type.UInt16Le(AfInet.AfInet),
|
||||
cbClientAddress : new type.UInt16Le(function() {
|
||||
return self.clientAddress.size();
|
||||
}),
|
||||
clientAddress : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
|
||||
return self.cbClientAddress;
|
||||
}) }),
|
||||
cbClientDir : new type.UInt16Le(function() {
|
||||
return self.clientDir.size();
|
||||
}),
|
||||
clientDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbClientDir;
|
||||
}) }),
|
||||
clientTimeZone : new type.BinaryString(Buffer.from(Array(172 + 1).join("\x00"))),
|
||||
clientSessionId : new type.UInt32Le(),
|
||||
performanceFlags : new type.UInt32Le()
|
||||
};
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Header of security header
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function securityHeader() {
|
||||
var self = {
|
||||
securityFlag : new type.UInt16Le(),
|
||||
securityFlagHi : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Security layer
|
||||
* @param transport {events.EventEmitter}
|
||||
*/
|
||||
function Sec(transport, fastPathTransport) {
|
||||
this.transport = transport;
|
||||
this.fastPathTransport = fastPathTransport;
|
||||
// init at connect event from transport layer
|
||||
this.gccClient = null;
|
||||
this.gccServer = null;
|
||||
var self = this;
|
||||
this.infos = rdpInfos(function() {
|
||||
return self.gccClient.core.rdpVersion.value === gcc.VERSION.RDP_VERSION_5_PLUS;
|
||||
});
|
||||
this.machineName = '';
|
||||
|
||||
|
||||
// basic encryption
|
||||
this.enableEncryption = false;
|
||||
|
||||
if (this.fastPathTransport) {
|
||||
this.fastPathTransport.on('fastPathData', function (secFlag, s) {
|
||||
self.recvFastPath(secFlag, s);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Sec, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send message with security header
|
||||
* @param flag {integer} security flag
|
||||
* @param data {type.*} message
|
||||
*/
|
||||
Sec.prototype.sendFlagged = function(flag, data) {
|
||||
this.transport.send('global', new type.Component([
|
||||
new type.UInt16Le(flag),
|
||||
new type.UInt16Le(),
|
||||
data
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Main send function
|
||||
* @param message {type.*} message to send
|
||||
*/
|
||||
Sec.prototype.send = function(message) {
|
||||
if (this.enableEncryption) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
|
||||
}
|
||||
this.transport.send('global', message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Main receive function
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recv = function(s) {
|
||||
if (this.enableEncryption) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
|
||||
}
|
||||
// not support yet basic RDP security layer
|
||||
this.emit('data', s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive fast path data
|
||||
* @param secFlag {integer} security flag
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recvFastPath = function (secFlag, s) {
|
||||
// transparent because basic RDP security layer not implemented
|
||||
this.emit('fastPathData', secFlag, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Client security layer
|
||||
* @param transport {events.EventEmitter}
|
||||
*/
|
||||
function Client(transport, fastPathTransport) {
|
||||
Sec.call(this, transport, fastPathTransport);
|
||||
// for basic RDP layer (in futur)
|
||||
this.enableSecureCheckSum = false;
|
||||
var self = this;
|
||||
this.transport.on('connect', function(gccClient, gccServer, userId, channels) {
|
||||
self.connect(gccClient, gccServer, userId, channels);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Client, Sec);
|
||||
|
||||
/**
|
||||
* Connect event
|
||||
*/
|
||||
Client.prototype.connect = function(gccClient, gccServer, userId, channels) {
|
||||
//init gcc information
|
||||
this.gccClient = gccClient;
|
||||
this.gccServer = gccServer;
|
||||
this.userId = userId;
|
||||
this.channelId = channels.find(function(e) {
|
||||
if(e.name === 'global') return true;
|
||||
}).id;
|
||||
this.sendInfoPkt();
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Send main information packet
|
||||
* VIP (very important packet) because contain credentials
|
||||
*/
|
||||
Client.prototype.sendInfoPkt = function() {
|
||||
this.sendFlagged(SecurityFlag.SEC_INFO_PKT, this.infos);
|
||||
var self = this;
|
||||
this.transport.once('global', function(s) {
|
||||
self.recvLicense(s);
|
||||
});
|
||||
};
|
||||
|
||||
function reverse(buffer) {
|
||||
var result = Buffer.alloc(buffer.length);
|
||||
for(var i = 0; i < buffer.length; i++) {
|
||||
result.writeUInt8(buffer.readUInt8(buffer.length - 1 - i), i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a valid license request
|
||||
* @param licenseRequest {object(lic.serverLicenseRequest)} license requets infos
|
||||
*/
|
||||
Client.prototype.sendClientNewLicenseRequest = function(licenseRequest) {
|
||||
log.debug('new license request');
|
||||
var serverRandom = licenseRequest.serverRandom.value;
|
||||
|
||||
// read server certificate
|
||||
var s = new type.Stream(licenseRequest.serverCertificate.obj.blobData.value);
|
||||
var certificate = cert.certificate().read(s).obj;
|
||||
var publicKey = certificate.certData.obj.getPublicKey();
|
||||
|
||||
var clientRandom = crypto.randomBytes(32);
|
||||
var preMasterSecret = crypto.randomBytes(48);
|
||||
var mSecret = masterSecret(preMasterSecret, clientRandom, serverRandom);
|
||||
var sessionKeyBlob = masterSecret(mSecret, serverRandom, clientRandom);
|
||||
|
||||
this.licenseMacSalt = sessionKeyBlob.slice(0, 16)
|
||||
this.licenseKey = finalHash(sessionKeyBlob.slice(16, 32), clientRandom, serverRandom);
|
||||
|
||||
var request = lic.clientNewLicenseRequest();
|
||||
request.obj.clientRandom.value = clientRandom;
|
||||
|
||||
var preMasterSecretEncrypted = reverse(rsa.encrypt(reverse(preMasterSecret), publicKey));
|
||||
var preMasterSecretEncryptedPadded = Buffer.alloc(preMasterSecretEncrypted.length + 8);
|
||||
preMasterSecretEncryptedPadded.fill(0);
|
||||
preMasterSecretEncrypted.copy(preMasterSecretEncryptedPadded);
|
||||
request.obj.encryptedPreMasterSecret.obj.blobData.value = preMasterSecretEncryptedPadded;
|
||||
|
||||
request.obj.ClientMachineName.obj.blobData.value = this.infos.obj.userName.value;
|
||||
request.obj.ClientUserName.obj.blobData.value = Buffer.from(this.machineName + '\x00');
|
||||
|
||||
this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(request));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a valid license request
|
||||
* @param platformChallenge {object(lic.serverPlatformChallenge)} platform challenge
|
||||
*/
|
||||
Client.prototype.sendClientChallengeResponse = function(platformChallenge) {
|
||||
log.debug('challenge license');
|
||||
var serverEncryptedChallenge = platformChallenge.encryptedPlatformChallenge.obj.blobData.value;
|
||||
var serverChallenge = crypto.createDecipheriv('rc4', this.licenseKey, '').update(serverEncryptedChallenge);
|
||||
if (serverChallenge.toString('ucs2') !== 'TEST\x00') {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_INVALID_LICENSE_CHALLENGE');
|
||||
}
|
||||
|
||||
var hwid = new type.Component([new type.UInt32Le(2), new type.BinaryString(crypto.randomBytes(16))]).toStream().buffer;
|
||||
|
||||
var response = lic.clientPLatformChallengeResponse();
|
||||
response.obj.encryptedPlatformChallengeResponse.obj.blobData.value = serverEncryptedChallenge;
|
||||
response.obj.encryptedHWID.obj.blobData.value = crypto.createCipheriv('rc4', this.licenseKey, '').update(hwid);
|
||||
|
||||
var sig = Buffer.alloc(serverChallenge.length + hwid.length);
|
||||
serverChallenge.copy(sig);
|
||||
hwid.copy(sig, serverChallenge.length);
|
||||
response.obj.MACData.value = macData(this.licenseMacSalt, sig);
|
||||
|
||||
this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(response));
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive license informations
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recvLicense = function(s) {
|
||||
var header = securityHeader().read(s).obj;
|
||||
if (!(header.securityFlag.value & SecurityFlag.SEC_LICENSE_PKT)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER');
|
||||
}
|
||||
|
||||
var message = lic.licensePacket().read(s).obj;
|
||||
// i'm accepted
|
||||
if (message.bMsgtype.value === lic.MessageType.NEW_LICENSE ||
|
||||
(message.bMsgtype.value === lic.MessageType.ERROR_ALERT
|
||||
&& message.licensingMessage.obj.dwErrorCode.value === lic.ErrorCode.STATUS_VALID_CLIENT
|
||||
&& message.licensingMessage.obj.dwStateTransition.value === lic.StateTransition.ST_NO_TRANSITION)) {
|
||||
this.emit('connect', this.gccClient.core, this.userId, this.channelId);
|
||||
var self = this;
|
||||
this.transport.on('global', function(s) {
|
||||
self.recv(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// server ask license request
|
||||
if (message.bMsgtype.value === lic.MessageType.LICENSE_REQUEST) {
|
||||
this.sendClientNewLicenseRequest(message.licensingMessage.obj);
|
||||
}
|
||||
|
||||
// server send challenge
|
||||
if (message.bMsgtype.value === lic.MessageType.PLATFORM_CHALLENGE) {
|
||||
this.sendClientChallengeResponse(message.licensingMessage.obj);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.emit('connect', this.gccClient.core);
|
||||
this.transport.once('global', function (s) {
|
||||
self.recvLicense(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
PerfFlag : PerfFlag,
|
||||
InfoFlag : InfoFlag,
|
||||
Client : Client
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue