├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── lib ├── asn1 │ ├── ber.js │ ├── index.js │ ├── spec.js │ └── univ.js ├── core │ ├── error.js │ ├── index.js │ ├── layer.js │ ├── log.js │ ├── rle.js │ └── type.js ├── index.js ├── protocol │ ├── cert.js │ ├── index.js │ ├── pdu │ │ ├── caps.js │ │ ├── data.js │ │ ├── global.js │ │ ├── index.js │ │ ├── lic.js │ │ └── sec.js │ ├── rdp.js │ ├── t125 │ │ ├── gcc.js │ │ ├── index.js │ │ ├── mcs.js │ │ └── per.js │ ├── tpkt.js │ └── x224.js └── security │ ├── index.js │ ├── jsbn.js │ ├── rsa.js │ └── x509.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | README.md~ 3 | npm_modules/* 4 | .settings/* 5 | node_modules/* 6 | *.log 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.1 - 20150708 2 | * Minor bug fix 3 | 4 | ## 0.2.0 - 20150707 5 | * Add this changelog 6 | * Configure keyboard layout for client side of protocol 7 | * Handle TLS error 8 | * Add log level -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-rdpjs 2 | ======== 3 | 4 | Remote Desktop Protocol for Node.js 5 | 6 | node-rdpjs is a pure implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (client and server side). node-rdpjs support only SSL security layer. 7 | 8 | ## Install 9 | 10 | You can install last release node-rdpjs through npm : 11 | 12 | ``` 13 | npm install node-rdpjs 14 | ``` 15 | 16 | Or work with dev branch : 17 | 18 | ``` 19 | git clone https://github.com/citronneur/node-rdpjs.git 20 | cd node-rdpjs 21 | npm install 22 | ``` 23 | 24 | ## RDP Client 25 | 26 | To create a simple rdp client : 27 | 28 | ```javascript 29 | var rdp = require('node-rdpjs'); 30 | 31 | var client = rdp.createClient({ 32 | domain : 'my_domain', 33 | userName : 'my_username', 34 | password : 'my_password', 35 | enablePerf : true, 36 | autoLogin : true, 37 | decompress : false, 38 | screen : { width : 800, height : 600 }, 39 | locale : 'en', 40 | logLevel : 'INFO' 41 | }).on('connect', function () { 42 | }).on('close', function() { 43 | }).on('bitmap', function(bitmap) { 44 | }).on('error', function(err) { 45 | }).connect('XXX.XXX.XXX.XXX', 3389); 46 | ``` 47 | 48 | Client parameters : 49 | 50 | * domain {string} Microsoft domain 51 | * userName {string} Username 52 | * password {string} password 53 | * enablePerf {boolean} Active some performance features (disable wallpaper) 54 | * autoLogin {boolean} start session if login informations are good 55 | * decompress {boolean} use RLE algorrithm for decompress bitmap 56 | * screen {object} screen size 57 | - width {integer} width of screen 58 | - height {integer} height of screen 59 | * locale {string} keyboard layout 60 | - en qwerty layout 61 | - fr azerty layout 62 | * logLevel {string} console log level of library 63 | - DEBUG 64 | - INFO 65 | - WARN 66 | - ERROR 67 | 68 | Use of decompress parameter impact performance. 69 | 70 | ### Client Events 71 | 72 | List of all available events from server 73 | 74 | #### connect 75 | 76 | Connect event is received when rdp stack is connected 77 | 78 | #### close 79 | 80 | Close event is received when rdp stack is close cleanly 81 | 82 | #### error 83 | 84 | Error event is received when a protocol error happened 85 | 86 | #### bitmap 87 | 88 | Bitmap event is received for a bitmap refresh order : 89 | 90 | * destTop {integer} y min position 91 | * destLeft {integer} x min position 92 | * destBottom {integer} y max position 93 | * destRight {integer} x max position 94 | * width {integer} width of bitmap data 95 | * height {integer} height of bitmap data 96 | * bitsPerPixel {integer} [15|16|24|32] bits per pixel 97 | * isCompress {boolean} true if bitmap is compressed with RLE algorithm 98 | * data : {Buffer} bitmap data 99 | 100 | ### Client Inputs 101 | 102 | Client inputs are mainly user inputs (mouse and keyboard). 103 | 104 | #### Mouse 105 | 106 | ```javascript 107 | client.sendPointerEvent(x, y, button, isPressed); 108 | ``` 109 | 110 | * x {integer} mouse x position in pixel 111 | * y {integer} mouse y position in pixel 112 | * button {integer} [ 1 (left) | 2 (right) | 3 (middle) ] 113 | * isPressed {boolean} true for a pressed button event 114 | 115 | #### Keyboard 116 | 117 | ```javascript 118 | client.sendKeyEventScancode(code, isPressed); 119 | ``` 120 | 121 | * code {integer} scancode of key 122 | * isPressed {boolean} true for a key pressed event 123 | 124 | ```javascript 125 | client.sendKeyEventUnicode(code, isPressed); 126 | ``` 127 | 128 | * code {integer} unicode char of key 129 | * isPressed {boolean} true for a key pressed event 130 | 131 | ## Project 132 | 133 | Please see [**mstsc.js**](https://github.com/citronneur/mstsc.js) project page to watch an example of node-rdpjs. 134 | 135 | ## Roadmap 136 | 137 | * Protocol server side 138 | * NLA Authentication security layer 139 | * RDP security layer for windows xp compatibility 140 | * Win32 orders 141 | * RemoteFX (H.264) codec 142 | -------------------------------------------------------------------------------- /lib/asn1/ber.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var type = require('../core').type; 21 | var log = require('../core').log; 22 | var error = require('../core').error; 23 | 24 | /** 25 | * Parse tag(T) field of BER TLV 26 | * And check with expected tag 27 | * @param s {type.Stream} 28 | * @param tag {spec.tag} 29 | * @returns {Boolean} True for valid tag matching 30 | */ 31 | function decodeTag(s, tag) { 32 | var nextTag = new type.UInt8().read(s).value; 33 | if (tag.tagNumber > 30) { 34 | nextTagNumber = new type.UInt8().read(s).value; 35 | } 36 | else { 37 | nextTagNumber = nextTag & 0x1F; 38 | } 39 | 40 | return ((nextTag & 0xE0) === (tag.tagClass | tag.tagFormat)) && (nextTagNumber === tag.tagNumber); 41 | }; 42 | 43 | /** 44 | * Parse length(L) field of BER TLV 45 | * @param s {type.Stream} 46 | * @returns {integer} 47 | */ 48 | function decodeLength(s) { 49 | var size = new type.UInt8().read(s).value; 50 | if(size & 0x80) { 51 | size &= ~0x80; 52 | if(size === 1) { 53 | size = new type.UInt8().read(s).value; 54 | } 55 | else if(size === 2) { 56 | size = new type.UInt16Be().read(s).value; 57 | } 58 | else{ 59 | throw new error.ProtocolError('NODE_RDP_ASN1_BER_INVALID_LENGTH'); 60 | } 61 | } 62 | return size; 63 | }; 64 | 65 | /** 66 | * Decode tuple TLV (Tag Length Value) of BER 67 | * @param s {type.Stream} 68 | * @param tag {spec.Asn1Tag} expected tag 69 | * @returns {type.BinaryString} Value of tuple 70 | */ 71 | function decode(s, tag) { 72 | if (!decodeTag(s, tag)) { 73 | throw new error.ProtocolError('NODE_RDP_ASN1_BER_INVALID_TAG'); 74 | } 75 | var length = decodeLength(s); 76 | 77 | if (length === 0) { 78 | return new type.Stream(0); 79 | } 80 | return new type.BinaryString(null,{ readLength : new type.CallableValue(length) }).read(s); 81 | }; 82 | 83 | function encodeTag(tag) { 84 | if(tag.tagNumber > 30) { 85 | return new type.Component([new type.UInt8(tag.tagClass | tag.tagFormat | 0x1F), new type.UInt8(tag.tagNumber)]); 86 | } 87 | else { 88 | return new type.UInt8((tag.tagClass | tag.tagFormat) | (tag.tagNumber & 0x1F)); 89 | } 90 | } 91 | 92 | function encodeLength(length) { 93 | if(length > 0x7f) { 94 | return new type.Component([new type.UInt8(0x82), new type.UInt16Be(length)]); 95 | } 96 | else { 97 | return new type.UInt8(length); 98 | } 99 | } 100 | 101 | function encode(tag, buffer) { 102 | return new type.Component([encodeTag(tag), encodeLength(buffer.size()), buffer]); 103 | } 104 | 105 | /** 106 | * Module Export 107 | */ 108 | module.exports = { 109 | decode : decode, 110 | encode : encode 111 | }; 112 | -------------------------------------------------------------------------------- /lib/asn1/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var ber = require('./ber'); 21 | var univ = require('./univ'); 22 | var spec = require('./spec'); 23 | 24 | module.exports = { 25 | ber : ber, 26 | univ : univ, 27 | spec : spec 28 | }; 29 | -------------------------------------------------------------------------------- /lib/asn1/spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var type = require('../core').type; 22 | var error = require('../core').error; 23 | 24 | /** 25 | * Tag Class 26 | */ 27 | var TagClass = { 28 | Universal : 0x00, 29 | Application : 0x40, 30 | Context : 0x80, 31 | Private : 0xC0 32 | }; 33 | 34 | /** 35 | * Tag Format 36 | */ 37 | var TagFormat = { 38 | Primitive : 0x00, 39 | Constructed : 0x20 40 | }; 41 | 42 | /** 43 | * ASN.1 tag 44 | * @param tagClass {TagClass} 45 | * @param tagFormat {TagFormat} 46 | * @param tagNumber {integer} 47 | */ 48 | function Asn1Tag(tagClass, tagFormat, tagNumber) { 49 | this.tagClass = tagClass; 50 | this.tagFormat = tagFormat; 51 | this.tagNumber = tagNumber; 52 | } 53 | 54 | /** 55 | * ASN.1 Specification 56 | * @param tag {Asn1Tag} 57 | */ 58 | function Asn1Spec(tag) { 59 | this.tag = tag; 60 | this.opt = false; 61 | } 62 | 63 | /** 64 | * Add an implicit tag 65 | * override tag 66 | * @param tag {Asn1Tag} 67 | * @returns {Asn1Spec} 68 | */ 69 | Asn1Spec.prototype.implicitTag = function(tag) { 70 | this.tag = tag; 71 | return this; 72 | }; 73 | 74 | /** 75 | * Set optional to true 76 | * @returns {Asn1Spec} 77 | */ 78 | Asn1Spec.prototype.optional = function() { 79 | this.opt = true; 80 | return this; 81 | }; 82 | 83 | /** 84 | * Add explicit tag 85 | * Append new tag header to existing tag 86 | * @param tag {Asn1Tag} 87 | * @returns {Asn1SpecExplicitTag} 88 | */ 89 | Asn1Spec.prototype.explicitTag = function(tag) { 90 | return new Asn1SpecExplicitTag(tag, this); 91 | }; 92 | 93 | /** 94 | * Decode must be implemented by all sub type 95 | * @param s {type.Stream} 96 | * @param decoder 97 | */ 98 | Asn1Spec.prototype.decode = function(s, decoder) { 99 | throw new error.FatalError('NODE_RDP_AS1_SPEC_DECODE_NOT_IMPLEMENTED'); 100 | }; 101 | 102 | /** 103 | * Encode must be implemented by all sub type 104 | * @param decoder 105 | */ 106 | Asn1Spec.prototype.encode = function(encoder) { 107 | throw new error.FatalError('NODE_RDP_AS1_SPEC_ENCODE_NOT_IMPLEMENTED'); 108 | }; 109 | 110 | /** 111 | * Component Asn1Spec object 112 | */ 113 | function Asn1SpecExplicitTag(tag, spec) { 114 | Asn1Spec.call(this, tag); 115 | this.spec = spec; 116 | } 117 | 118 | inherits(Asn1SpecExplicitTag, Asn1Spec); 119 | 120 | /** 121 | * Decode first header 122 | * @param s {type.Stream} 123 | * @param decoder 124 | */ 125 | Asn1Spec.prototype.decode = function(s, decoder) { 126 | var specStream = new type.Stream(decoder.decode(s, this.tag).value); 127 | this.spec.decode(specStream, decoder); 128 | }; 129 | 130 | /** 131 | * Module exports 132 | */ 133 | module.exports = { 134 | TagClass : TagClass, 135 | TagFormat : TagFormat, 136 | Asn1Tag : Asn1Tag, 137 | Asn1Spec : Asn1Spec, 138 | Asn1SpecExplicitTag : Asn1SpecExplicitTag 139 | }; 140 | -------------------------------------------------------------------------------- /lib/asn1/univ.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var spec = require('./spec'); 21 | var type = require('../core').type; 22 | var error = require('../core').error; 23 | var inherits = require('util').inherits; 24 | 25 | /** 26 | * ASN.1 Universal tags 27 | * @see http://www.obj-sys.com/asn1tutorial/node124.html 28 | */ 29 | var UniversalTag = { 30 | Boolean : 1, 31 | Integer : 2, 32 | BitString : 3, 33 | OctetString : 4, 34 | Null : 5, 35 | ObjectIdentifier : 6, 36 | ObjectDescriptor : 7, 37 | Enumerate : 10, 38 | UTF8String : 12, 39 | Sequence : 16, 40 | Set : 17, 41 | PrintableString : 19, 42 | T61String : 20, 43 | IA5String : 22, 44 | UTCTime : 23, 45 | GeneralizedTime : 24, 46 | UniversalString : 28, 47 | BMPString : 30 48 | }; 49 | 50 | /** 51 | * Boolean type 52 | * @param value {boolean} inner value 53 | */ 54 | function Boolean(value) { 55 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Boolean)); 56 | this.value = value || false; 57 | } 58 | 59 | inherits(Boolean, spec.Asn1Spec); 60 | 61 | /** 62 | * @param s {type.Stream} 63 | * @param decoder {ber.decoder} 64 | * @returns {Boolean} 65 | */ 66 | Boolean.prototype.decode = function(s, decoder) { 67 | this.value = new type.UInt8().read(new type.Stream(decoder.decode(s, this.tag).value)).value !== 0; 68 | return this; 69 | }; 70 | 71 | /** 72 | * @param decoder {ber.decoder} 73 | * @returns {type.*} 74 | */ 75 | Boolean.prototype.encode = function(encoder) { 76 | if(this.value) { 77 | return encoder.encode(this.tag, new type.UInt8(0xff)); 78 | } 79 | else { 80 | return encoder.encode(this.tag, new type.UInt8(0)); 81 | } 82 | }; 83 | 84 | /** 85 | * Integer type 86 | * @param value {integer | Buffer} 87 | */ 88 | function Integer(value) { 89 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Integer)); 90 | this.value = value || 0; 91 | } 92 | 93 | inherits(Integer, spec.Asn1Spec); 94 | 95 | /** 96 | * @param s {type.Stream} 97 | * @param decoder {ber.decoder} 98 | * @returns {Integer} 99 | */ 100 | Integer.prototype.decode = function(s, decoder) { 101 | var integerBuffer = decoder.decode(s, this.tag).value; 102 | if(integerBuffer.length < 5) { 103 | var integerStream = new type.Stream(integerBuffer); 104 | while (integerStream.availableLength() > 0) { 105 | this.value = this.value << 8; 106 | this.value |= new type.UInt8().read(integerStream).value; 107 | } 108 | } 109 | // bignum case 110 | else { 111 | this.value = integerBuffer; 112 | } 113 | return this; 114 | }; 115 | 116 | /** 117 | * @param encoder {ber.decoder} 118 | * @returns {type.*} 119 | */ 120 | Integer.prototype.encode = function(encoder) { 121 | if(this.value <= 0xff) { 122 | return encoder.encode(this.tag, new type.UInt8(this.value)); 123 | } 124 | else if(this.value <= 0xffff) { 125 | return encoder.encode(this.tag, new type.UInt16Be(this.value)); 126 | } 127 | else { 128 | return encoder.encode(this.tag, new type.UInt32Be(this.value)); 129 | } 130 | }; 131 | 132 | /** 133 | * Sequence type 134 | * @param value {object} 135 | */ 136 | function Sequence(value) { 137 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Constructed, UniversalTag.Sequence)); 138 | this.value = value || []; 139 | } 140 | 141 | inherits(Sequence, spec.Asn1Spec); 142 | 143 | /** 144 | * @param s {type.Stream} 145 | * @param decoder {ber.decoder} 146 | * @returns {Sequence} 147 | */ 148 | Sequence.prototype.decode = function(s, decoder) { 149 | var sequenceStream = new type.Stream(decoder.decode(s, this.tag).value); 150 | for (var i in this.value) { 151 | var rec = sequenceStream.offset; 152 | try { 153 | this.value[i].decode(sequenceStream, decoder); 154 | } catch(e) { 155 | if ((e.message === 'NODE_RDP_ASN1_BER_INVALID_TAG') && !this.value[i].opt) { 156 | throw new error.ProtocolError('NODE_RDP_ASN1_UNIV_SEQUENCE_FIELD_NOT_PRESENT'); 157 | } 158 | sequenceStream.offset = rec; 159 | } 160 | } 161 | return this; 162 | }; 163 | 164 | /** 165 | * Encode sequence 166 | * @param encoder 167 | * @returns {type.Component} 168 | */ 169 | Sequence.prototype.encode = function(encoder) { 170 | var sequence = new type.Component([]); 171 | for (var i in this.value) { 172 | sequence.obj.push(this.value[i].encode(encoder)) 173 | } 174 | return encoder.encode(this.tag, sequence); 175 | }; 176 | 177 | 178 | /** 179 | * Enumerate type 180 | * @param value {integer} 181 | */ 182 | function Enumerate(value) { 183 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Enumerate)); 184 | this.value = value || 0; 185 | } 186 | 187 | inherits(Enumerate, spec.Asn1Spec); 188 | 189 | /** 190 | * @param s {type.Stream} 191 | * @param decoder {ber.decoder} 192 | * @returns {Enumerate} 193 | */ 194 | Enumerate.prototype.decode = function(s, decoder) { 195 | this.value = new type.UInt8().read(new type.Stream(decoder.decode(s, this.tag).value)).value; 196 | return this; 197 | }; 198 | 199 | /** 200 | * Encode enumerate type 201 | * @param encoder 202 | * @returns {type.Component} 203 | */ 204 | Enumerate.prototype.encode = function(encoder) { 205 | return encoder.encode(this.tag, new type.UInt8(this.value)); 206 | }; 207 | 208 | /** 209 | * OctetString type 210 | * @param value {Buffer} 211 | */ 212 | function OctetString(value) { 213 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.OctetString)); 214 | this.value = value || new Buffer(0); 215 | } 216 | 217 | inherits(OctetString, spec.Asn1Spec); 218 | 219 | /** 220 | * @param s {type.Stream} 221 | * @param decoder {ber.decoder} 222 | * @returns {OctetString} 223 | */ 224 | OctetString.prototype.decode = function(s, decoder) { 225 | this.value = decoder.decode(s, this.tag).value; 226 | return this; 227 | }; 228 | 229 | /** 230 | * Encode Octet String 231 | * @param encoder 232 | * @returns {type.Component} 233 | */ 234 | OctetString.prototype.encode = function(encoder) { 235 | return encoder.encode(this.tag, new type.BinaryString(this.value)); 236 | }; 237 | 238 | /** 239 | * ObjectIdentifier type 240 | * @param value {Buffer} 241 | */ 242 | function ObjectIdentifier(value) { 243 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.ObjectIdentifier)); 244 | this.value = value || new Buffer(5); 245 | } 246 | 247 | inherits(ObjectIdentifier, spec.Asn1Spec); 248 | 249 | /** 250 | * @param s {type.Stream} 251 | * @param decoder {ber.decoder} 252 | * @returns {ObjectIdentifier} 253 | */ 254 | ObjectIdentifier.prototype.decode = function(s, decoder) { 255 | this.value = decoder.decode(s, this.tag).value; 256 | return this; 257 | }; 258 | 259 | /** 260 | * Null type 261 | */ 262 | function Null() { 263 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Null)); 264 | } 265 | 266 | inherits(Null, spec.Asn1Spec); 267 | 268 | /** 269 | * @param s {type.Stream} 270 | * @param decoder {ber.decoder} 271 | * @returns {Null} 272 | */ 273 | Null.prototype.decode = function(s, decoder) { 274 | decoder.decode(s, this.tag); 275 | return this; 276 | }; 277 | 278 | /** 279 | * Choice type 280 | * @param value {object} list of available type 281 | */ 282 | function Choice(value) { 283 | // not tagged type 284 | spec.Asn1Spec.call(this, new spec.Asn1Tag()); 285 | this.value = value; 286 | } 287 | 288 | inherits(Choice, spec.Asn1Spec); 289 | 290 | /** 291 | * @param s {type.Stream} 292 | * @param decoder {ber.decoder} 293 | * @returns {Choice} 294 | */ 295 | Choice.prototype.decode = function(s, decoder) { 296 | for (var i in this.value) { 297 | var rec = s.offset; 298 | try { 299 | this.value[i].decode(s, decoder); 300 | break; 301 | } 302 | catch(e) { 303 | s.offset = rec; 304 | } 305 | } 306 | return this; 307 | }; 308 | 309 | /** 310 | * SetOf type 311 | * @param factory {function} type builder 312 | * @param value {object} list of available type 313 | */ 314 | function SetOf(factory, value) { 315 | // not tagged type 316 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Constructed, UniversalTag.Set)); 317 | this.factory = factory; 318 | this.value = value || []; 319 | } 320 | 321 | inherits(SetOf, spec.Asn1Spec); 322 | 323 | /** 324 | * @param s {type.Stream} 325 | * @param decoder {ber.decoder} 326 | * @returns {SetOf} 327 | */ 328 | SetOf.prototype.decode = function(s, decoder) { 329 | var setOfStream = new type.Stream(decoder.decode(s, this.tag).value); 330 | while (setOfStream.availableLength() > 0) { 331 | this.value.push(this.factory().decode(setOfStream, decoder)); 332 | } 333 | return this; 334 | }; 335 | 336 | /** 337 | * SequenceOf type 338 | * @param factory {function} type builder 339 | * @param value {object} list of available type 340 | */ 341 | function SequenceOf(factory, value) { 342 | // not tagged type 343 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Constructed, UniversalTag.Sequence)); 344 | this.factory = factory; 345 | this.value = value || []; 346 | } 347 | 348 | inherits(SequenceOf, spec.Asn1Spec); 349 | 350 | /** 351 | * @param s {type.Stream} 352 | * @param decoder {ber.decoder} 353 | * @returns {SequenceOf} 354 | */ 355 | SequenceOf.prototype.decode = function(s, decoder) { 356 | var sequenceOfStream = new type.Stream(decoder.decode(s, this.tag).value); 357 | while (sequenceOfStream.availableLength() > 0) { 358 | this.value.push(this.factory().decode(sequenceOfStream, decoder)); 359 | } 360 | return this; 361 | }; 362 | 363 | /** 364 | * BitString type 365 | * @param value {Buffer} 366 | */ 367 | function BitString(value) { 368 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.BitString)); 369 | this.value = []; 370 | } 371 | 372 | inherits(BitString, spec.Asn1Spec); 373 | 374 | /** 375 | * @param s {type.Stream} 376 | * @param decoder {ber.decoder} 377 | * @returns {BitString} 378 | */ 379 | BitString.prototype.decode = function(s, decoder) { 380 | var bitStream = new type.Stream(decoder.decode(s, this.tag).value); 381 | var padding = new type.UInt8().read(bitStream).value; 382 | var value = []; 383 | for(var i = 0; i < padding; i++) { 384 | value.push(0); 385 | } 386 | 387 | while(bitStream.availableLength() > 0) { 388 | var octet = new type.UInt8().read(bitStream).value; 389 | var currentPadding = 0; 390 | if(bitStream.availableLength() === 0) { 391 | currentPadding = padding; 392 | } 393 | for(var i = 7; i >= currentPadding; i--) { 394 | value.push(((octet >> i) & 1)?1:0); 395 | } 396 | } 397 | this.value = value; 398 | return this; 399 | }; 400 | 401 | /** 402 | * Convert bit string to buffer object 403 | * @returns {Buffer} 404 | */ 405 | BitString.prototype.toBuffer = function () { 406 | var length = this.value.length / 8; 407 | var resultStream = new type.Stream(length); 408 | for (var i = 0; i < length; i ++) { 409 | var currentOctet = 0; 410 | for (var j = 0; j < 8; j++) { 411 | currentOctet = currentOctet | (this.value[i * 8 + j] << (7 - j)); 412 | } 413 | new type.UInt8(currentOctet).write(resultStream); 414 | } 415 | return resultStream.buffer; 416 | } 417 | 418 | /** 419 | * T61String type 420 | * @param value {Buffer} 421 | */ 422 | function T61String(value) { 423 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.T61String)); 424 | this.value = value; 425 | } 426 | 427 | inherits(T61String, spec.Asn1Spec); 428 | 429 | /** 430 | * @param s {type.Stream} 431 | * @param decoder {ber.decoder} 432 | * @returns {T61String} 433 | */ 434 | T61String.prototype.decode = function(s, decoder) { 435 | this.value = decoder.decode(s, this.tag).value; 436 | return this; 437 | }; 438 | 439 | /** 440 | * PrintableString type 441 | * @param value {Buffer} 442 | */ 443 | function PrintableString(value) { 444 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.PrintableString)); 445 | this.value = value; 446 | } 447 | 448 | inherits(PrintableString, spec.Asn1Spec); 449 | 450 | /** 451 | * @param s {type.Stream} 452 | * @param decoder {ber.decoder} 453 | * @returns {PrintableString} 454 | */ 455 | PrintableString.prototype.decode = function(s, decoder) { 456 | this.value = decoder.decode(s, this.tag).value; 457 | return this; 458 | }; 459 | 460 | /** 461 | * UniversalString type 462 | * @param value {Buffer} 463 | */ 464 | function UniversalString(value) { 465 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.UniversalString)); 466 | this.value = value; 467 | } 468 | 469 | inherits(UniversalString, spec.Asn1Spec); 470 | 471 | /** 472 | * @param s {type.Stream} 473 | * @param decoder {ber.decoder} 474 | * @returns {UniversalString} 475 | */ 476 | UniversalString.prototype.decode = function(s, decoder) { 477 | this.value = decoder.decode(s, this.tag).value; 478 | return this; 479 | }; 480 | 481 | /** 482 | * UTF8String type 483 | * @param value {Buffer} 484 | */ 485 | function UTF8String(value) { 486 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.UTF8String)); 487 | this.value = value; 488 | } 489 | 490 | inherits(UTF8String, spec.Asn1Spec); 491 | 492 | /** 493 | * @param s {type.Stream} 494 | * @param decoder {ber.decoder} 495 | * @returns {UTF8String} 496 | */ 497 | UTF8String.prototype.decode = function(s, decoder) { 498 | this.value = decoder.decode(s, this.tag).value; 499 | return this; 500 | }; 501 | 502 | /** 503 | * BMPString type 504 | * @param value {Buffer} 505 | */ 506 | function BMPString(value) { 507 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.BMPString)); 508 | this.value = value; 509 | } 510 | 511 | inherits(BMPString, spec.Asn1Spec); 512 | 513 | /** 514 | * @param s {type.Stream} 515 | * @param decoder {ber.decoder} 516 | * @returns {BMPString} 517 | */ 518 | BMPString.prototype.decode = function(s, decoder) { 519 | this.value = decoder.decode(s, this.tag).value; 520 | return this; 521 | }; 522 | 523 | /** 524 | * IA5String type 525 | * @param value {Buffer} 526 | */ 527 | function IA5String(value) { 528 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.IA5String)); 529 | this.value = value; 530 | } 531 | 532 | inherits(IA5String, spec.Asn1Spec); 533 | 534 | /** 535 | * @param s {type.Stream} 536 | * @param decoder {ber.decoder} 537 | * @returns {IA5String} 538 | */ 539 | IA5String.prototype.decode = function(s, decoder) { 540 | this.value = decoder.decode(s, this.tag).value; 541 | return this; 542 | }; 543 | 544 | /** 545 | * UTCTime type 546 | * @param value {Buffer} 547 | */ 548 | function UTCTime(value) { 549 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.UTCTime)); 550 | this.value = value; 551 | } 552 | 553 | inherits(UTCTime, spec.Asn1Spec); 554 | 555 | /** 556 | * @param s {type.Stream} 557 | * @param decoder {ber.decoder} 558 | * @returns {UTCTime} 559 | */ 560 | UTCTime.prototype.decode = function(s, decoder) { 561 | this.value = decoder.decode(s, this.tag).value; 562 | return this; 563 | }; 564 | 565 | /** 566 | * GeneralizedTime type 567 | * @param value {Buffer} 568 | */ 569 | function GeneralizedTime(value) { 570 | spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.GeneralizedTime)); 571 | this.value = value; 572 | } 573 | 574 | inherits(GeneralizedTime, spec.Asn1Spec); 575 | 576 | /** 577 | * @param s {type.Stream} 578 | * @param decoder {ber.decoder} 579 | * @returns {GeneralizedTime} 580 | */ 581 | GeneralizedTime.prototype.decode = function(s, decoder) { 582 | this.value = decoder.decode(s, this.tag).value; 583 | return this; 584 | }; 585 | 586 | module.exports = { 587 | Boolean : Boolean, 588 | Integer : Integer, 589 | Sequence : Sequence, 590 | Enumerate : Enumerate, 591 | OctetString : OctetString, 592 | ObjectIdentifier : ObjectIdentifier, 593 | Null : Null, 594 | Choice : Choice, 595 | SequenceOf : SequenceOf, 596 | SetOf : SetOf, 597 | BitString : BitString, 598 | T61String : T61String, 599 | PrintableString : PrintableString, 600 | UniversalString : UniversalString, 601 | UTF8String : UTF8String, 602 | BMPString : BMPString, 603 | IA5String : IA5String, 604 | UTCTime : UTCTime, 605 | GeneralizedTime : GeneralizedTime 606 | }; 607 | -------------------------------------------------------------------------------- /lib/core/error.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | 22 | /** 23 | * Fatal error stop program 24 | */ 25 | function FatalError(message, code) { 26 | Error.captureStackTrace(this); 27 | this.message = message || ""; 28 | this.code = code || 'NODE_RDP_CORE_ERROR_NO_ERROR_CODE'; 29 | } 30 | 31 | /** 32 | * inherit from error 33 | */ 34 | inherits(FatalError, Error); 35 | 36 | /** 37 | * Protocol error (non fatal); 38 | */ 39 | function ProtocolError(code, message) { 40 | Error.captureStackTrace(this); 41 | this.code = code; 42 | this.message = message || ""; 43 | } 44 | 45 | /** 46 | * inherit from error 47 | */ 48 | inherits(ProtocolError, Error); 49 | 50 | /** 51 | * ImplementationError error (non fatal); 52 | */ 53 | function ImplementationError(code, message) { 54 | Error.captureStackTrace(this); 55 | this.code = code; 56 | this.message = message || ""; 57 | } 58 | 59 | /** 60 | * inherit from error 61 | */ 62 | inherits(ImplementationError, Error); 63 | 64 | /** 65 | * Module exports 66 | */ 67 | module.exports = { 68 | FatalError : FatalError, 69 | ProtocolError : ProtocolError, 70 | ImplementationError : ImplementationError 71 | }; -------------------------------------------------------------------------------- /lib/core/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var layer = require('./layer'); 21 | var type = require('./type'); 22 | var log = require('./log'); 23 | var error = require('./error'); 24 | var rle = require('./rle'); 25 | 26 | module.exports = { 27 | layer : layer, 28 | type : type, 29 | log : log, 30 | error : error, 31 | rle : rle 32 | }; 33 | -------------------------------------------------------------------------------- /lib/core/layer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var fs = require('fs'); 22 | var type = require('./type'); 23 | var log = require('./log'); 24 | var tls = require('tls'); 25 | var crypto = require('crypto'); 26 | var events = require('events'); 27 | 28 | /** 29 | * Buffer data from socket to present 30 | * well formed packets 31 | */ 32 | function BufferLayer(socket) { 33 | //for ssl connection 34 | this.secureSocket = null; 35 | this.socket = socket; 36 | 37 | var self = this; 38 | // bind event 39 | this.socket.on('data', function(data) { 40 | try { 41 | self.recv(data); 42 | } 43 | catch(e) { 44 | self.socket.destroy(); 45 | self.emit('error', e); 46 | } 47 | }).on('close', function() { 48 | self.emit('close'); 49 | }).on('error', function (err) { 50 | self.emit('error', err); 51 | }); 52 | 53 | //buffer data 54 | this.buffers = []; 55 | this.bufferLength = 0; 56 | //expected size 57 | this.expectedSize = 0; 58 | } 59 | 60 | inherits(BufferLayer, events.EventEmitter); 61 | 62 | /** 63 | * Call from tcp layer 64 | * @param data tcp stream 65 | */ 66 | BufferLayer.prototype.recv = function(data) { 67 | this.buffers[this.buffers.length] = data; 68 | this.bufferLength += data.length; 69 | 70 | while(this.bufferLength >= this.expectedSize) { 71 | //linear buffer 72 | var expectedData = new type.Stream(this.expectedSize); 73 | 74 | //create expected data 75 | while(expectedData.availableLength() > 0) { 76 | 77 | var rest = expectedData.availableLength(); 78 | var buffer = this.buffers.shift(); 79 | 80 | if(buffer.length > expectedData.availableLength()) { 81 | this.buffers.unshift(buffer.slice(rest)); 82 | new type.BinaryString(buffer, { readLength : new type.CallableValue(expectedData.availableLength()) }).write(expectedData); 83 | } 84 | else { 85 | new type.BinaryString(buffer).write(expectedData); 86 | } 87 | } 88 | 89 | this.bufferLength -= this.expectedSize; 90 | expectedData.offset = 0; 91 | this.emit('data', expectedData); 92 | } 93 | }; 94 | 95 | /** 96 | * Call tcp socket to write stream 97 | * @param {type.Type} packet 98 | */ 99 | BufferLayer.prototype.send = function(data) { 100 | var s = new type.Stream(data.size()); 101 | data.write(s); 102 | if(this.secureSocket) { 103 | this.secureSocket.write(s.buffer); 104 | } 105 | else { 106 | this.socket.write(s.buffer); 107 | } 108 | }; 109 | 110 | /** 111 | * Wait expected size data before call callback function 112 | * @param {number} expectSize size expected 113 | */ 114 | BufferLayer.prototype.expect = function(expectedSize) { 115 | this.expectedSize = expectedSize; 116 | }; 117 | 118 | /** 119 | * Convert connection to TLS connection 120 | * @param callback {func} when connection is done 121 | */ 122 | BufferLayer.prototype.startTLS = function(callback) { 123 | var self = this; 124 | 125 | this.secureSocket = tls.connect({ 126 | socket: this.socket, 127 | secureContext: tls.createSecureContext(), 128 | isServer: false, 129 | requestCert: false, 130 | rejectUnauthorized: false 131 | }, (err) => { 132 | log.warn(err); 133 | callback(err); 134 | }); 135 | 136 | this.secureSocket.on('data', function(data) { 137 | try { 138 | self.recv(data); 139 | } 140 | catch(e) { 141 | self.socket.destroy(); 142 | self.emit('error', e); 143 | } 144 | }).on('error', function (err) { 145 | self.emit('error', err); 146 | }); 147 | }; 148 | 149 | /** 150 | * Convert connection to TLS server 151 | * @param keyFilePath {string} key file path 152 | * @param crtFilePath {string} certificat file path 153 | * @param callback {function} 154 | */ 155 | BufferLayer.prototype.listenTLS = function(keyFilePath, crtFilePath, callback) { 156 | var self = this; 157 | 158 | this.secureSocket = tls.connect({ 159 | socket: this.socket, 160 | secureContext: tls.createSecureContext({ 161 | key: fs.readFileSync(keyFilePath), 162 | cert: fs.readFileSync(crtFilePath), 163 | }), 164 | isServer: true, 165 | requestCert: false, 166 | rejectUnauthorized: false 167 | }, (err) => { 168 | log.warn(err); 169 | callback(err); 170 | }); 171 | 172 | this.secureSocket.on('data', function(data) { 173 | try { 174 | self.recv(data); 175 | } 176 | catch(e) { 177 | self.socket.destroy(); 178 | self.emit('error', e); 179 | } 180 | }).on('error', function (err) { 181 | self.emit('error', err); 182 | }); 183 | }; 184 | 185 | /** 186 | * close stack 187 | */ 188 | BufferLayer.prototype.close = function() { 189 | this.socket.end(); 190 | }; 191 | 192 | /** 193 | * Module exports 194 | */ 195 | module.exports = { 196 | BufferLayer : BufferLayer 197 | }; 198 | -------------------------------------------------------------------------------- /lib/core/log.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var Logger = require('bunyan'); 21 | 22 | var Levels = { 23 | 'DEBUG': 1, 24 | 'INFO': 2, 25 | 'WARN': 3, 26 | 'ERROR': 4 27 | } 28 | 29 | let logStreams = []; 30 | 31 | if (process.env.enable_log_file === 'true') { 32 | logStreams.push( 33 | { 34 | type: 'rotating-file', 35 | period: '1d', 36 | count: 2, 37 | path: `node-rdpjs${process.pid}.log`, 38 | level: process.env.log_level 39 | } 40 | ); 41 | } 42 | 43 | logStreams.push( 44 | { 45 | stream: process.stderr, 46 | level: process.env.log_level 47 | } 48 | ); 49 | 50 | var logger = Logger.createLogger({ 51 | name: 'node-rdpjs', 52 | streams: logStreams 53 | }); 54 | 55 | 56 | function log(level, message) { 57 | if (Levels[level] < module.exports.level) return; 58 | console.log("[node-rdpjs] " + level + ":\t" + message); 59 | } 60 | 61 | /** 62 | * Module exports 63 | */ 64 | module.exports = { 65 | level: Levels.INFO, 66 | Levels: Levels, 67 | debug: function (message) { logger.debug(message); }, 68 | info: function (message) { logger.info(message); }, 69 | warn: function (message) { logger.warn(message); }, 70 | error: function (message) { logger.error(message); } 71 | }; 72 | -------------------------------------------------------------------------------- /lib/core/type.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var log = require('./log'); 22 | var error = require('./error'); 23 | 24 | /** 25 | * Stream wrapper around buffer type 26 | * @param i {Buffer | integer} size of init buffer 27 | * @returns 28 | */ 29 | function Stream(i) { 30 | this.offset = 0; 31 | if (i instanceof Buffer) { 32 | this.buffer = i; 33 | } 34 | else { 35 | this.buffer = new Buffer(i || 8192); 36 | } 37 | } 38 | 39 | /** 40 | * Return length of available data in stream 41 | * @returns {Number} length of available data in stream 42 | */ 43 | Stream.prototype.availableLength = function() { 44 | return this.buffer.length - this.offset; 45 | }; 46 | 47 | /** 48 | * increment offset 49 | * @param length {integer} length of padding 50 | */ 51 | Stream.prototype.readPadding = function(length) { 52 | this.offset += length; 53 | }; 54 | 55 | /** 56 | * Format string buffer 57 | * @returns {string} buffer stringified 58 | */ 59 | Stream.prototype.getValue = function() { 60 | return this.buffer; 61 | }; 62 | 63 | /** 64 | * @param value {object | function} inner value 65 | * @returns 66 | */ 67 | function CallableValue(value) { 68 | if(value) { 69 | this.value = value; 70 | } 71 | } 72 | 73 | /** 74 | * For syntaxic purpose 75 | */ 76 | Object.defineProperty(CallableValue.prototype, "value", { 77 | get: function() { return this._value(); }, 78 | set: function(e) { 79 | if(typeof e !== 'function') { 80 | this._value = function () { return e; }; 81 | } 82 | else { 83 | this._value = e; 84 | } 85 | } 86 | }); 87 | 88 | /** 89 | * Type readable or writable by binary stream 90 | * @param {object} opt 91 | * .conditional {boolean} read or write type depend on conditional call 92 | * @returns 93 | */ 94 | function Type(opt) { 95 | CallableValue.call(this); 96 | this.opt = opt || {}; 97 | this.isReaded = false; 98 | this.isWritten = false; 99 | } 100 | 101 | inherits(Type, CallableValue); 102 | 103 | /** 104 | * Write type into binary stream s 105 | * @param {type.Stream} s binary stream 106 | */ 107 | Type.prototype.write = function(s) { 108 | //do not write false conditional type 109 | if(this.opt.conditional && !this.opt.conditional()) 110 | return this; 111 | 112 | this.isWritten = true; 113 | 114 | this.writeValue(s); 115 | return this; 116 | }; 117 | 118 | /** 119 | * Read type from binary stream 120 | * @param {type.Stream} s binary stream 121 | * @returns this to chain call 122 | */ 123 | Type.prototype.read = function(s) { 124 | //do not read false conditional type 125 | if(this.opt.conditional && !this.opt.conditional()) 126 | return this; 127 | 128 | if(this.opt.optional && s.availableLength() < this.size()) 129 | return this; 130 | 131 | this.isReaded = true; 132 | 133 | //constant case 134 | if(this.opt.constant) { 135 | var oldValue = this.value; 136 | try { 137 | this.readValue(s); 138 | } 139 | catch(e) { 140 | if (e instanceof RangeError) { 141 | throw new error.ProtocolError("NODE_RDP_CORE_TYPE_STREAM_TOO_SMALL"); 142 | } 143 | throw e; 144 | } 145 | 146 | if(oldValue !== this.value) { 147 | log.error('constant value mismatch ' + oldValue + ' != ' + this.value); 148 | throw new error.ProtocolError("NODE_RDP_CORE_TYPE_CONSTANT_VALUE_MISMATCH"); 149 | } 150 | } 151 | else { 152 | try { 153 | this.readValue(s); 154 | } 155 | catch(e) { 156 | if (e instanceof RangeError) { 157 | throw new error.ProtocolError("NODE_RDP_CORE_TYPE_STREAM_TOO_SMALL"); 158 | } 159 | throw e; 160 | } 161 | } 162 | 163 | return this; 164 | }; 165 | 166 | /** 167 | * Size of type 168 | * @returns {int} Size of type 169 | */ 170 | Type.prototype.size = function() { 171 | if(this.opt.conditional && !this.opt.conditional()) 172 | return 0; 173 | return this._size_(); 174 | }; 175 | 176 | /** 177 | * Convert type to stream 178 | * Usefull when you want to buffer 179 | * @returns {Stream} 180 | */ 181 | Type.prototype.toStream = function() { 182 | var result = new Stream(this.size()); 183 | this.write(result); 184 | return result; 185 | }; 186 | 187 | /** 188 | * Node of Raw types 189 | * @param {object} obj composite object 190 | * @param {object} opt Type parameters 191 | */ 192 | function Component(obj, opt) { 193 | Type.call(this, opt); 194 | this.obj = obj; 195 | } 196 | 197 | //inherit from type 198 | inherits(Component, Type); 199 | 200 | /** 201 | * ignore criterion 202 | * @param i {string} index name in obj 203 | * @returns {Boolean} true if can be ignore 204 | */ 205 | Component.prototype.ignore = function(i) { 206 | // ignore meta information 207 | if(i.lastIndexOf("__", 0) === 0) { 208 | return true; 209 | } 210 | // ignore function 211 | if(typeof(this.obj[i]) === 'function') { 212 | return true; 213 | } 214 | return false; 215 | }; 216 | 217 | /** 218 | * Write each sub type into stream 219 | * @param {Stream} s 220 | */ 221 | Component.prototype.writeValue = function(s) { 222 | for(var i in this.obj) { 223 | if(this.ignore(i)) { 224 | continue; 225 | } 226 | try { 227 | this.obj[i].write(s); 228 | } 229 | catch(e) { 230 | log.info('during write of field ' + i); 231 | throw e; 232 | } 233 | } 234 | }; 235 | 236 | /** 237 | * Read each sub type into stream 238 | * @param {Stream} s from read stream 239 | */ 240 | Component.prototype.readValue = function(s) { 241 | var readStream = s; 242 | if(this.opt.readLength) { 243 | readStream = new Stream(s.buffer.slice(s.offset, s.offset + this.opt.readLength.value)); 244 | } 245 | 246 | for(var i in this.obj) { 247 | // ignore meta information 248 | if(this.ignore(i)) { 249 | continue; 250 | } 251 | try { 252 | this.obj[i].read(readStream); 253 | } 254 | catch(e) { 255 | log.info('during read of field ' + i); 256 | throw e; 257 | } 258 | } 259 | 260 | // padding 261 | if (this.opt.readLength) { 262 | s.offset += this.opt.readLength.value; 263 | if (readStream.offset < this.opt.readLength.value) { 264 | log.debug('still have available data : read it as padding'); 265 | } 266 | } 267 | }; 268 | 269 | /** 270 | * Sum size of sub types 271 | */ 272 | Component.prototype._size_ = function() { 273 | var size = 0; 274 | for(var i in this.obj) { 275 | if(this.ignore(i)) { 276 | continue; 277 | } 278 | size += this.obj[i].size(); 279 | } 280 | return size; 281 | }; 282 | 283 | /** 284 | * Leaf of tree type 285 | * @param {number} value of type 286 | * @param {function} readBufferCallback Buffer prototype read function 287 | * @param {function} writeBufferCallback Buffer prototype write function 288 | * @param {object} opt Type parameter 289 | */ 290 | function SingleType(value, nbBytes, readBufferCallback, writeBufferCallback, opt){ 291 | Type.call(this, opt); 292 | this.value = value || 0; 293 | this.nbBytes = nbBytes; 294 | this.readBufferCallback = readBufferCallback; 295 | this.writeBufferCallback = writeBufferCallback; 296 | } 297 | 298 | //inherit from type 299 | inherits(SingleType, Type); 300 | 301 | /** 302 | * Write SingleType value into stream 303 | * @param s 304 | */ 305 | SingleType.prototype.writeValue = function(s) { 306 | this.writeBufferCallback.call(s.buffer, this.value, s.offset); 307 | s.offset += this._size_(); 308 | }; 309 | 310 | /** 311 | * Read SingleType value into stream 312 | * @param {Stream} s from read stream 313 | */ 314 | SingleType.prototype.readValue = function(s) { 315 | this.value = this.readBufferCallback.call(s.buffer, s.offset); 316 | s.offset += this._size_(); 317 | }; 318 | 319 | /** 320 | * Size of single type 321 | * @returns Size of single type 322 | */ 323 | SingleType.prototype._size_ = function() { 324 | return this.nbBytes; 325 | }; 326 | 327 | /** 328 | * Integer on 1 byte 329 | * @param {number | function} value of type 330 | * @param {object} opt Type parameter 331 | * @returns 332 | */ 333 | function UInt8(value, opt) { 334 | SingleType.call(this, value, 1, Buffer.prototype.readUInt8, Buffer.prototype.writeUInt8, opt); 335 | } 336 | 337 | //inherit from type 338 | inherits(UInt8, SingleType); 339 | 340 | /** 341 | * Integer on 2 bytes in Little Endian 342 | * @param {number | function} value to write or compare if constant 343 | * @param {object} opt Type parameter 344 | * @returns 345 | */ 346 | function UInt16Le(value, opt) { 347 | SingleType.call(this, value, 2, Buffer.prototype.readUInt16LE, Buffer.prototype.writeUInt16LE, opt); 348 | } 349 | 350 | //inherit from type 351 | inherits(UInt16Le, SingleType); 352 | 353 | /** 354 | * Integer on 2 bytes in Big Endian 355 | * @param {number | function} value to write or compare if constant 356 | * @param {object} opt Type parameter 357 | * @returns 358 | */ 359 | function UInt16Be(value, opt) { 360 | SingleType.call(this, value, 2, Buffer.prototype.readUInt16BE, Buffer.prototype.writeUInt16BE, opt); 361 | } 362 | 363 | //inherit from type 364 | inherits(UInt16Be, SingleType); 365 | 366 | /** 367 | * Integer on 4 bytes in Little Endian 368 | * @param {number | function} value to write or compare if constant 369 | * @param {object} opt Type parameter 370 | * @returns 371 | */ 372 | function UInt32Le(value, opt) { 373 | SingleType.call(this, value, 4, Buffer.prototype.readUInt32LE, Buffer.prototype.writeUInt32LE, opt); 374 | } 375 | 376 | //inherit from type 377 | inherits(UInt32Le, SingleType); 378 | 379 | /** 380 | * Integer on 4 bytes in Big Endian 381 | * @param {number | function} value to write or compare if constant 382 | * @param {object} opt Type parameter 383 | * @returns 384 | */ 385 | function UInt32Be(value, opt) { 386 | SingleType.call(this, value, 4, Buffer.prototype.readUInt32BE, Buffer.prototype.writeUInt32BE, opt); 387 | } 388 | 389 | //inherit from type 390 | inherits(UInt32Be, SingleType); 391 | 392 | /** 393 | * @param value {Buffer} javascript source string 394 | * @param opt {object} type options 395 | * .readLength {type} length for reading operation 396 | * @returns {type.BinaryString} 397 | */ 398 | function BinaryString(value, opt) { 399 | Type.call(this, opt); 400 | this.value = value || new Buffer(""); 401 | } 402 | 403 | //inherit from type 404 | inherits(BinaryString, Type); 405 | 406 | /** 407 | * Write value into string 408 | * @param s {type.Stream} 409 | */ 410 | BinaryString.prototype.writeValue = function(s) { 411 | this.value.copy(s.buffer, s.offset); 412 | s.offset += this._size_(); 413 | }; 414 | 415 | /** 416 | * Read string from offset to read length if specified or end of stream 417 | * @param s {type.Stream} 418 | */ 419 | BinaryString.prototype.readValue = function(s) { 420 | if(this.opt.readLength) { 421 | this.value = s.buffer.slice(s.offset, s.offset + this.opt.readLength.value); 422 | } 423 | else { 424 | this.value = s.buffer.slice(s.offset); 425 | } 426 | s.offset += this._size_(); 427 | }; 428 | 429 | /** 430 | * @returns {integer} length of string 431 | */ 432 | BinaryString.prototype._size_ = function() { 433 | return this.value.length; 434 | }; 435 | 436 | /** 437 | * Dynamic built type depend on factory function 438 | * @param message {object} parent object 439 | * @param field {string} name of object field 440 | * @param factory {function} factory use to built new type 441 | * @param opt {object} type options 442 | */ 443 | function Factory(factory, opt) { 444 | Type.call(this, opt); 445 | this.factory = factory; 446 | } 447 | 448 | //inherit from type 449 | inherits(Factory, Type); 450 | 451 | /** 452 | * build type and write into stream 453 | * @param s {Stream} input stream 454 | */ 455 | Factory.prototype.writeValue = function(s) { 456 | this.factory(s); 457 | }; 458 | 459 | /** 460 | * build type and read from stream 461 | * @param s {Stream} input stream 462 | */ 463 | Factory.prototype.readValue = function(s) { 464 | this.factory(s); 465 | }; 466 | 467 | /** 468 | * must be never called 469 | */ 470 | Factory.prototype._size_ = function() { 471 | throw new error.FatalError('NODE_RDP_CORE_TYPE_FACTORY_TYPE_HAVE_NO_SIZE'); 472 | }; 473 | 474 | /** 475 | * Module exports 476 | */ 477 | module.exports = { 478 | Stream : Stream, 479 | Component : Component, 480 | UInt8 : UInt8, 481 | UInt16Le : UInt16Le, 482 | UInt16Be : UInt16Be, 483 | UInt32Le : UInt32Le, 484 | UInt32Be : UInt32Be, 485 | BinaryString : BinaryString, 486 | CallableValue : CallableValue, 487 | Factory : Factory 488 | }; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var protocol = require('./protocol'); 21 | module.exports = { 22 | createClient : protocol.rdp.createClient, 23 | createServer : protocol.rdp.createServer 24 | }; 25 | -------------------------------------------------------------------------------- /lib/protocol/cert.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var type = require('../core').type; 21 | var log = require('../core').log; 22 | var x509 = require('../security').x509; 23 | var rsa = require('../security').rsa; 24 | var asn1 = require('../asn1'); 25 | 26 | /** 27 | *  @see http://msdn.microsoft.com/en-us/library/cc240521.aspx 28 | */ 29 | var CertificateType = { 30 | CERT_CHAIN_VERSION_1 : 0x00000001, 31 | CERT_CHAIN_VERSION_2 : 0x00000002 32 | }; 33 | 34 | /** 35 | * @see http://msdn.microsoft.com/en-us/library/cc240520.aspx 36 | * @returns 37 | */ 38 | function rsaPublicKey(opt) { 39 | var self = { 40 | magic : new type.UInt32Le(0x31415352, { constant : true }), 41 | keylen : new type.UInt32Le(function() { 42 | return self.modulus.size() + self.paddinf.size(); 43 | }), 44 | bitlen : new type.UInt32Le(function() { 45 | return (self.keylen.value - 8) * 8; 46 | }), 47 | datalen : new type.UInt32Le(function() { 48 | return (self.bitlen.value / 8) - 1; 49 | }), 50 | pubExp : new type.UInt32Le(), 51 | modulus : new type.BinaryString(null, { readLength : new type.CallableValue(function() { 52 | return self.keylen.value - 8; 53 | }) }), 54 | padding : new type.BinaryString(new Buffer(Array(8 + 1).join('\x00')), { readLength : new type.CallableValue(8) }) 55 | }; 56 | 57 | return new type.Component(self, opt); 58 | } 59 | 60 | /** 61 | * http://msdn.microsoft.com/en-us/library/cc240519.aspx 62 | * @returns {type.Component} 63 | */ 64 | function proprietaryCertificate() { 65 | var self = { 66 | __TYPE__ : CertificateType.CERT_CHAIN_VERSION_1, 67 | dwSigAlgId : new type.UInt32Le(0x00000001, { constant : true }), 68 | dwKeyAlgId : new type.UInt32Le(0x00000001, { constant : true }), 69 | wPublicKeyBlobType : new type.UInt16Le(0x0006, { constant : true }), 70 | wPublicKeyBlobLen : new type.UInt16Le(function() { 71 | return self.PublicKeyBlob.size(); 72 | }), 73 | PublicKeyBlob : rsaPublicKey({ readLength : new type.CallableValue(function() { 74 | return self.wPublicKeyBlobLen.value; 75 | }) }), 76 | wSignatureBlobType : new type.UInt16Le(0x0008, { constant : true }), 77 | wSignatureBlobLen : new type.UInt16Le(function() { 78 | return self.SignatureBlob.size() + self.padding.size(); 79 | }), 80 | SignatureBlob : new type.BinaryString(null, { readLength : new type.CallableValue(function() { 81 | return self.wSignatureBlobLen.value - self.padding.size; 82 | }) }), 83 | padding : new type.BinaryString(Array(8 + 1).join('\x00'), { readLength : new type.CallableValue(8) }), 84 | /** 85 | * @return {object} rsa.publicKey 86 | */ 87 | getPublicKey : function() { 88 | return rsa.publicKey(self.PublicKeyBlob.obj.modulus.value, self.PublicKeyBlob.obj.pubExp.value); 89 | } 90 | }; 91 | 92 | return new type.Component(self); 93 | } 94 | 95 | /** 96 | * For x509 certificate 97 | * @see http://msdn.microsoft.com/en-us/library/cc241911.aspx 98 | * @returns {type.Component} 99 | */ 100 | function certBlob() { 101 | var self = { 102 | cbCert : new type.UInt32Le(function() { 103 | return self.abCert.size(); 104 | }), 105 | abCert : new type.BinaryString(null, { readLength : new type.CallableValue(function() { 106 | return self.cbCert.value; 107 | }) }) 108 | }; 109 | 110 | return new type.Component(self); 111 | } 112 | 113 | /** 114 | * x509 certificate chain 115 | * @see http://msdn.microsoft.com/en-us/library/cc241910.aspx 116 | * @returns {type.Component} 117 | */ 118 | function x509CertificateChain() { 119 | var self = { 120 | __TYPE__ : CertificateType.CERT_CHAIN_VERSION_2, 121 | NumCertBlobs : new type.UInt32Le(), 122 | CertBlobArray : new type.Factory(function(s) { 123 | self.CertBlobArray = new type.Component([]); 124 | for(var i = 0; i < self.NumCertBlobs.value; i++) { 125 | self.CertBlobArray.obj.push(certBlob().read(s)); 126 | } 127 | }), 128 | padding : new type.BinaryString(null, { readLength : new type.CallableValue(function() { 129 | return 8 + 4 * self.NumCertBlobs.value; 130 | }) }), 131 | /** 132 | * @return {object} {n : modulus{bignum}, e : publicexponent{integer} 133 | */ 134 | getPublicKey : function(){ 135 | var cert = x509.X509Certificate().decode(new type.Stream(self.CertBlobArray.obj[self.CertBlobArray.obj.length - 1].obj.abCert.value), asn1.ber); 136 | var publikeyStream = new type.Stream(cert.value.tbsCertificate.value.subjectPublicKeyInfo.value.subjectPublicKey.toBuffer()); 137 | var asn1PublicKey = x509.RSAPublicKey().decode(publikeyStream, asn1.ber); 138 | return rsa.publicKey(asn1PublicKey.value.modulus.value, asn1PublicKey.value.publicExponent.value); 139 | } 140 | }; 141 | 142 | return new type.Component(self); 143 | } 144 | 145 | function certificate() { 146 | var self = { 147 | dwVersion : new type.UInt32Le(function() { 148 | return self.certData.__TYPE__; 149 | }), 150 | certData : new type.Factory(function(s) { 151 | switch(self.dwVersion.value & 0x7fffffff) { 152 | case CertificateType.CERT_CHAIN_VERSION_1: 153 | log.debug('read proprietary certificate'); 154 | self.certData = proprietaryCertificate().read(s); 155 | break; 156 | case CertificateType.CERT_CHAIN_VERSION_2: 157 | log.debug('read x.509 certificate chain'); 158 | self.certData = x509CertificateChain().read(s); 159 | break; 160 | default: 161 | log.error('unknown cert type ' + self.dwVersion.value & 0x7fffffff); 162 | } 163 | }) 164 | }; 165 | 166 | return new type.Component(self); 167 | } 168 | 169 | /** 170 | * Module exports 171 | */ 172 | module.exports = { 173 | CertificateType : CertificateType, 174 | certificate : certificate 175 | }; -------------------------------------------------------------------------------- /lib/protocol/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var TPKT = require('./tpkt'); 21 | var x224 = require('./x224'); 22 | var t125 = require('./t125'); 23 | var rdp = require('./rdp'); 24 | 25 | module.exports = { 26 | TPKT : TPKT, 27 | x224 : x224, 28 | t125 : t125, 29 | rdp : rdp 30 | }; 31 | -------------------------------------------------------------------------------- /lib/protocol/pdu/caps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var type = require('../../core').type; 21 | var log = require('../../core').log; 22 | var error = require('../../core').error; 23 | 24 | /** 25 | * @see http://msdn.microsoft.com/en-us/library/cc240486.aspx 26 | */ 27 | var CapsType = { 28 | CAPSTYPE_GENERAL : 0x0001, 29 | CAPSTYPE_BITMAP : 0x0002, 30 | CAPSTYPE_ORDER : 0x0003, 31 | CAPSTYPE_BITMAPCACHE : 0x0004, 32 | CAPSTYPE_CONTROL : 0x0005, 33 | CAPSTYPE_ACTIVATION : 0x0007, 34 | CAPSTYPE_POINTER : 0x0008, 35 | CAPSTYPE_SHARE : 0x0009, 36 | CAPSTYPE_COLORCACHE : 0x000A, 37 | CAPSTYPE_SOUND : 0x000C, 38 | CAPSTYPE_INPUT : 0x000D, 39 | CAPSTYPE_FONT : 0x000E, 40 | CAPSTYPE_BRUSH : 0x000F, 41 | CAPSTYPE_GLYPHCACHE : 0x0010, 42 | CAPSTYPE_OFFSCREENCACHE : 0x0011, 43 | CAPSTYPE_BITMAPCACHE_HOSTSUPPORT : 0x0012, 44 | CAPSTYPE_BITMAPCACHE_REV2 : 0x0013, 45 | CAPSTYPE_VIRTUALCHANNEL : 0x0014, 46 | CAPSTYPE_DRAWNINEGRIDCACHE : 0x0015, 47 | CAPSTYPE_DRAWGDIPLUS : 0x0016, 48 | CAPSTYPE_RAIL : 0x0017, 49 | CAPSTYPE_WINDOW : 0x0018, 50 | CAPSETTYPE_COMPDESK : 0x0019, 51 | CAPSETTYPE_MULTIFRAGMENTUPDATE : 0x001A, 52 | CAPSETTYPE_LARGE_POINTER : 0x001B, 53 | CAPSETTYPE_SURFACE_COMMANDS : 0x001C, 54 | CAPSETTYPE_BITMAP_CODECS : 0x001D, 55 | CAPSSETTYPE_FRAME_ACKNOWLEDGE : 0x001E 56 | }; 57 | 58 | /** 59 | * @see http://msdn.microsoft.com/en-us/library/cc240549.aspx 60 | */ 61 | var MajorType = { 62 | OSMAJORTYPE_UNSPECIFIED : 0x0000, 63 | OSMAJORTYPE_WINDOWS : 0x0001, 64 | OSMAJORTYPE_OS2 : 0x0002, 65 | OSMAJORTYPE_MACINTOSH : 0x0003, 66 | OSMAJORTYPE_UNIX : 0x0004, 67 | OSMAJORTYPE_IOS : 0x0005, 68 | OSMAJORTYPE_OSX : 0x0006, 69 | OSMAJORTYPE_ANDROID : 0x0007 70 | }; 71 | 72 | /** 73 | * @see http://msdn.microsoft.com/en-us/library/cc240549.aspx 74 | */ 75 | var MinorType = { 76 | OSMINORTYPE_UNSPECIFIED : 0x0000, 77 | OSMINORTYPE_WINDOWS_31X : 0x0001, 78 | OSMINORTYPE_WINDOWS_95 : 0x0002, 79 | OSMINORTYPE_WINDOWS_NT : 0x0003, 80 | OSMINORTYPE_OS2_V21 : 0x0004, 81 | OSMINORTYPE_POWER_PC : 0x0005, 82 | OSMINORTYPE_MACINTOSH : 0x0006, 83 | OSMINORTYPE_NATIVE_XSERVER : 0x0007, 84 | OSMINORTYPE_PSEUDO_XSERVER : 0x0008, 85 | OSMINORTYPE_WINDOWS_RT : 0x0009 86 | }; 87 | 88 | /** 89 | * @see http://msdn.microsoft.com/en-us/library/cc240549.aspx 90 | */ 91 | var GeneralExtraFlag = { 92 | FASTPATH_OUTPUT_SUPPORTED : 0x0001, 93 | NO_BITMAP_COMPRESSION_HDR : 0x0400, 94 | LONG_CREDENTIALS_SUPPORTED : 0x0004, 95 | AUTORECONNECT_SUPPORTED : 0x0008, 96 | ENC_SALTED_CHECKSUM : 0x0010 97 | }; 98 | 99 | var Boolean = { 100 | FALSE : 0x00, 101 | TRUE : 0x01 102 | }; 103 | 104 | /** 105 | * @see http://msdn.microsoft.com/en-us/library/cc240556.aspx 106 | */ 107 | var OrderFlag = { 108 | NEGOTIATEORDERSUPPORT : 0x0002, 109 | ZEROBOUNDSDELTASSUPPORT : 0x0008, 110 | COLORINDEXSUPPORT : 0x0020, 111 | SOLIDPATTERNBRUSHONLY : 0x0040, 112 | ORDERFLAGS_EXTRA_FLAGS : 0x0080 113 | }; 114 | 115 | /** 116 | * @see http://msdn.microsoft.com/en-us/library/cc240556.aspx 117 | */ 118 | var Order = { 119 | TS_NEG_DSTBLT_INDEX : 0x00, 120 | TS_NEG_PATBLT_INDEX : 0x01, 121 | TS_NEG_SCRBLT_INDEX : 0x02, 122 | TS_NEG_MEMBLT_INDEX : 0x03, 123 | TS_NEG_MEM3BLT_INDEX : 0x04, 124 | TS_NEG_DRAWNINEGRID_INDEX : 0x07, 125 | TS_NEG_LINETO_INDEX : 0x08, 126 | TS_NEG_MULTI_DRAWNINEGRID_INDEX : 0x09, 127 | TS_NEG_SAVEBITMAP_INDEX : 0x0B, 128 | TS_NEG_MULTIDSTBLT_INDEX : 0x0F, 129 | TS_NEG_MULTIPATBLT_INDEX : 0x10, 130 | TS_NEG_MULTISCRBLT_INDEX : 0x11, 131 | TS_NEG_MULTIOPAQUERECT_INDEX : 0x12, 132 | TS_NEG_FAST_INDEX_INDEX : 0x13, 133 | TS_NEG_POLYGON_SC_INDEX : 0x14, 134 | TS_NEG_POLYGON_CB_INDEX : 0x15, 135 | TS_NEG_POLYLINE_INDEX : 0x16, 136 | TS_NEG_FAST_GLYPH_INDEX : 0x18, 137 | TS_NEG_ELLIPSE_SC_INDEX : 0x19, 138 | TS_NEG_ELLIPSE_CB_INDEX : 0x1A, 139 | TS_NEG_INDEX_INDEX : 0x1B 140 | }; 141 | 142 | var OrderEx = { 143 | ORDERFLAGS_EX_CACHE_BITMAP_REV3_SUPPORT : 0x0002, 144 | ORDERFLAGS_EX_ALTSEC_FRAME_MARKER_SUPPORT : 0x0004 145 | }; 146 | 147 | /** 148 | * @see http://msdn.microsoft.com/en-us/library/cc240563.aspx 149 | */ 150 | var InputFlags = { 151 | INPUT_FLAG_SCANCODES : 0x0001, 152 | INPUT_FLAG_MOUSEX : 0x0004, 153 | INPUT_FLAG_FASTPATH_INPUT : 0x0008, 154 | INPUT_FLAG_UNICODE : 0x0010, 155 | INPUT_FLAG_FASTPATH_INPUT2 : 0x0020, 156 | INPUT_FLAG_UNUSED1 : 0x0040, 157 | INPUT_FLAG_UNUSED2 : 0x0080, 158 | TS_INPUT_FLAG_MOUSE_HWHEEL : 0x0100 159 | }; 160 | 161 | /** 162 | * @see http://msdn.microsoft.com/en-us/library/cc240564.aspx 163 | */ 164 | var BrushSupport = { 165 | BRUSH_DEFAULT : 0x00000000, 166 | BRUSH_COLOR_8x8 : 0x00000001, 167 | BRUSH_COLOR_FULL : 0x00000002 168 | }; 169 | 170 | /** 171 | * @see http://msdn.microsoft.com/en-us/library/cc240565.aspx 172 | */ 173 | var GlyphSupport = { 174 | GLYPH_SUPPORT_NONE : 0x0000, 175 | GLYPH_SUPPORT_PARTIAL : 0x0001, 176 | GLYPH_SUPPORT_FULL : 0x0002, 177 | GLYPH_SUPPORT_ENCODE : 0x0003 178 | }; 179 | 180 | /** 181 | * @see http://msdn.microsoft.com/en-us/library/cc240550.aspx 182 | */ 183 | var OffscreenSupportLevel = { 184 | FALSE : 0x00000000, 185 | TRUE : 0x00000001 186 | }; 187 | 188 | /** 189 | * @see http://msdn.microsoft.com/en-us/library/cc240551.aspx 190 | */ 191 | var VirtualChannelCompressionFlag = { 192 | VCCAPS_NO_COMPR : 0x00000000, 193 | VCCAPS_COMPR_SC : 0x00000001, 194 | VCCAPS_COMPR_CS_8K : 0x00000002 195 | }; 196 | 197 | /** 198 | * @see http://msdn.microsoft.com/en-us/library/cc240552.aspx 199 | */ 200 | var SoundFlag = { 201 | NONE : 0x0000, 202 | SOUND_BEEPS_FLAG : 0x0001 203 | }; 204 | 205 | /** 206 | * @see http://msdn.microsoft.com/en-us/library/cc240549.aspx 207 | * @param opt {object} type options 208 | * @returns {type.Component} 209 | */ 210 | function generalCapability(opt) { 211 | var self = { 212 | __TYPE__ : CapsType.CAPSTYPE_GENERAL, 213 | osMajorType : new type.UInt16Le(), 214 | osMinorType : new type.UInt16Le(), 215 | protocolVersion : new type.UInt16Le(0x0200, {constant : true}), 216 | pad2octetsA : new type.UInt16Le(), 217 | generalCompressionTypes : new type.UInt16Le(0, {constant : true}), 218 | extraFlags : new type.UInt16Le(), 219 | updateCapabilityFlag : new type.UInt16Le(0, {constant : true}), 220 | remoteUnshareFlag : new type.UInt16Le(0, {constant : true}), 221 | generalCompressionLevel : new type.UInt16Le(0, {constant : true}), 222 | refreshRectSupport : new type.UInt8(), 223 | suppressOutputSupport : new type.UInt8() 224 | }; 225 | 226 | return new type.Component(self, opt); 227 | } 228 | 229 | /** 230 | * @see http://msdn.microsoft.com/en-us/library/cc240554.aspx 231 | * @param opt {object} type options 232 | * @returns {type.Component} 233 | */ 234 | function bitmapCapability(opt) { 235 | var self = { 236 | __TYPE__ : CapsType.CAPSTYPE_BITMAP, 237 | preferredBitsPerPixel : new type.UInt16Le(), 238 | receive1BitPerPixel : new type.UInt16Le(0x0001), 239 | receive4BitsPerPixel : new type.UInt16Le(0x0001), 240 | receive8BitsPerPixel : new type.UInt16Le(0x0001), 241 | desktopWidth : new type.UInt16Le(), 242 | desktopHeight : new type.UInt16Le(), 243 | pad2octets : new type.UInt16Le(), 244 | desktopResizeFlag : new type.UInt16Le(), 245 | bitmapCompressionFlag : new type.UInt16Le(0x0001, {constant : true}), 246 | highColorFlags : new type.UInt8(0), 247 | drawingFlags : new type.UInt8(), 248 | multipleRectangleSupport : new type.UInt16Le(0x0001, {constant : true}), 249 | pad2octetsB : new type.UInt16Le() 250 | }; 251 | 252 | return new type.Component(self, opt); 253 | } 254 | 255 | /** 256 | * @see http://msdn.microsoft.com/en-us/library/cc240556.aspx 257 | * @param orders {type.BinaryString|null} list of available orders 258 | * @param opt {object} type options 259 | * @returns {type.Component} 260 | */ 261 | function orderCapability(orders, opt) { 262 | if(orders && orders.size() !== 32) { 263 | throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_CAPS_BAD_ORDERS_SIZE'); 264 | } 265 | 266 | var self = { 267 | __TYPE__ : CapsType.CAPSTYPE_ORDER, 268 | terminalDescriptor : new type.BinaryString(new Buffer(Array(16 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(16)}), 269 | pad4octetsA : new type.UInt32Le(0), 270 | desktopSaveXGranularity : new type.UInt16Le(1), 271 | desktopSaveYGranularity : new type.UInt16Le(20), 272 | pad2octetsA : new type.UInt16Le(0), 273 | maximumOrderLevel : new type.UInt16Le(1), 274 | numberFonts : new type.UInt16Le(), 275 | orderFlags : new type.UInt16Le(OrderFlag.NEGOTIATEORDERSUPPORT), 276 | orderSupport : orders || new type.Factory(function(s) { 277 | self.orderSupport = new type.BinaryString(null, {readLength : new type.CallableValue(32)}).read(s); 278 | }), 279 | textFlags : new type.UInt16Le(), 280 | orderSupportExFlags : new type.UInt16Le(), 281 | pad4octetsB : new type.UInt32Le(), 282 | desktopSaveSize : new type.UInt32Le(480 * 480), 283 | pad2octetsC : new type.UInt16Le(), 284 | pad2octetsD : new type.UInt16Le(), 285 | textANSICodePage : new type.UInt16Le(0), 286 | pad2octetsE : new type.UInt16Le() 287 | }; 288 | 289 | return new type.Component(self, opt); 290 | } 291 | 292 | /** 293 | * @see http://msdn.microsoft.com/en-us/library/cc240559.aspx 294 | * @param opt type options 295 | * @returns {type.Component} 296 | */ 297 | function bitmapCacheCapability(opt) { 298 | var self = { 299 | __TYPE__ : CapsType.CAPSTYPE_BITMAPCACHE, 300 | pad1 : new type.UInt32Le(), 301 | pad2 : new type.UInt32Le(), 302 | pad3 : new type.UInt32Le(), 303 | pad4 : new type.UInt32Le(), 304 | pad5 : new type.UInt32Le(), 305 | pad6 : new type.UInt32Le(), 306 | cache0Entries : new type.UInt16Le(), 307 | cache0MaximumCellSize : new type.UInt16Le(), 308 | cache1Entries : new type.UInt16Le(), 309 | cache1MaximumCellSize : new type.UInt16Le(), 310 | cache2Entries : new type.UInt16Le(), 311 | cache2MaximumCellSize : new type.UInt16Le() 312 | }; 313 | 314 | return new type.Component(self, opt); 315 | } 316 | 317 | /** 318 | * 319 | * @param isServer {boolean} true if in server mode 320 | * @param opt {object} type options 321 | * @returns {type.Component} 322 | */ 323 | function pointerCapability(isServer, opt) { 324 | var self = { 325 | __TYPE__ : CapsType.CAPSTYPE_POINTER, 326 | colorPointerFlag : new type.UInt16Le(), 327 | colorPointerCacheSize : new type.UInt16Le(20), 328 | //old version of rdp doesn't support ... 329 | pointerCacheSize : new type.UInt16Le(null, {conditional : function() { 330 | return isServer || false; 331 | }}) 332 | }; 333 | 334 | return new type.Component(self, opt); 335 | } 336 | 337 | /** 338 | * @see http://msdn.microsoft.com/en-us/library/cc240563.aspx 339 | * @param opt {object} type options 340 | * @returns {type.Component} 341 | */ 342 | function inputCapability(opt) { 343 | var self = { 344 | __TYPE__ : CapsType.CAPSTYPE_INPUT, 345 | inputFlags : new type.UInt16Le(), 346 | pad2octetsA : new type.UInt16Le(), 347 | // same value as gcc.ClientCoreSettings.kbdLayout 348 | keyboardLayout : new type.UInt32Le(), 349 | // same value as gcc.ClientCoreSettings.keyboardType 350 | keyboardType : new type.UInt32Le(), 351 | // same value as gcc.ClientCoreSettings.keyboardSubType 352 | keyboardSubType : new type.UInt32Le(), 353 | // same value as gcc.ClientCoreSettings.keyboardFnKeys 354 | keyboardFunctionKey : new type.UInt32Le(), 355 | // same value as gcc.ClientCoreSettingrrs.imeFileName 356 | imeFileName : new type.BinaryString(new Buffer(Array(64 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(64)}) 357 | }; 358 | 359 | return new type.Component(self, opt); 360 | } 361 | 362 | /** 363 | * @see http://msdn.microsoft.com/en-us/library/cc240564.aspx 364 | * @param opt {object} type options 365 | * @returns {type.Component} 366 | */ 367 | function brushCapability(opt) { 368 | var self = { 369 | __TYPE__ : CapsType.CAPSTYPE_BRUSH, 370 | brushSupportLevel : new type.UInt32Le(BrushSupport.BRUSH_DEFAULT) 371 | }; 372 | 373 | return new type.Component(self, opt); 374 | } 375 | 376 | /** 377 | * @see http://msdn.microsoft.com/en-us/library/cc240566.aspx 378 | * @param opt {object} type options 379 | * @returns {type.Component} 380 | */ 381 | function cacheEntry(opt) { 382 | var self = { 383 | cacheEntries : new type.UInt16Le(), 384 | cacheMaximumCellSize : new type.UInt16Le() 385 | }; 386 | 387 | return new type.Component(self, opt); 388 | } 389 | 390 | /** 391 | * @see http://msdn.microsoft.com/en-us/library/cc240565.aspx 392 | * @param entries {type.Component} cache entries 393 | * @param opt {object} type options 394 | * @returns {type.Component} 395 | */ 396 | function glyphCapability(entries, opt) { 397 | var self = { 398 | __TYPE__ : CapsType.CAPSTYPE_GLYPHCACHE, 399 | glyphCache : entries || new type.Factory(function(s) { 400 | self.glyphCache = new type.Component([]); 401 | for(var i = 0; i < 10; i++) { 402 | self.glyphCache.obj.push(cacheEntry().read(s)); 403 | } 404 | }), 405 | fragCache : new type.UInt32Le(), 406 | // all fonts are sent with bitmap format (very expensive) 407 | glyphSupportLevel : new type.UInt16Le(GlyphSupport.GLYPH_SUPPORT_NONE), 408 | pad2octets : new type.UInt16Le() 409 | }; 410 | 411 | return new type.Component(self, opt); 412 | } 413 | 414 | /** 415 | * @see http://msdn.microsoft.com/en-us/library/cc240550.aspx 416 | * @param opt {object} type options 417 | * @returns {type.Component} 418 | */ 419 | function offscreenBitmapCacheCapability(opt) { 420 | var self = { 421 | __TYPE__ : CapsType.CAPSTYPE_OFFSCREENCACHE, 422 | offscreenSupportLevel : new type.UInt32Le(OffscreenSupportLevel.FALSE), 423 | offscreenCacheSize : new type.UInt16Le(), 424 | offscreenCacheEntries : new type.UInt16Le() 425 | }; 426 | 427 | return new type.Component(self, opt); 428 | } 429 | 430 | /** 431 | * @see http://msdn.microsoft.com/en-us/library/cc240551.aspx 432 | * @param opt {object} type options 433 | * @returns {type.Component} 434 | */ 435 | function virtualChannelCapability(opt) { 436 | var self = { 437 | __TYPE__ : CapsType.CAPSTYPE_VIRTUALCHANNEL, 438 | flags : new type.UInt32Le(VirtualChannelCompressionFlag.VCCAPS_NO_COMPR), 439 | VCChunkSize : new type.UInt32Le(null, {optional : true}) 440 | }; 441 | 442 | return new type.Component(self, opt); 443 | } 444 | 445 | /** 446 | * @see http://msdn.microsoft.com/en-us/library/cc240552.aspx 447 | * @param opt {object} type options 448 | * @returns {type.Component} 449 | */ 450 | function soundCapability(opt) { 451 | var self = { 452 | __TYPE__ : CapsType.CAPSTYPE_SOUND, 453 | soundFlags : new type.UInt16Le(SoundFlag.NONE), 454 | pad2octetsA : new type.UInt16Le() 455 | }; 456 | 457 | return new type.Component(self, opt); 458 | } 459 | 460 | /** 461 | * @see http://msdn.microsoft.com/en-us/library/cc240568.aspx 462 | * @param opt {object} type options 463 | * @returns {type.Component} 464 | */ 465 | function controlCapability(opt) { 466 | var self = { 467 | __TYPE__ : CapsType.CAPSTYPE_CONTROL, 468 | controlFlags : new type.UInt16Le(), 469 | remoteDetachFlag : new type.UInt16Le(), 470 | controlInterest : new type.UInt16Le(0x0002), 471 | detachInterest : new type.UInt16Le(0x0002) 472 | }; 473 | 474 | return new type.Component(self, opt); 475 | } 476 | 477 | /** 478 | * @see http://msdn.microsoft.com/en-us/library/cc240569.aspx 479 | * @param opt {object} type options 480 | * @returns {type.Component} 481 | */ 482 | function windowActivationCapability(opt) { 483 | var self = { 484 | __TYPE__ : CapsType.CAPSTYPE_ACTIVATION, 485 | helpKeyFlag : new type.UInt16Le(), 486 | helpKeyIndexFlag : new type.UInt16Le(), 487 | helpExtendedKeyFlag : new type.UInt16Le(), 488 | windowManagerKeyFlag : new type.UInt16Le() 489 | }; 490 | 491 | return new type.Component(self, opt); 492 | } 493 | 494 | /** 495 | * @see http://msdn.microsoft.com/en-us/library/cc240571.aspx 496 | * @param opt {object} type options 497 | * @returns {type.Component} 498 | */ 499 | function fontCapability(opt) { 500 | var self = { 501 | __TYPE__ : CapsType.CAPSTYPE_FONT, 502 | fontSupportFlags : new type.UInt16Le(0x0001), 503 | pad2octets : new type.UInt16Le() 504 | }; 505 | 506 | return new type.Component(self, opt); 507 | } 508 | 509 | /** 510 | * @see http://msdn.microsoft.com/en-us/library/cc241564.aspx 511 | * @param opt {object} type options 512 | * @returns {type.Component} 513 | */ 514 | function colorCacheCapability(opt) { 515 | var self = { 516 | __TYPE__ : CapsType.CAPSTYPE_COLORCACHE, 517 | colorTableCacheSize : new type.UInt16Le(0x0006), 518 | pad2octets : new type.UInt16Le() 519 | }; 520 | 521 | return new type.Component(self, opt); 522 | } 523 | 524 | /** 525 | * @see http://msdn.microsoft.com/en-us/library/cc240570.aspx 526 | * @param opt {object} type options 527 | * @returns {type.Component} 528 | */ 529 | function shareCapability(opt) { 530 | var self = { 531 | __TYPE__ : CapsType.CAPSTYPE_SHARE, 532 | nodeId : new type.UInt16Le(), 533 | pad2octets : new type.UInt16Le() 534 | }; 535 | 536 | return new type.Component(self, opt); 537 | } 538 | 539 | /** 540 | * @see http://msdn.microsoft.com/en-us/library/cc240649.aspx 541 | * @param opt {object} type options 542 | * @returns {type.Component} 543 | */ 544 | function multiFragmentUpdate(opt) { 545 | var self = { 546 | __TYPE__ : CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE, 547 | MaxRequestSize : new type.UInt32Le(0) 548 | }; 549 | 550 | return new type.Component(self, opt); 551 | } 552 | 553 | /** 554 | * Capability wrapper packet 555 | * @see http://msdn.microsoft.com/en-us/library/cc240486.aspx 556 | * @param cap {type.Component} 557 | * @param opt {object} type options 558 | * @returns {type.Component} 559 | */ 560 | function capability(cap, opt) { 561 | var self = { 562 | capabilitySetType : new type.UInt16Le(function() { 563 | return self.capability.obj.__TYPE__; 564 | }), 565 | lengthCapability : new type.UInt16Le(function() { 566 | return new type.Component(self).size(); 567 | }), 568 | capability : cap || new type.Factory(function(s) { 569 | switch(self.capabilitySetType.value) { 570 | case CapsType.CAPSTYPE_GENERAL: 571 | self.capability = generalCapability({readLength : new type.CallableValue(function() { 572 | return self.lengthCapability.value - 4; 573 | })}).read(s); 574 | break; 575 | case CapsType.CAPSTYPE_BITMAP: 576 | self.capability = bitmapCapability({readLength : new type.CallableValue(function() { 577 | return self.lengthCapability.value - 4; 578 | })}).read(s); 579 | break; 580 | case CapsType.CAPSTYPE_ORDER: 581 | self.capability = orderCapability(null, {readLength : new type.CallableValue(function() { 582 | return self.lengthCapability.value - 4; 583 | })}).read(s); 584 | break; 585 | case CapsType.CAPSTYPE_BITMAPCACHE: 586 | self.capability = bitmapCacheCapability({readLength : new type.CallableValue(function() { 587 | return self.lengthCapability.value - 4; 588 | })}).read(s); 589 | break; 590 | case CapsType.CAPSTYPE_POINTER: 591 | self.capability = pointerCapability(false, {readLength : new type.CallableValue(function() { 592 | return self.lengthCapability.value - 4; 593 | })}).read(s); 594 | break; 595 | case CapsType.CAPSTYPE_INPUT: 596 | self.capability = inputCapability({readLength : new type.CallableValue(function() { 597 | return self.lengthCapability.value - 4; 598 | })}).read(s); 599 | break; 600 | case CapsType.CAPSTYPE_BRUSH: 601 | self.capability = brushCapability({readLength : new type.CallableValue(function() { 602 | return self.lengthCapability.value - 4; 603 | })}).read(s); 604 | break; 605 | case CapsType.CAPSTYPE_GLYPHCACHE: 606 | self.capability = glyphCapability(null, {readLength : new type.CallableValue(function() { 607 | return self.lengthCapability.value - 4; 608 | })}).read(s); 609 | break; 610 | case CapsType.CAPSTYPE_OFFSCREENCACHE: 611 | self.capability = offscreenBitmapCacheCapability({readLength : new type.CallableValue(function() { 612 | return self.lengthCapability.value - 4; 613 | })}).read(s); 614 | break; 615 | case CapsType.CAPSTYPE_VIRTUALCHANNEL: 616 | self.capability = virtualChannelCapability({readLength : new type.CallableValue(function() { 617 | return self.lengthCapability.value - 4; 618 | })}).read(s); 619 | break; 620 | case CapsType.CAPSTYPE_SOUND: 621 | self.capability = soundCapability({readLength : new type.CallableValue(function() { 622 | return self.lengthCapability.value - 4; 623 | })}).read(s); 624 | break; 625 | case CapsType.CAPSTYPE_CONTROL: 626 | self.capability = controlCapability({readLength : new type.CallableValue(function() { 627 | return self.lengthCapability.value - 4; 628 | })}).read(s); 629 | break; 630 | case CapsType.CAPSTYPE_ACTIVATION: 631 | self.capability = windowActivationCapability({readLength : new type.CallableValue(function() { 632 | return self.lengthCapability.value - 4; 633 | })}).read(s); 634 | break; 635 | case CapsType.CAPSTYPE_FONT: 636 | self.capability = fontCapability({readLength : new type.CallableValue(function() { 637 | return self.lengthCapability.value - 4; 638 | })}).read(s); 639 | break; 640 | case CapsType.CAPSTYPE_COLORCACHE: 641 | self.capability = colorCacheCapability({readLength : new type.CallableValue(function() { 642 | return self.lengthCapability.value - 4; 643 | })}).read(s); 644 | break; 645 | case CapsType.CAPSTYPE_SHARE: 646 | self.capability = shareCapability({readLength : new type.CallableValue(function() { 647 | return self.lengthCapability.value - 4; 648 | })}).read(s); 649 | break; 650 | case CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE: 651 | self.capability = multiFragmentUpdate({readLength : new type.CallableValue(function() { 652 | return self.lengthCapability.value - 4; 653 | })}).read(s); 654 | break; 655 | default: 656 | log.debug('unknown capability ' + self.capabilitySetType.value); 657 | self.capability = new type.BinaryString(null, {readLength : new type.CallableValue(function() { 658 | return self.lengthCapability.value - 4; 659 | })}).read(s); 660 | } 661 | }) 662 | }; 663 | 664 | return new type.Component(self, opt); 665 | } 666 | 667 | /** 668 | * Module exports 669 | */ 670 | module.exports = { 671 | CapsType : CapsType, 672 | MajorType : MajorType, 673 | MinorType : MinorType, 674 | GeneralExtraFlag : GeneralExtraFlag, 675 | Boolean : Boolean, 676 | OrderFlag : OrderFlag, 677 | Order : Order, 678 | OrderEx : OrderEx, 679 | InputFlags : InputFlags, 680 | BrushSupport : BrushSupport, 681 | GlyphSupport : GlyphSupport, 682 | OffscreenSupportLevel : OffscreenSupportLevel, 683 | VirtualChannelCompressionFlag : VirtualChannelCompressionFlag, 684 | SoundFlag : SoundFlag, 685 | generalCapability : generalCapability, 686 | bitmapCapability : bitmapCapability, 687 | orderCapability : orderCapability, 688 | bitmapCacheCapability : bitmapCacheCapability, 689 | pointerCapability : pointerCapability, 690 | inputCapability : inputCapability, 691 | brushCapability : brushCapability, 692 | cacheEntry : cacheEntry, 693 | glyphCapability : glyphCapability, 694 | offscreenBitmapCacheCapability : offscreenBitmapCacheCapability, 695 | virtualChannelCapability : virtualChannelCapability, 696 | soundCapability : soundCapability, 697 | controlCapability : controlCapability, 698 | windowActivationCapability : windowActivationCapability, 699 | fontCapability : fontCapability, 700 | colorCacheCapability : colorCacheCapability, 701 | shareCapability : shareCapability, 702 | multiFragmentUpdate : multiFragmentUpdate, 703 | capability : capability 704 | }; -------------------------------------------------------------------------------- /lib/protocol/pdu/global.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var events = require('events'); 22 | var caps = require('./caps'); 23 | var data = require('./data'); 24 | var type = require('../../core').type; 25 | var log = require('../../core').log; 26 | 27 | /** 28 | * Global channel for all graphic updates 29 | * capabilities exchange and input handles 30 | */ 31 | function Global(transport, fastPathTransport) { 32 | this.transport = transport; 33 | this.fastPathTransport = fastPathTransport; 34 | // must be init via connect event 35 | this.userId = 0; 36 | this.serverCapabilities = []; 37 | this.clientCapabilities = []; 38 | } 39 | 40 | //inherit from Layer 41 | inherits(Global, events.EventEmitter); 42 | 43 | /** 44 | * Send formated PDU message 45 | * @param message {type.Component} PDU message 46 | */ 47 | Global.prototype.sendPDU = function(message) { 48 | this.transport.send(data.pdu(this.userId, message)); 49 | }; 50 | 51 | /** 52 | * Send formated Data PDU message 53 | * @param message {type.Component} PDU message 54 | */ 55 | Global.prototype.sendDataPDU = function(message) { 56 | this.sendPDU(data.dataPDU(message, this.shareId)); 57 | }; 58 | 59 | /** 60 | * Client side of Global channel automata 61 | * @param transport 62 | */ 63 | function Client(transport, fastPathTransport) { 64 | Global.call(this, transport, fastPathTransport); 65 | var self = this; 66 | this.transport.once('connect', function(core, userId, channelId) { 67 | self.connect(core, userId, channelId); 68 | }).on('close', function() { 69 | self.emit('close'); 70 | }).on('error', function (err) { 71 | self.emit('error', err); 72 | }); 73 | 74 | if (this.fastPathTransport) { 75 | this.fastPathTransport.on('fastPathData', function (secFlag, s) { 76 | self.recvFastPath(secFlag, s); 77 | }); 78 | } 79 | 80 | // init client capabilities 81 | this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL] = caps.generalCapability(); 82 | this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP] = caps.bitmapCapability(); 83 | this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER] = caps.orderCapability( 84 | new type.Component([ 85 | 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), 86 | 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), 87 | 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), 88 | 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) 89 | ])); 90 | this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAPCACHE] = caps.bitmapCacheCapability(); 91 | this.clientCapabilities[caps.CapsType.CAPSTYPE_POINTER] = caps.pointerCapability(); 92 | this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT] = caps.inputCapability(); 93 | this.clientCapabilities[caps.CapsType.CAPSTYPE_BRUSH] = caps.brushCapability(); 94 | this.clientCapabilities[caps.CapsType.CAPSTYPE_GLYPHCACHE] = caps.glyphCapability( 95 | new type.Component([ 96 | caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), 97 | caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry() 98 | ])); 99 | this.clientCapabilities[caps.CapsType.CAPSTYPE_OFFSCREENCACHE] = caps.offscreenBitmapCacheCapability(); 100 | this.clientCapabilities[caps.CapsType.CAPSTYPE_VIRTUALCHANNEL] = caps.virtualChannelCapability(); 101 | this.clientCapabilities[caps.CapsType.CAPSTYPE_SOUND] = caps.soundCapability(); 102 | this.clientCapabilities[caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate(); 103 | } 104 | 105 | // inherit from Layer 106 | inherits(Client, Global); 107 | 108 | /** 109 | * connect function 110 | * @param gccCore {type.Component(clientCoreData)} 111 | */ 112 | Client.prototype.connect = function(gccCore, userId, channelId) { 113 | this.gccCore = gccCore; 114 | this.userId = userId; 115 | this.channelId = channelId; 116 | var self = this; 117 | this.transport.once('data', function(s) { 118 | self.recvDemandActivePDU(s); 119 | }); 120 | }; 121 | 122 | /** 123 | * close stack 124 | */ 125 | Client.prototype.close = function() { 126 | this.transport.close(); 127 | }; 128 | 129 | /** 130 | * Receive capabilities from server 131 | * @param s {type.Stream} 132 | */ 133 | Client.prototype.recvDemandActivePDU = function(s) { 134 | var pdu = data.pdu().read(s); 135 | if (pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DEMANDACTIVEPDU) { 136 | log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence'); 137 | 138 | // loop on state 139 | var self = this; 140 | this.transport.once('data', function(s) { 141 | self.recvDemandActivePDU(s); 142 | }); 143 | return; 144 | } 145 | 146 | // store share id 147 | this.shareId = pdu.obj.pduMessage.obj.shareId.value; 148 | 149 | // store server capabilities 150 | for(var i in pdu.obj.pduMessage.obj.capabilitySets.obj) { 151 | var cap = pdu.obj.pduMessage.obj.capabilitySets.obj[i].obj.capability; 152 | if(!cap.obj) { 153 | continue; 154 | } 155 | this.serverCapabilities[cap.obj.__TYPE__] = cap; 156 | } 157 | 158 | this.transport.enableSecureCheckSum = !!(this.serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj.extraFlags.value & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM); 159 | 160 | this.sendConfirmActivePDU(); 161 | this.sendClientFinalizeSynchronizePDU(); 162 | 163 | var self = this; 164 | this.transport.once('data', function(s) { 165 | self.recvServerSynchronizePDU(s); 166 | }); 167 | }; 168 | 169 | /** 170 | * global channel automata state 171 | * @param s {type.Stream} 172 | */ 173 | Client.prototype.recvServerSynchronizePDU = function(s) { 174 | var pdu = data.pdu().read(s); 175 | if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU 176 | || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_SYNCHRONIZE) { 177 | log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence'); 178 | // loop on state 179 | var self = this; 180 | this.transport.once('data', function(s) { 181 | self.recvServerSynchronizePDU(s); 182 | }); 183 | return; 184 | } 185 | 186 | var self = this; 187 | this.transport.once('data', function(s) { 188 | self.recvServerControlCooperatePDU(s); 189 | }); 190 | }; 191 | 192 | /** 193 | * global channel automata state 194 | * @param s {type.Stream} 195 | */ 196 | Client.prototype.recvServerControlCooperatePDU = function(s) { 197 | var pdu = data.pdu().read(s); 198 | if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU 199 | || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL 200 | || pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_COOPERATE) { 201 | log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence'); 202 | 203 | // loop on state 204 | var self = this; 205 | this.transport.once('data', function(s) { 206 | self.recvServerControlCooperatePDU(s); 207 | }); 208 | } 209 | 210 | var self = this; 211 | this.transport.once('data', function(s) { 212 | self.recvServerControlGrantedPDU(s); 213 | }); 214 | }; 215 | 216 | /** 217 | * global channel automata state 218 | * @param s {type.Stream} 219 | */ 220 | Client.prototype.recvServerControlGrantedPDU = function(s) { 221 | var pdu = data.pdu().read(s); 222 | if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU 223 | || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL 224 | || pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_GRANTED_CONTROL) { 225 | log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence'); 226 | 227 | // loop on state 228 | var self = this; 229 | this.transport.once('data', function(s) { 230 | self.recvServerControlGrantedPDU(s); 231 | }); 232 | } 233 | 234 | var self = this; 235 | this.transport.once('data', function(s) { 236 | self.recvServerFontMapPDU(s); 237 | }); 238 | }; 239 | 240 | /** 241 | * global channel automata state 242 | * @param s {type.Stream} 243 | */ 244 | Client.prototype.recvServerFontMapPDU = function(s) { 245 | var pdu = data.pdu().read(s); 246 | if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU 247 | || pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_FONTMAP) { 248 | log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence'); 249 | 250 | // loop on state 251 | var self = this; 252 | this.transport.once('data', function(s) { 253 | self.recvServerFontMapPDU(s); 254 | }); 255 | } 256 | 257 | this.emit('connect'); 258 | var self = this; 259 | this.transport.on('data', function(s) { 260 | self.recvPDU(s); 261 | }); 262 | }; 263 | 264 | /** 265 | * Main reveive fast path 266 | * @param secFlag {integer} 267 | * @param s {type.Stream} 268 | */ 269 | Client.prototype.recvFastPath = function (secFlag, s) { 270 | while (s.availableLength() > 0) { 271 | var pdu = data.fastPathUpdatePDU().read(s); 272 | switch (pdu.obj.updateHeader.value & 0xf) { 273 | case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: 274 | this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj); 275 | break; 276 | default: 277 | } 278 | } 279 | }; 280 | 281 | /** 282 | * global channel automata state 283 | * @param s {type.Stream} 284 | */ 285 | Client.prototype.recvPDU = function(s) { 286 | while (s.availableLength() > 0) { 287 | var pdu = data.pdu().read(s); 288 | switch(pdu.obj.shareControlHeader.obj.pduType.value) { 289 | case data.PDUType.PDUTYPE_DEACTIVATEALLPDU: 290 | var self = this; 291 | this.transport.removeAllListeners('data'); 292 | this.transport.once('data', function(s) { 293 | self.recvDemandActivePDU(s); 294 | }); 295 | break; 296 | case data.PDUType.PDUTYPE_DATAPDU: 297 | this.readDataPDU(pdu.obj.pduMessage) 298 | break; 299 | default: 300 | log.debug('ignore pdu type ' + pdu.obj.shareControlHeader.obj.pduType.value); 301 | } 302 | } 303 | }; 304 | 305 | /** 306 | * main receive for data PDU packet 307 | * @param dataPDU {data.dataPDU} 308 | */ 309 | Client.prototype.readDataPDU = function (dataPDU) { 310 | switch(dataPDU.obj.shareDataHeader.obj.pduType2.value) { 311 | case data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: 312 | break; 313 | case data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED: 314 | this.transport.close(); 315 | break; 316 | case data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO: 317 | this.emit('session'); 318 | break; 319 | case data.PDUType2.PDUTYPE2_UPDATE: 320 | this.readUpdateDataPDU(dataPDU.obj.pduData) 321 | break; 322 | } 323 | }; 324 | 325 | /** 326 | * Main upadate pdu receive function 327 | * @param updateDataPDU 328 | */ 329 | Client.prototype.readUpdateDataPDU = function (updateDataPDU) { 330 | switch(updateDataPDU.obj.updateType.value) { 331 | case data.UpdateType.UPDATETYPE_BITMAP: 332 | this.emit('bitmap', updateDataPDU.obj.updateData.obj.rectangles.obj) 333 | break; 334 | } 335 | }; 336 | 337 | /** 338 | * send all client capabilities 339 | */ 340 | Client.prototype.sendConfirmActivePDU = function () { 341 | var generalCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj; 342 | generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS; 343 | generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT; 344 | generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED 345 | | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR 346 | | caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM 347 | | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED; 348 | 349 | var bitmapCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].obj; 350 | bitmapCapability.preferredBitsPerPixel.value = this.gccCore.highColorDepth.value; 351 | bitmapCapability.desktopWidth.value = this.gccCore.desktopWidth.value; 352 | bitmapCapability.desktopHeight.value = this.gccCore.desktopHeight.value; 353 | 354 | var orderCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].obj; 355 | orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT; 356 | 357 | var inputCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].obj; 358 | inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE; 359 | inputCapability.keyboardLayout = this.gccCore.kbdLayout; 360 | inputCapability.keyboardType = this.gccCore.keyboardType; 361 | inputCapability.keyboardSubType = this.gccCore.keyboardSubType; 362 | inputCapability.keyboardrFunctionKey = this.gccCore.keyboardFnKeys; 363 | inputCapability.imeFileName = this.gccCore.imeFileName; 364 | 365 | var capabilities = new type.Component([]); 366 | for(var i in this.clientCapabilities) { 367 | capabilities.obj.push(caps.capability(this.clientCapabilities[i])); 368 | } 369 | 370 | var confirmActivePDU = data.confirmActivePDU(capabilities, this.shareId); 371 | 372 | this.sendPDU(confirmActivePDU); 373 | }; 374 | 375 | /** 376 | * send synchronize PDU 377 | */ 378 | Client.prototype.sendClientFinalizeSynchronizePDU = function() { 379 | this.sendDataPDU(data.synchronizeDataPDU(this.channelId)); 380 | this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_COOPERATE)); 381 | this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL)); 382 | this.sendDataPDU(data.fontListDataPDU()); 383 | }; 384 | 385 | /** 386 | * Send input event as slow path input 387 | * @param inputEvents {array} 388 | */ 389 | Client.prototype.sendInputEvents = function (inputEvents) { 390 | var pdu = data.clientInputEventPDU(new type.Component(inputEvents.map(function (e) { 391 | return data.slowPathInputEvent(e); 392 | }))); 393 | 394 | this.sendDataPDU(pdu); 395 | }; 396 | 397 | /** 398 | * Module exports 399 | */ 400 | module.exports = { 401 | Client : Client 402 | }; -------------------------------------------------------------------------------- /lib/protocol/pdu/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var lic = require('./lic'); 21 | var sec = require('./sec'); 22 | var global = require('./global'); 23 | var data = require('./data'); 24 | 25 | module.exports = { 26 | lic : lic, 27 | sec : sec, 28 | global : global, 29 | data : data 30 | }; 31 | -------------------------------------------------------------------------------- /lib/protocol/pdu/lic.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var type = require('../../core').type; 21 | 22 | var MessageType = { 23 | LICENSE_REQUEST : 0x01, 24 | PLATFORM_CHALLENGE : 0x02, 25 | NEW_LICENSE : 0x03, 26 | UPGRADE_LICENSE : 0x04, 27 | LICENSE_INFO : 0x12, 28 | NEW_LICENSE_REQUEST : 0x13, 29 | PLATFORM_CHALLENGE_RESPONSE : 0x15, 30 | ERROR_ALERT : 0xFF 31 | }; 32 | 33 | /** 34 | * @see http://msdn.microsoft.com/en-us/library/cc240482.aspx 35 | */ 36 | var ErrorCode = { 37 | ERR_INVALID_SERVER_CERTIFICATE : 0x00000001, 38 | ERR_NO_LICENSE : 0x00000002, 39 | ERR_INVALID_SCOPE : 0x00000004, 40 | ERR_NO_LICENSE_SERVER : 0x00000006, 41 | STATUS_VALID_CLIENT : 0x00000007, 42 | ERR_INVALID_CLIENT : 0x00000008, 43 | ERR_INVALID_PRODUCTID : 0x0000000B, 44 | ERR_INVALID_MESSAGE_LEN : 0x0000000C, 45 | ERR_INVALID_MAC : 0x00000003 46 | }; 47 | 48 | /** 49 | * @see http://msdn.microsoft.com/en-us/library/cc240482.aspx 50 | */ 51 | var StateTransition = { 52 | ST_TOTAL_ABORT : 0x00000001, 53 | ST_NO_TRANSITION : 0x00000002, 54 | ST_RESET_PHASE_TO_START : 0x00000003, 55 | ST_RESEND_LAST_MESSAGE : 0x00000004 56 | }; 57 | 58 | /** 59 | * @see http://msdn.microsoft.com/en-us/library/cc240481.aspx 60 | */ 61 | var BinaryBlobType = { 62 | BB_ANY_BLOB : 0x0000, 63 | BB_DATA_BLOB : 0x0001, 64 | BB_RANDOM_BLOB : 0x0002, 65 | BB_CERTIFICATE_BLOB : 0x0003, 66 | BB_ERROR_BLOB : 0x0004, 67 | BB_ENCRYPTED_DATA_BLOB : 0x0009, 68 | BB_KEY_EXCHG_ALG_BLOB : 0x000D, 69 | BB_SCOPE_BLOB : 0x000E, 70 | BB_CLIENT_USER_NAME_BLOB : 0x000F, 71 | BB_CLIENT_MACHINE_NAME_BLOB : 0x0010 72 | }; 73 | 74 | var Preambule = { 75 | PREAMBLE_VERSION_2_0 : 0x2, 76 | PREAMBLE_VERSION_3_0 : 0x3, 77 | EXTENDED_ERROR_MSG_SUPPORTED : 0x80 78 | }; 79 | 80 | /** 81 | * Binary blob to emcompass license information 82 | * @see http://msdn.microsoft.com/en-us/library/cc240481.aspx 83 | * @param blobType {BinaryBlobType.*} 84 | * @returns {type.Component} 85 | */ 86 | function licenseBinaryBlob(blobType) { 87 | blobType = blobType || BinaryBlobType.BB_ANY_BLOB; 88 | var self = { 89 | wBlobType : new type.UInt16Le(blobType, { constant : (blobType === BinaryBlobType.BB_ANY_BLOB)?false:true }), 90 | wBlobLen : new type.UInt16Le(function() { 91 | return self.blobData.size(); 92 | }), 93 | blobData : new type.BinaryString(null, { readLength : new type.CallableValue(function() { 94 | return self.wBlobLen.value; 95 | })}) 96 | }; 97 | 98 | return new type.Component(self); 99 | } 100 | 101 | /** 102 | * Error message in license PDU automata 103 | * @see http://msdn.microsoft.com/en-us/library/cc240482.aspx 104 | * @param opt {object} type options 105 | * @returns {type.Component} 106 | */ 107 | function licensingErrorMessage(opt) { 108 | var self = { 109 | __TYPE__ : MessageType.ERROR_ALERT, 110 | dwErrorCode : new type.UInt32Le(), 111 | dwStateTransition : new type.UInt32Le(), 112 | blob : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB) 113 | }; 114 | 115 | return new type.Component(self, opt); 116 | } 117 | 118 | /** 119 | * License product informations 120 | * @see http://msdn.microsoft.com/en-us/library/cc241915.aspx 121 | * @returns {type.Component} 122 | */ 123 | function productInformation() { 124 | var self = { 125 | dwVersion : new type.UInt32Le(), 126 | cbCompanyName : new type.UInt32Le(function() { 127 | return self.pbCompanyName.size(); 128 | }), 129 | // may contain "Microsoft Corporation" from server microsoft 130 | pbCompanyName : new type.BinaryString(new Buffer('Microsoft Corporation', 'ucs2'), { readLength : new type.CallableValue(function() { 131 | return self.cbCompanyName.value; 132 | })}), 133 | cbProductId : new type.UInt32Le(function() { 134 | return self.pbProductId.size(); 135 | }), 136 | // may contain "A02" from microsoft license server 137 | pbProductId : new type.BinaryString(new Buffer('A02', 'ucs2'), { readLength : new type.CallableValue(function() { 138 | return self.cbProductId.value; 139 | })}) 140 | }; 141 | 142 | return new type.Component(self); 143 | } 144 | 145 | /** 146 | * Use in license negotiation 147 | * @see http://msdn.microsoft.com/en-us/library/cc241917.aspx 148 | * @returns {type.Component} 149 | */ 150 | function scope() { 151 | var self = { 152 | scope : licenseBinaryBlob(BinaryBlobType.BB_SCOPE_BLOB) 153 | }; 154 | 155 | return new type.Component(self); 156 | } 157 | 158 | /** 159 | * @see http://msdn.microsoft.com/en-us/library/cc241916.aspx 160 | * @returns {type.Component} 161 | */ 162 | function scopeList() { 163 | var self = { 164 | scopeCount : new type.UInt32Le(function() { 165 | return self.scopeArray.length; 166 | }), 167 | scopeArray : new type.Factory(function(s) { 168 | self.scopeArray = new type.Component([]); 169 | for(var i = 0; i < self.scopeCount.value; i++) { 170 | self.scopeArray.obj.push(scope().read(s)); 171 | } 172 | }) 173 | }; 174 | 175 | return new type.Component(self); 176 | } 177 | 178 | /** 179 | * @see http://msdn.microsoft.com/en-us/library/cc241914.aspx 180 | * @param opt {object} type options 181 | * @returns {type.Component} 182 | */ 183 | function serverLicenseRequest(opt) { 184 | var self = { 185 | __TYPE__ : MessageType.LICENSE_REQUEST, 186 | serverRandom : new type.BinaryString(new Buffer(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) } ), 187 | productInfo : productInformation(), 188 | keyExchangeList : licenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB), 189 | serverCertificate : licenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB), 190 | scopeList : scopeList() 191 | }; 192 | 193 | return new type.Component(self, opt); 194 | } 195 | 196 | /** 197 | * @see http://msdn.microsoft.com/en-us/library/cc241918.aspx 198 | * @param opt {object} type options 199 | * @returns {type.Component} 200 | */ 201 | function clientNewLicenseRequest(opt) { 202 | var self = { 203 | __TYPE__ : MessageType.NEW_LICENSE_REQUEST, 204 | preferredKeyExchangeAlg : new type.UInt32Le(0x00000001, { constant : true }), 205 | // pure microsoft client ;-) 206 | // http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10 207 | platformId : new type.UInt32Le(0x04000000 | 0x00010000), 208 | clientRandom : new type.BinaryString(new Buffer(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) }), 209 | encryptedPreMasterSecret : licenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB), 210 | ClientUserName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB), 211 | ClientMachineName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB) 212 | }; 213 | 214 | return new type.Component(self, opt); 215 | } 216 | 217 | /** 218 | * @see http://msdn.microsoft.com/en-us/library/cc241921.aspx 219 | * @param opt {object} type options 220 | * @returns {type.Component} 221 | */ 222 | function serverPlatformChallenge(opt) { 223 | var self = { 224 | __TYPE__ : MessageType.PLATFORM_CHALLENGE, 225 | connectFlags : new type.UInt32Le(), 226 | encryptedPlatformChallenge : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB), 227 | MACData : new type.BinaryString(new Buffer(Array(16 + 1).join('\x00')), { readLength : new type.CallableValue(16) }) 228 | }; 229 | 230 | return new type.Component(self, opt); 231 | } 232 | 233 | /** 234 | * @see http://msdn.microsoft.com/en-us/library/cc241922.aspx 235 | * @param opt {object} type options 236 | * @returns {type.Component} 237 | */ 238 | function clientPLatformChallengeResponse(opt) { 239 | var self = { 240 | __TYPE__ : MessageType.PLATFORM_CHALLENGE_RESPONSE, 241 | encryptedPlatformChallengeResponse : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB), 242 | encryptedHWID : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB), 243 | MACData : new type.BinaryString(new Buffer(Array(16 + 1).join('\x00'), 'binary'), { readLength : new type.CallableValue(16) }) 244 | }; 245 | 246 | return new type.Component(self, opt); 247 | }; 248 | 249 | /** 250 | * Global license packet 251 | * @param packet {type.* | null} send packet 252 | * @returns {type.Component} 253 | */ 254 | function licensePacket(message) { 255 | var self = { 256 | bMsgtype : new type.UInt8(function() { 257 | return self.licensingMessage.obj.__TYPE__; 258 | }), 259 | flag : new type.UInt8(Preambule.PREAMBLE_VERSION_3_0), 260 | wMsgSize : new type.UInt16Le(function() { 261 | return new type.Component(self).size(); 262 | }), 263 | licensingMessage : message || new type.Factory(function(s) { 264 | switch(self.bMsgtype.value) { 265 | case MessageType.ERROR_ALERT: 266 | self.licensingMessage = licensingErrorMessage({ readLength : new type.CallableValue(function() { 267 | return self.wMsgSize.value - 4; 268 | })}).read(s); 269 | break; 270 | case MessageType.LICENSE_REQUEST: 271 | self.licensingMessage = serverLicenseRequest({ readLength : new type.CallableValue(function() { 272 | return self.wMsgSize.value - 4; 273 | })}).read(s); 274 | break; 275 | case MessageType.NEW_LICENSE_REQUEST: 276 | self.licensingMessage = clientNewLicenseRequest({ readLength : new type.CallableValue(function() { 277 | return self.wMsgSize.value - 4; 278 | })}).read(s); 279 | break; 280 | case MessageType.PLATFORM_CHALLENGE: 281 | self.licensingMessage = serverPlatformChallenge({ readLength : new type.CallableValue(function() { 282 | return self.wMsgSize.value - 4; 283 | })}).read(s); 284 | break; 285 | case MessageType.PLATFORM_CHALLENGE_RESPONSE: 286 | self.licensingMessage = clientPLatformChallengeResponse({ readLength : new type.CallableValue(function() { 287 | return self.wMsgSize.value - 4; 288 | })}).read(s); 289 | break; 290 | default: 291 | log.error('unknown license message type ' + self.bMsgtype.value); 292 | } 293 | }) 294 | }; 295 | 296 | return new type.Component(self); 297 | } 298 | 299 | /** 300 | * Module exports 301 | */ 302 | module.exports = { 303 | MessageType : MessageType, 304 | ErrorCode : ErrorCode, 305 | StateTransition : StateTransition, 306 | licensePacket : licensePacket, 307 | clientNewLicenseRequest : clientNewLicenseRequest, 308 | clientPLatformChallengeResponse : clientPLatformChallengeResponse 309 | }; -------------------------------------------------------------------------------- /lib/protocol/pdu/sec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var crypto = require('crypto'); 22 | var events = require('events'); 23 | var type = require('../../core').type; 24 | var error = require('../../core').error; 25 | var log = require('../../core').log; 26 | var gcc = require('../t125/gcc'); 27 | var lic = require('./lic'); 28 | var cert = require('../cert'); 29 | var rsa = require('../../security').rsa; 30 | 31 | /** 32 | * @see http://msdn.microsoft.com/en-us/library/cc240579.aspx 33 | */ 34 | var SecurityFlag = { 35 | SEC_EXCHANGE_PKT : 0x0001, 36 | SEC_TRANSPORT_REQ : 0x0002, 37 | RDP_SEC_TRANSPORT_RSP : 0x0004, 38 | SEC_ENCRYPT : 0x0008, 39 | SEC_RESET_SEQNO : 0x0010, 40 | SEC_IGNORE_SEQNO : 0x0020, 41 | SEC_INFO_PKT : 0x0040, 42 | SEC_LICENSE_PKT : 0x0080, 43 | SEC_LICENSE_ENCRYPT_CS : 0x0200, 44 | SEC_LICENSE_ENCRYPT_SC : 0x0200, 45 | SEC_REDIRECTION_PKT : 0x0400, 46 | SEC_SECURE_CHECKSUM : 0x0800, 47 | SEC_AUTODETECT_REQ : 0x1000, 48 | SEC_AUTODETECT_RSP : 0x2000, 49 | SEC_HEARTBEAT : 0x4000, 50 | SEC_FLAGSHI_VALID : 0x8000 51 | }; 52 | 53 | /** 54 | * @see https://msdn.microsoft.com/en-us/library/cc240475.aspx 55 | */ 56 | var InfoFlag = { 57 | INFO_MOUSE : 0x00000001, 58 | INFO_DISABLECTRLALTDEL : 0x00000002, 59 | INFO_AUTOLOGON : 0x00000008, 60 | INFO_UNICODE : 0x00000010, 61 | INFO_MAXIMIZESHELL : 0x00000020, 62 | INFO_LOGONNOTIFY : 0x00000040, 63 | INFO_COMPRESSION : 0x00000080, 64 | INFO_ENABLEWINDOWSKEY : 0x00000100, 65 | INFO_REMOTECONSOLEAUDIO : 0x00002000, 66 | INFO_FORCE_ENCRYPTED_CS_PDU : 0x00004000, 67 | INFO_RAIL : 0x00008000, 68 | INFO_LOGONERRORS : 0x00010000, 69 | INFO_MOUSE_HAS_WHEEL : 0x00020000, 70 | INFO_PASSWORD_IS_SC_PIN : 0x00040000, 71 | INFO_NOAUDIOPLAYBACK : 0x00080000, 72 | INFO_USING_SAVED_CREDS : 0x00100000, 73 | INFO_AUDIOCAPTURE : 0x00200000, 74 | INFO_VIDEO_DISABLE : 0x00400000, 75 | INFO_CompressionTypeMask : 0x00001E00 76 | }; 77 | 78 | /** 79 | * @see https://msdn.microsoft.com/en-us/library/cc240476.aspx 80 | */ 81 | var AfInet = { 82 | AfInet : 0x00002, 83 | AF_INET6 : 0x0017 84 | }; 85 | 86 | /** 87 | * @see https://msdn.microsoft.com/en-us/library/cc240476.aspx 88 | */ 89 | var PerfFlag = { 90 | PERF_DISABLE_WALLPAPER : 0x00000001, 91 | PERF_DISABLE_FULLWINDOWDRAG : 0x00000002, 92 | PERF_DISABLE_MENUANIMATIONS : 0x00000004, 93 | PERF_DISABLE_THEMING : 0x00000008, 94 | PERF_DISABLE_CURSOR_SHADOW : 0x00000020, 95 | PERF_DISABLE_CURSORSETTINGS : 0x00000040, 96 | PERF_ENABLE_FONT_SMOOTHING : 0x00000080, 97 | PERF_ENABLE_DESKTOP_COMPOSITION : 0x00000100 98 | }; 99 | 100 | /** 101 | * @see http://msdn.microsoft.com/en-us/library/cc241992.aspx 102 | * @param input {Buffer} Binary data 103 | * @param salt {Buffer} salt for context call 104 | * @param salt1 {Buffer} another salt (ex : client random) 105 | * @param salt2 {Buffer} another salt (ex : server random) 106 | * @return {Buffer} 107 | */ 108 | function saltedHash(input, salt, salt1, salt2) { 109 | var sha1Digest = crypto.createHash('sha1'); 110 | sha1Digest.update(input); 111 | sha1Digest.update(salt.slice(0, 48)); 112 | sha1Digest.update(salt1); 113 | sha1Digest.update(salt2); 114 | 115 | var sha1Sig = sha1Digest.digest(); 116 | 117 | var md5Digest = crypto.createHash('md5'); 118 | md5Digest.update(salt.slice(0, 48)); 119 | md5Digest.update(sha1Sig); 120 | return md5Digest.digest(); 121 | } 122 | 123 | /** 124 | * @param key {Buffer} secret 125 | * @param random1 {Buffer} client random 126 | * @param random2 {Buffer} server random 127 | * @returns {Buffer} 128 | */ 129 | function finalHash (key, random1, random2) { 130 | var md5Digest = crypto.createHash('md5'); 131 | md5Digest.update(key); 132 | md5Digest.update(random1); 133 | md5Digest.update(random2); 134 | return md5Digest.digest(); 135 | } 136 | 137 | /** 138 | * @see http://msdn.microsoft.com/en-us/library/cc241992.aspx 139 | * @param secret {Buffer} secret 140 | * @param random1 {Buffer} client random 141 | * @param random2 {Buffer} server random 142 | * @returns {Buffer} 143 | */ 144 | function masterSecret (secret, random1, random2) { 145 | var sh1 = saltedHash(new Buffer('A'), secret, random1, random2); 146 | var sh2 = saltedHash(new Buffer('BB'), secret, random1, random2); 147 | var sh3 = saltedHash(new Buffer('CCC'), secret, random1, random2); 148 | 149 | var ms = new Buffer(sh1.length + sh2.length + sh3.length); 150 | sh1.copy(ms); 151 | sh2.copy(ms, sh1.length); 152 | sh3.copy(ms, sh1.length + sh2.length); 153 | return ms; 154 | } 155 | 156 | /** 157 | * @see http://msdn.microsoft.com/en-us/library/cc241995.aspx 158 | * @param macSaltKey {Buffer} key 159 | * @param data {Buffer} data 160 | * @returns {Buffer} 161 | */ 162 | function macData(macSaltKey, data) { 163 | var salt1 = new Buffer(40); 164 | salt1.fill(0x36); 165 | 166 | var salt2 = new Buffer(48); 167 | salt2.fill(0x5c); 168 | 169 | var dataLength = new type.UInt32Le(data.length).toStream().buffer; 170 | 171 | var sha1 = crypto.createHash('sha1'); 172 | sha1.update(macSaltKey); 173 | sha1.update(salt1); 174 | sha1.update(dataLength); 175 | sha1.update(data); 176 | var sha1Digest = sha1.digest(); 177 | 178 | var md5 = crypto.createHash('md5'); 179 | md5.update(macSaltKey); 180 | md5.update(salt2); 181 | md5.update(sha1Digest); 182 | 183 | return md5.digest(); 184 | } 185 | 186 | /** 187 | * RDP client informations 188 | * @param extendedInfoConditional {boolean} true if RDP5+ 189 | * @returns {type.Component} 190 | */ 191 | function rdpInfos(extendedInfoConditional) { 192 | var self = { 193 | codePage : new type.UInt32Le(), 194 | flag : new type.UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY), 195 | cbDomain : new type.UInt16Le(function() { 196 | return self.domain.size() - 2; 197 | }), 198 | cbUserName : new type.UInt16Le(function() { 199 | return self.userName.size() - 2; 200 | }), 201 | cbPassword : new type.UInt16Le(function() { 202 | return self.password.size() - 2; 203 | }), 204 | cbAlternateShell : new type.UInt16Le(function() { 205 | return self.alternateShell.size() - 2; 206 | }), 207 | cbWorkingDir : new type.UInt16Le(function() { 208 | return self.workingDir.size() - 2; 209 | }), 210 | domain : new type.BinaryString(new Buffer('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() { 211 | return self.cbDomain.value + 2; 212 | })}), 213 | userName : new type.BinaryString(new Buffer('\x00', 'ucs2'), { readLength : new type.CallableValue(function() { 214 | return self.cbUserName.value + 2; 215 | })}), 216 | password : new type.BinaryString(new Buffer('\x00', 'ucs2'), { readLength : new type.CallableValue(function () { 217 | return self.cbPassword.value + 2; 218 | })}), 219 | alternateShell : new type.BinaryString(new Buffer('\x00', 'ucs2'), { readLength : new type.CallableValue(function() { 220 | return self.cbAlternateShell.value + 2; 221 | })}), 222 | workingDir : new type.BinaryString(new Buffer('\x00', 'ucs2'), { readLength : new type.CallableValue(function() { 223 | return self.cbWorkingDir.value + 2; 224 | })}), 225 | extendedInfo : rdpExtendedInfos({ conditional : extendedInfoConditional }) 226 | }; 227 | 228 | return new type.Component(self); 229 | } 230 | 231 | /** 232 | * RDP client extended informations present in RDP5+ 233 | * @param opt 234 | * @returns {type.Component} 235 | */ 236 | function rdpExtendedInfos(opt) { 237 | var self = { 238 | clientAddressFamily : new type.UInt16Le(AfInet.AfInet), 239 | cbClientAddress : new type.UInt16Le(function() { 240 | return self.clientAddress.size(); 241 | }), 242 | clientAddress : new type.BinaryString(new Buffer('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() { 243 | return self.cbClientAddress; 244 | }) }), 245 | cbClientDir : new type.UInt16Le(function() { 246 | return self.clientDir.size(); 247 | }), 248 | clientDir : new type.BinaryString(new Buffer('\x00', 'ucs2'), { readLength : new type.CallableValue(function() { 249 | return self.cbClientDir; 250 | }) }), 251 | clientTimeZone : new type.BinaryString(new Buffer(Array(172 + 1).join("\x00"))), 252 | clientSessionId : new type.UInt32Le(), 253 | performanceFlags : new type.UInt32Le() 254 | }; 255 | return new type.Component(self, opt); 256 | } 257 | 258 | /** 259 | * Header of security header 260 | * @returns {type.Component} 261 | */ 262 | function securityHeader() { 263 | var self = { 264 | securityFlag : new type.UInt16Le(), 265 | securityFlagHi : new type.UInt16Le() 266 | }; 267 | 268 | return new type.Component(self); 269 | } 270 | 271 | /** 272 | * Security layer 273 | * @param transport {events.EventEmitter} 274 | */ 275 | function Sec(transport, fastPathTransport) { 276 | this.transport = transport; 277 | this.fastPathTransport = fastPathTransport; 278 | // init at connect event from transport layer 279 | this.gccClient = null; 280 | this.gccServer = null; 281 | var self = this; 282 | this.infos = rdpInfos(function() { 283 | return self.gccClient.core.rdpVersion.value === gcc.VERSION.RDP_VERSION_5_PLUS; 284 | }); 285 | this.machineName = ''; 286 | 287 | 288 | // basic encryption 289 | this.enableEncryption = false; 290 | 291 | if (this.fastPathTransport) { 292 | this.fastPathTransport.on('fastPathData', function (secFlag, s) { 293 | self.recvFastPath(secFlag, s); 294 | }); 295 | } 296 | }; 297 | 298 | //inherit from Layer 299 | inherits(Sec, events.EventEmitter); 300 | 301 | /** 302 | * Send message with security header 303 | * @param flag {integer} security flag 304 | * @param data {type.*} message 305 | */ 306 | Sec.prototype.sendFlagged = function(flag, data) { 307 | this.transport.send('global', new type.Component([ 308 | new type.UInt16Le(flag), 309 | new type.UInt16Le(), 310 | data 311 | ])); 312 | }; 313 | 314 | /** 315 | * Main send function 316 | * @param message {type.*} message to send 317 | */ 318 | Sec.prototype.send = function(message) { 319 | if (this.enableEncryption) { 320 | throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED'); 321 | } 322 | this.transport.send('global', message); 323 | }; 324 | 325 | /** 326 | * Main receive function 327 | * @param s {type.Stream} 328 | */ 329 | Sec.prototype.recv = function(s) { 330 | if (this.enableEncryption) { 331 | throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED'); 332 | } 333 | // not support yet basic RDP security layer 334 | this.emit('data', s); 335 | }; 336 | 337 | /** 338 | * Receive fast path data 339 | * @param secFlag {integer} security flag 340 | * @param s {type.Stream} 341 | */ 342 | Sec.prototype.recvFastPath = function (secFlag, s) { 343 | // transparent because basic RDP security layer not implemented 344 | this.emit('fastPathData', secFlag, s); 345 | }; 346 | 347 | /** 348 | * Client security layer 349 | * @param transport {events.EventEmitter} 350 | */ 351 | function Client(transport, fastPathTransport) { 352 | Sec.call(this, transport, fastPathTransport); 353 | // for basic RDP layer (in futur) 354 | this.enableSecureCheckSum = false; 355 | var self = this; 356 | this.transport.on('connect', function(gccClient, gccServer, userId, channels) { 357 | self.connect(gccClient, gccServer, userId, channels); 358 | }).on('close', function() { 359 | self.emit('close'); 360 | }).on('error', function (err) { 361 | self.emit('error', err); 362 | }); 363 | }; 364 | 365 | //inherit from Layer 366 | inherits(Client, Sec); 367 | 368 | /** 369 | * Connect event 370 | */ 371 | Client.prototype.connect = function(gccClient, gccServer, userId, channels) { 372 | //init gcc information 373 | this.gccClient = gccClient; 374 | this.gccServer = gccServer; 375 | this.userId = userId; 376 | this.channelId = channels.find(function(e) { 377 | if(e.name === 'global') return true; 378 | }).id; 379 | this.sendInfoPkt(); 380 | }; 381 | 382 | /** 383 | * close stack 384 | */ 385 | Client.prototype.close = function() { 386 | this.transport.close(); 387 | }; 388 | 389 | /** 390 | * Send main information packet 391 | * VIP (very important packet) because contain credentials 392 | */ 393 | Client.prototype.sendInfoPkt = function() { 394 | this.sendFlagged(SecurityFlag.SEC_INFO_PKT, this.infos); 395 | var self = this; 396 | this.transport.once('global', function(s) { 397 | self.recvLicense(s); 398 | }); 399 | }; 400 | 401 | function reverse(buffer) { 402 | var result = new Buffer(buffer.length); 403 | for(var i = 0; i < buffer.length; i++) { 404 | result.writeUInt8(buffer.readUInt8(buffer.length - 1 - i), i); 405 | } 406 | return result; 407 | } 408 | 409 | /** 410 | * Send a valid license request 411 | * @param licenseRequest {object(lic.serverLicenseRequest)} license requets infos 412 | */ 413 | Client.prototype.sendClientNewLicenseRequest = function(licenseRequest) { 414 | log.debug('new license request'); 415 | var serverRandom = licenseRequest.serverRandom.value; 416 | 417 | // read server certificate 418 | var s = new type.Stream(licenseRequest.serverCertificate.obj.blobData.value); 419 | var certificate = cert.certificate().read(s).obj; 420 | var publicKey = certificate.certData.obj.getPublicKey(); 421 | 422 | var clientRandom = crypto.randomBytes(32); 423 | var preMasterSecret = crypto.randomBytes(48); 424 | var mSecret = masterSecret(preMasterSecret, clientRandom, serverRandom); 425 | var sessionKeyBlob = masterSecret(mSecret, serverRandom, clientRandom); 426 | 427 | this.licenseMacSalt = sessionKeyBlob.slice(0, 16) 428 | this.licenseKey = finalHash(sessionKeyBlob.slice(16, 32), clientRandom, serverRandom); 429 | 430 | var request = lic.clientNewLicenseRequest(); 431 | request.obj.clientRandom.value = clientRandom; 432 | 433 | var preMasterSecretEncrypted = reverse(rsa.encrypt(reverse(preMasterSecret), publicKey)); 434 | var preMasterSecretEncryptedPadded = new Buffer(preMasterSecretEncrypted.length + 8); 435 | preMasterSecretEncryptedPadded.fill(0); 436 | preMasterSecretEncrypted.copy(preMasterSecretEncryptedPadded); 437 | request.obj.encryptedPreMasterSecret.obj.blobData.value = preMasterSecretEncryptedPadded; 438 | 439 | request.obj.ClientMachineName.obj.blobData.value = this.infos.obj.userName.value; 440 | request.obj.ClientUserName.obj.blobData.value = new Buffer(this.machineName + '\x00'); 441 | 442 | this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(request)); 443 | }; 444 | 445 | /** 446 | * Send a valid license request 447 | * @param platformChallenge {object(lic.serverPlatformChallenge)} platform challenge 448 | */ 449 | Client.prototype.sendClientChallengeResponse = function(platformChallenge) { 450 | log.debug('challenge license'); 451 | var serverEncryptedChallenge = platformChallenge.encryptedPlatformChallenge.obj.blobData.value; 452 | var serverChallenge = crypto.createDecipheriv('rc4', this.licenseKey, '').update(serverEncryptedChallenge); 453 | if (serverChallenge.toString('ucs2') !== 'TEST\x00') { 454 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_INVALID_LICENSE_CHALLENGE'); 455 | } 456 | 457 | var hwid = new type.Component([new type.UInt32Le(2), new type.BinaryString(crypto.randomBytes(16))]).toStream().buffer; 458 | 459 | var response = lic.clientPLatformChallengeResponse(); 460 | response.obj.encryptedPlatformChallengeResponse.obj.blobData.value = serverEncryptedChallenge; 461 | response.obj.encryptedHWID.obj.blobData.value = crypto.createCipheriv('rc4', this.licenseKey, '').update(hwid); 462 | 463 | var sig = new Buffer(serverChallenge.length + hwid.length); 464 | serverChallenge.copy(sig); 465 | hwid.copy(sig, serverChallenge.length); 466 | response.obj.MACData.value = macData(this.licenseMacSalt, sig); 467 | 468 | this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(response)); 469 | }; 470 | 471 | /** 472 | * Receive license informations 473 | * @param s {type.Stream} 474 | */ 475 | Sec.prototype.recvLicense = function(s) { 476 | var header = securityHeader().read(s).obj; 477 | if (!(header.securityFlag.value & SecurityFlag.SEC_LICENSE_PKT)) { 478 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER'); 479 | } 480 | 481 | var message = lic.licensePacket().read(s).obj; 482 | // i'm accepted 483 | if (message.bMsgtype.value === lic.MessageType.NEW_LICENSE || 484 | (message.bMsgtype.value === lic.MessageType.ERROR_ALERT 485 | && message.licensingMessage.obj.dwErrorCode.value === lic.ErrorCode.STATUS_VALID_CLIENT 486 | && message.licensingMessage.obj.dwStateTransition.value === lic.StateTransition.ST_NO_TRANSITION)) { 487 | this.emit('connect', this.gccClient.core, this.userId, this.channelId); 488 | var self = this; 489 | this.transport.on('global', function(s) { 490 | self.recv(s); 491 | }); 492 | return; 493 | } 494 | 495 | // server ask license request 496 | if (message.bMsgtype.value === lic.MessageType.LICENSE_REQUEST) { 497 | this.sendClientNewLicenseRequest(message.licensingMessage.obj); 498 | } 499 | 500 | // server send challenge 501 | if (message.bMsgtype.value === lic.MessageType.PLATFORM_CHALLENGE) { 502 | this.sendClientChallengeResponse(message.licensingMessage.obj); 503 | } 504 | 505 | var self = this; 506 | this.emit('connect', this.gccClient.core); 507 | this.transport.once('global', function (s) { 508 | self.recvLicense(s); 509 | }); 510 | }; 511 | 512 | /** 513 | * Module exports 514 | */ 515 | module.exports = { 516 | PerfFlag : PerfFlag, 517 | InfoFlag : InfoFlag, 518 | Client : Client 519 | }; -------------------------------------------------------------------------------- /lib/protocol/rdp.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var net = require('net'); 21 | var inherits = require('util').inherits; 22 | var events = require('events'); 23 | var layer = require('../core').layer; 24 | var error = require('../core').error; 25 | var rle = require('../core').rle; 26 | var log = require('../core').log; 27 | var TPKT = require('./tpkt'); 28 | var x224 = require('./x224'); 29 | var t125 = require('./t125'); 30 | var pdu = require('./pdu'); 31 | 32 | /** 33 | * decompress bitmap from RLE algorithm 34 | * @param bitmap {object} bitmap object of bitmap event of node-rdpjs 35 | */ 36 | function decompress (bitmap) { 37 | var fName = null; 38 | switch (bitmap.bitsPerPixel.value) { 39 | case 15: 40 | fName = 'bitmap_decompress_15'; 41 | break; 42 | case 16: 43 | fName = 'bitmap_decompress_16'; 44 | break; 45 | case 24: 46 | fName = 'bitmap_decompress_24'; 47 | break; 48 | case 32: 49 | fName = 'bitmap_decompress_32'; 50 | break; 51 | default: 52 | throw 'invalid bitmap data format'; 53 | } 54 | 55 | var input = new Uint8Array(bitmap.bitmapDataStream.value); 56 | var inputPtr = rle._malloc(input.length); 57 | var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length); 58 | inputHeap.set(input); 59 | 60 | var ouputSize = bitmap.width.value * bitmap.height.value * 4; 61 | var outputPtr = rle._malloc(ouputSize); 62 | 63 | var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize); 64 | 65 | var res = rle.ccall(fName, 66 | 'number', 67 | ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'], 68 | [outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length] 69 | ); 70 | 71 | var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize); 72 | 73 | rle._free(inputPtr); 74 | rle._free(outputPtr); 75 | 76 | return output; 77 | } 78 | 79 | /** 80 | * Main RDP module 81 | */ 82 | function RdpClient(config) { 83 | config = config || {}; 84 | this.connected = false; 85 | this.bufferLayer = new layer.BufferLayer(new net.Socket()); 86 | this.tpkt = new TPKT(this.bufferLayer); 87 | this.x224 = new x224.Client(this.tpkt); 88 | this.mcs = new t125.mcs.Client(this.x224); 89 | this.sec = new pdu.sec.Client(this.mcs, this.tpkt); 90 | this.global = new pdu.global.Client(this.sec, this.sec); 91 | 92 | // config log level 93 | log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO; 94 | 95 | // credentials 96 | if (config.domain) { 97 | this.sec.infos.obj.domain.value = new Buffer(config.domain + '\x00', 'ucs2'); 98 | } 99 | if (config.userName) { 100 | this.sec.infos.obj.userName.value = new Buffer(config.userName + '\x00', 'ucs2'); 101 | } 102 | if (config.password) { 103 | this.sec.infos.obj.password.value = new Buffer(config.password + '\x00', 'ucs2'); 104 | } 105 | if(config.workingDir) { 106 | this.sec.infos.obj.workingDir.value = new Buffer(config.workingDir + '\x00', 'ucs2'); 107 | } 108 | if(config.alternateShell) { 109 | this.sec.infos.obj.alternateShell.value = new Buffer(config.alternateShell + '\x00', 'ucs2'); 110 | } 111 | 112 | if (config.enablePerf) { 113 | this.sec.infos.obj.extendedInfo.obj.performanceFlags.value = 114 | pdu.sec.PerfFlag.PERF_DISABLE_WALLPAPER 115 | | pdu.sec.PerfFlag.PERF_DISABLE_MENUANIMATIONS 116 | | pdu.sec.PerfFlag.PERF_DISABLE_CURSOR_SHADOW 117 | | pdu.sec.PerfFlag.PERF_DISABLE_THEMING 118 | | pdu.sec.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG; 119 | } 120 | 121 | if (config.autoLogin) { 122 | this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON; 123 | } 124 | 125 | if (config.screen && config.screen.width && config.screen.height) { 126 | this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width; 127 | this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height; 128 | } 129 | 130 | log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value); 131 | 132 | // config keyboard layout 133 | switch (config.locale) { 134 | case 'fr': 135 | log.debug('french keyboard layout'); 136 | this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH; 137 | break; 138 | case 'en': 139 | default: 140 | log.debug('english keyboard layout'); 141 | this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US; 142 | } 143 | 144 | 145 | //bind all events 146 | var self = this; 147 | this.global.on('connect', function () { 148 | self.connected = true; 149 | self.emit('connect'); 150 | }).on('session', function () { 151 | self.emit('session'); 152 | }).on('close', function () { 153 | self.connected = false; 154 | self.emit('close'); 155 | }).on('bitmap', function (bitmaps) { 156 | for(var bitmap in bitmaps) { 157 | var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value; 158 | var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION; 159 | 160 | if (isCompress && config.decompress) { 161 | bitmapData = decompress(bitmaps[bitmap].obj); 162 | isCompress = false; 163 | } 164 | 165 | self.emit('bitmap', { 166 | destTop : bitmaps[bitmap].obj.destTop.value, 167 | destLeft : bitmaps[bitmap].obj.destLeft.value, 168 | destBottom : bitmaps[bitmap].obj.destBottom.value, 169 | destRight : bitmaps[bitmap].obj.destRight.value, 170 | width : bitmaps[bitmap].obj.width.value, 171 | height : bitmaps[bitmap].obj.height.value, 172 | bitsPerPixel : bitmaps[bitmap].obj.bitsPerPixel.value, 173 | isCompress : isCompress, 174 | data : bitmapData 175 | }); 176 | } 177 | }).on('error', function (err) { 178 | log.warn(err.code + '(' + err.message + ')\n' + err.stack); 179 | if (err instanceof error.FatalError) { 180 | throw err; 181 | } 182 | else { 183 | self.emit('error', err); 184 | } 185 | }); 186 | } 187 | 188 | inherits(RdpClient, events.EventEmitter); 189 | 190 | /** 191 | * Connect RDP client 192 | * @param host {string} destination host 193 | * @param port {integer} destination port 194 | */ 195 | RdpClient.prototype.connect = function (host, port) { 196 | log.debug('connect to ' + host + ':' + port); 197 | var self = this; 198 | this.bufferLayer.socket.connect(port, host, function () { 199 | // in client mode connection start from x224 layer 200 | self.x224.connect(); 201 | }); 202 | return this; 203 | }; 204 | 205 | /** 206 | * Close RDP client 207 | */ 208 | RdpClient.prototype.close = function () { 209 | if(this.connected) { 210 | this.global.close(); 211 | } 212 | this.connected = false; 213 | return this; 214 | }; 215 | 216 | /** 217 | * Send pointer event to server 218 | * @param x {integer} mouse x position 219 | * @param y {integer} mouse y position 220 | * @param button {integer} button number of mouse 221 | * @param isPressed {boolean} state of button 222 | */ 223 | RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) { 224 | if (!this.connected) 225 | return; 226 | 227 | var event = pdu.data.pointerEvent(); 228 | if (isPressed) { 229 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN; 230 | } 231 | 232 | switch(button) { 233 | case 1: 234 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1; 235 | break; 236 | case 2: 237 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2; 238 | break; 239 | case 3: 240 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3 241 | break; 242 | default: 243 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE; 244 | } 245 | 246 | event.obj.xPos.value = x; 247 | event.obj.yPos.value = y; 248 | 249 | this.global.sendInputEvents([event]); 250 | }; 251 | 252 | /** 253 | * send scancode event 254 | * @param code {integer} 255 | * @param isPressed {boolean} 256 | * @param extended {boolenan} extended keys 257 | */ 258 | RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) { 259 | if (!this.connected) 260 | return; 261 | extended = extended || false; 262 | var event = pdu.data.scancodeKeyEvent(); 263 | event.obj.keyCode.value = code; 264 | 265 | if (!isPressed) { 266 | event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE; 267 | } 268 | 269 | if (extended) { 270 | event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED; 271 | } 272 | 273 | this.global.sendInputEvents([event]); 274 | }; 275 | 276 | /** 277 | * Send key event as unicode 278 | * @param code {integer} 279 | * @param isPressed {boolean} 280 | */ 281 | RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) { 282 | if (!this.connected) 283 | return; 284 | 285 | var event = pdu.data.unicodeKeyEvent(); 286 | event.obj.unicode.value = code; 287 | 288 | if (!isPressed) { 289 | event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE; 290 | } 291 | this.global.sendInputEvents([event]); 292 | } 293 | 294 | /** 295 | * Wheel mouse event 296 | * @param x {integer} mouse x position 297 | * @param y {integer} mouse y position 298 | * @param step {integer} wheel step 299 | * @param isNegative {boolean} 300 | * @param isHorizontal {boolean} 301 | */ 302 | RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizontal) { 303 | if (!this.connected) 304 | return; 305 | 306 | var event = pdu.data.pointerEvent(); 307 | if (isHorizontal) { 308 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL; 309 | } 310 | else { 311 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL; 312 | } 313 | 314 | 315 | if (isNegative) { 316 | event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE; 317 | } 318 | 319 | event.obj.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask) 320 | 321 | event.obj.xPos.value = x; 322 | event.obj.yPos.value = y; 323 | 324 | this.global.sendInputEvents([event]); 325 | } 326 | 327 | function createClient(config) { 328 | return new RdpClient(config); 329 | }; 330 | 331 | /** 332 | * RDP server side protocol 333 | * @param config {object} configuration 334 | * @param socket {net.Socket} 335 | */ 336 | function RdpServer(config, socket) { 337 | if (!(config.key && config.cert)) { 338 | throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools') 339 | } 340 | this.connected = false; 341 | this.bufferLayer = new layer.BufferLayer(socket); 342 | this.tpkt = new TPKT(this.bufferLayer); 343 | this.x224 = new x224.Server(this.tpkt, config.key, config.cert); 344 | this.mcs = new t125.mcs.Server(this.x224); 345 | }; 346 | 347 | inherits(RdpServer, events.EventEmitter); 348 | 349 | function createServer (config, next) { 350 | return net.createServer(function (socket) { 351 | next(new RdpServer(config, socket)); 352 | }); 353 | }; 354 | 355 | /** 356 | * Module exports 357 | */ 358 | module.exports = { 359 | createClient : createClient, 360 | createServer : createServer 361 | }; -------------------------------------------------------------------------------- /lib/protocol/t125/gcc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var type = require('../../core').type; 21 | var log = require('../../core').log; 22 | var error = require('../../core').error; 23 | var per = require('./per'); 24 | 25 | 26 | var t124_02_98_oid = [ 0, 0, 20, 124, 0, 1 ]; 27 | var h221_cs_key = "Duca"; 28 | var h221_sc_key = "McDn"; 29 | 30 | 31 | /** 32 | * @see http://msdn.microsoft.com/en-us/library/cc240509.aspx 33 | */ 34 | var MessageType = { 35 | //server -> client 36 | SC_CORE : 0x0C01, 37 | SC_SECURITY : 0x0C02, 38 | SC_NET : 0x0C03, 39 | //client -> server 40 | CS_CORE : 0xC001, 41 | CS_SECURITY : 0xC002, 42 | CS_NET : 0xC003, 43 | CS_CLUSTER : 0xC004, 44 | CS_MONITOR : 0xC005 45 | }; 46 | 47 | /** 48 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 49 | */ 50 | var ColorDepth = { 51 | RNS_UD_COLOR_8BPP : 0xCA01, 52 | RNS_UD_COLOR_16BPP_555 : 0xCA02, 53 | RNS_UD_COLOR_16BPP_565 : 0xCA03, 54 | RNS_UD_COLOR_24BPP : 0xCA04 55 | }; 56 | 57 | /** 58 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 59 | */ 60 | var HighColor = { 61 | HIGH_COLOR_4BPP : 0x0004, 62 | HIGH_COLOR_8BPP : 0x0008, 63 | HIGH_COLOR_15BPP : 0x000f, 64 | HIGH_COLOR_16BPP : 0x0010, 65 | HIGH_COLOR_24BPP : 0x0018 66 | }; 67 | 68 | /** 69 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 70 | */ 71 | var Support = { 72 | RNS_UD_24BPP_SUPPORT : 0x0001, 73 | RNS_UD_16BPP_SUPPORT : 0x0002, 74 | RNS_UD_15BPP_SUPPORT : 0x0004, 75 | RNS_UD_32BPP_SUPPORT : 0x0008 76 | }; 77 | 78 | /** 79 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 80 | */ 81 | var CapabilityFlag = { 82 | RNS_UD_CS_SUPPORT_ERRINFO_PDU : 0x0001, 83 | RNS_UD_CS_WANT_32BPP_SESSION : 0x0002, 84 | RNS_UD_CS_SUPPORT_STATUSINFO_PDU : 0x0004, 85 | RNS_UD_CS_STRONG_ASYMMETRIC_KEYS : 0x0008, 86 | RNS_UD_CS_UNUSED : 0x0010, 87 | RNS_UD_CS_VALID_CONNECTION_TYPE : 0x0020, 88 | RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU : 0x0040, 89 | RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT : 0x0080, 90 | RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL : 0x0100, 91 | RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE : 0x0200, 92 | RNS_UD_CS_SUPPORT_HEARTBEAT_PDU : 0x0400 93 | }; 94 | 95 | /** 96 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 97 | */ 98 | var ConnectionType = { 99 | CONNECTION_TYPE_MODEM : 0x01, 100 | CONNECTION_TYPE_BROADBAND_LOW : 0x02, 101 | CONNECTION_TYPE_SATELLITE : 0x03, 102 | CONNECTION_TYPE_BROADBAND_HIGH : 0x04, 103 | CONNECTION_TYPE_WAN : 0x05, 104 | CONNECTION_TYPE_LAN : 0x06, 105 | CONNECTION_TYPE_AUTODETECT : 0x07 106 | }; 107 | 108 | /** 109 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 110 | */ 111 | var VERSION = { 112 | RDP_VERSION_4 : 0x00080001, 113 | RDP_VERSION_5_PLUS : 0x00080004 114 | }; 115 | 116 | var Sequence = { 117 | RNS_UD_SAS_DEL : 0xAA03 118 | }; 119 | 120 | /** 121 | * @see http://msdn.microsoft.com/en-us/library/cc240511.aspx 122 | */ 123 | var EncryptionMethod = { 124 | ENCRYPTION_FLAG_40BIT : 0x00000001, 125 | ENCRYPTION_FLAG_128BIT : 0x00000002, 126 | ENCRYPTION_FLAG_56BIT : 0x00000008, 127 | FIPS_ENCRYPTION_FLAG : 0x00000010 128 | }; 129 | 130 | /** 131 | * @see http://msdn.microsoft.com/en-us/library/cc240518.aspx 132 | */ 133 | var EncryptionLevel = { 134 | ENCRYPTION_LEVEL_NONE : 0x00000000, 135 | ENCRYPTION_LEVEL_LOW : 0x00000001, 136 | ENCRYPTION_LEVEL_CLIENT_COMPATIBLE : 0x00000002, 137 | ENCRYPTION_LEVEL_HIGH : 0x00000003, 138 | ENCRYPTION_LEVEL_FIPS : 0x00000004 139 | }; 140 | 141 | /** 142 | * @see http://msdn.microsoft.com/en-us/library/cc240513.aspx 143 | */ 144 | var ChannelOptions = { 145 | CHANNEL_OPTION_INITIALIZED : 0x80000000, 146 | CHANNEL_OPTION_ENCRYPT_RDP : 0x40000000, 147 | CHANNEL_OPTION_ENCRYPT_SC : 0x20000000, 148 | CHANNEL_OPTION_ENCRYPT_CS : 0x10000000, 149 | CHANNEL_OPTION_PRI_HIGH : 0x08000000, 150 | CHANNEL_OPTION_PRI_MED : 0x04000000, 151 | CHANNEL_OPTION_PRI_LOW : 0x02000000, 152 | CHANNEL_OPTION_COMPRESS_RDP : 0x00800000, 153 | CHANNEL_OPTION_COMPRESS : 0x00400000, 154 | CHANNEL_OPTION_SHOW_PROTOCOL : 0x00200000, 155 | REMOTE_CONTROL_PERSISTENT : 0x00100000 156 | }; 157 | 158 | /** 159 | * IBM_101_102_KEYS is the most common keyboard type 160 | */ 161 | var KeyboardType = { 162 | IBM_PC_XT_83_KEY : 0x00000001, 163 | OLIVETTI : 0x00000002, 164 | IBM_PC_AT_84_KEY : 0x00000003, 165 | IBM_101_102_KEYS : 0x00000004, 166 | NOKIA_1050 : 0x00000005, 167 | NOKIA_9140 : 0x00000006, 168 | JAPANESE : 0x00000007 169 | }; 170 | 171 | /** 172 | * @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx 173 | */ 174 | var KeyboardLayout = { 175 | ARABIC : 0x00000401, 176 | BULGARIAN : 0x00000402, 177 | CHINESE_US_KEYBOARD : 0x00000404, 178 | CZECH : 0x00000405, 179 | DANISH : 0x00000406, 180 | GERMAN : 0x00000407, 181 | GREEK : 0x00000408, 182 | US : 0x00000409, 183 | SPANISH : 0x0000040a, 184 | FINNISH : 0x0000040b, 185 | FRENCH : 0x0000040c, 186 | HEBREW : 0x0000040d, 187 | HUNGARIAN : 0x0000040e, 188 | ICELANDIC : 0x0000040f, 189 | ITALIAN : 0x00000410, 190 | JAPANESE : 0x00000411, 191 | KOREAN : 0x00000412, 192 | DUTCH : 0x00000413, 193 | NORWEGIAN : 0x00000414 194 | }; 195 | 196 | /** 197 | * @see http://msdn.microsoft.com/en-us/library/cc240521.aspx 198 | */ 199 | var CertificateType = { 200 | CERT_CHAIN_VERSION_1 : 0x00000001, 201 | CERT_CHAIN_VERSION_2 : 0x00000002 202 | }; 203 | 204 | /** 205 | * @param {type.Type} data 206 | * @returns {type.Component} 207 | */ 208 | function block(data) { 209 | var self = { 210 | // type of data block 211 | type : new type.UInt16Le(function() { 212 | return self.data.obj.__TYPE__; 213 | }), 214 | // length of entire packet 215 | length : new type.UInt16Le(function() { 216 | return new type.Component(self).size(); 217 | }), 218 | // data block 219 | data : data || new type.Factory(function(s){ 220 | var options = { 221 | readLength : new type.CallableValue( function () { 222 | return self.length.value - 4; 223 | }) 224 | }; 225 | switch(self.type.value) { 226 | case MessageType.SC_CORE: 227 | self.data = serverCoreData(options).read(s); 228 | break; 229 | case MessageType.SC_SECURITY: 230 | self.data = serverSecurityData(options).read(s); 231 | break; 232 | case MessageType.SC_NET: 233 | self.data = serverNetworkData(null, options).read(s); 234 | break; 235 | case MessageType.CS_CORE: 236 | self.data = clientCoreData(options).read(s); 237 | break; 238 | case MessageType.CS_SECURITY: 239 | self.data = clientSecurityData(options).read(s); 240 | break; 241 | case MessageType.CS_NET: 242 | self.data = clientNetworkData(null, options).read(s); 243 | break; 244 | default: 245 | log.debug("unknown gcc block type " + self.type.value); 246 | self.data = new type.BinaryString(null, options).read(s); 247 | } 248 | }) 249 | }; 250 | 251 | return new type.Component(self); 252 | } 253 | 254 | /** 255 | * Main client informations 256 | * keyboard 257 | * screen definition 258 | * color depth 259 | * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx 260 | * @param opt {object} Classic type options 261 | * @returns {type.Component} 262 | */ 263 | function clientCoreData(opt) { 264 | var self = { 265 | __TYPE__ : MessageType.CS_CORE, 266 | rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS), 267 | desktopWidth : new type.UInt16Le(1280), 268 | desktopHeight : new type.UInt16Le(800), 269 | colorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP), 270 | sasSequence : new type.UInt16Le(Sequence.RNS_UD_SAS_DEL), 271 | kbdLayout : new type.UInt32Le(KeyboardLayout.FRENCH), 272 | clientBuild : new type.UInt32Le(3790), 273 | clientName : new type.BinaryString(new Buffer('node-rdpjs\x00\x00\x00\x00\x00\x00', 'ucs2'), { readLength : new type.CallableValue(32) }), 274 | keyboardType : new type.UInt32Le(KeyboardType.IBM_101_102_KEYS), 275 | keyboardSubType : new type.UInt32Le(0), 276 | keyboardFnKeys : new type.UInt32Le(12), 277 | imeFileName : new type.BinaryString(new Buffer(Array(64 + 1).join('\x00')), { readLength : new type.CallableValue(64), optional : true }), 278 | postBeta2ColorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, { optional : true }), 279 | clientProductId : new type.UInt16Le(1, { optional : true }), 280 | serialNumber : new type.UInt32Le(0, { optional : true }), 281 | highColorDepth : new type.UInt16Le(HighColor.HIGH_COLOR_24BPP, { optional : true }), 282 | 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 }), 283 | earlyCapabilityFlags : new type.UInt16Le(CapabilityFlag.RNS_UD_CS_SUPPORT_ERRINFO_PDU, { optional : true }), 284 | clientDigProductId : new type.BinaryString(new Buffer(Array(64 + 1).join('\x00')), { optional : true, readLength : new type.CallableValue(64) }), 285 | connectionType : new type.UInt8(0, { optional : true }), 286 | pad1octet : new type.UInt8(0, { optional : true }), 287 | serverSelectedProtocol : new type.UInt32Le(0, { optional : true }) 288 | }; 289 | 290 | return new type.Component(self, opt); 291 | } 292 | 293 | /** 294 | * @see http://msdn.microsoft.com/en-us/library/cc240517.aspx 295 | * @param opt {object} Classic type options 296 | * @returns {type.Component} 297 | */ 298 | function serverCoreData(opt) { 299 | var self = { 300 | __TYPE__ : MessageType.SC_CORE, 301 | rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS), 302 | clientRequestedProtocol : new type.UInt32Le(null, { optional : true }), 303 | earlyCapabilityFlags : new type.UInt32Le(null, { optional : true }) 304 | }; 305 | 306 | return new type.Component(self, opt); 307 | } 308 | 309 | /** 310 | * @see http://msdn.microsoft.com/en-us/library/cc240511.aspx 311 | * @param opt {object} Classic type options 312 | * @returns {type.Component} 313 | */ 314 | function clientSecurityData(opt) { 315 | var self = { 316 | __TYPE__ : MessageType.CS_SECURITY, 317 | encryptionMethods : new type.UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT), 318 | extEncryptionMethods : new type.UInt32Le() 319 | }; 320 | 321 | return new type.Component(self, opt); 322 | } 323 | 324 | /** 325 | * Only use for SSL (RDP security layer TODO) 326 | * @see http://msdn.microsoft.com/en-us/library/cc240518.aspx 327 | * @param opt {object} Classic type options 328 | * @returns {type.Component} 329 | */ 330 | function serverSecurityData(opt) { 331 | var self = { 332 | __TYPE__ : MessageType.SC_SECURITY, 333 | encryptionMethod : new type.UInt32Le(), 334 | encryptionLevel : new type.UInt32Le() 335 | }; 336 | 337 | return new type.Component(self, opt); 338 | } 339 | 340 | /** 341 | * Channel definition 342 | * @param opt {object} Classic type options 343 | * @returns {type.Component} 344 | */ 345 | function channelDef (opt) { 346 | var self = { 347 | name : new type.BinaryString(null, { readLength : new type.CallableValue(8) }), 348 | options : new type.UInt32Le() 349 | }; 350 | 351 | return new type.Component(self, opt); 352 | } 353 | 354 | /** 355 | * Optional channel requests (sound, clipboard ...) 356 | * @param opt {object} Classic type options 357 | * @returns {type.Component} 358 | */ 359 | function clientNetworkData(channelDefArray, opt) { 360 | var self = { 361 | __TYPE__ : MessageType.CS_NET, 362 | channelCount : new type.UInt32Le( function () { 363 | return self.channelDefArray.obj.length; 364 | }), 365 | channelDefArray : channelDefArray || new type.Factory( function (s) { 366 | self.channelDefArray = new type.Component([]); 367 | 368 | for (var i = 0; i < self.channelCount.value; i++) { 369 | self.channelDefArray.obj.push(channelDef().read(s)); 370 | } 371 | }) 372 | }; 373 | 374 | return new type.Component(self, opt); 375 | } 376 | 377 | /** 378 | * @param channelIds {type.Component} list of available channels 379 | * @param opt {object} Classic type options 380 | * @returns {type.Component} 381 | */ 382 | function serverNetworkData (channelIds, opt) { 383 | var self = { 384 | __TYPE__ : MessageType.SC_NET, 385 | MCSChannelId : new type.UInt16Le(1003, { constant : true }), 386 | channelCount : new type.UInt16Le(function () { 387 | return self.channelIdArray.obj.length; 388 | }), 389 | channelIdArray : channelIds || new type.Factory( function (s) { 390 | self.channelIdArray = new type.Component([]); 391 | for (var i = 0; i < self.channelCount.value; i++) { 392 | self.channelIdArray.obj.push(new type.UInt16Le().read(s)); 393 | } 394 | }), 395 | pad : new type.UInt16Le(null, { conditional : function () { 396 | return (self.channelCount.value % 2) === 1; 397 | }}) 398 | }; 399 | 400 | return new type.Component(self, opt); 401 | } 402 | 403 | /** 404 | * Client or server GCC settings block 405 | * @param blocks {type.Component} array of gcc blocks 406 | * @param opt {object} options to component type 407 | * @returns {type.Component} 408 | */ 409 | function settings(blocks, opt) { 410 | var self = { 411 | blocks : blocks || new type.Factory(function(s) { 412 | self.blocks = new type.Component([]); 413 | // read until end of stream 414 | while(s.availableLength() > 0) { 415 | self.blocks.obj.push(block().read(s)); 416 | } 417 | }), 418 | }; 419 | 420 | return new type.Component(self, opt); 421 | } 422 | 423 | /** 424 | * Read GCC response from server 425 | * @param s {type.Stream} current stream 426 | * @returns {Array(type.Component)} list of server block 427 | */ 428 | function readConferenceCreateResponse(s) { 429 | per.readChoice(s); 430 | 431 | if(!per.readObjectIdentifier(s, t124_02_98_oid)) { 432 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124'); 433 | } 434 | 435 | per.readLength(s); 436 | per.readChoice(s); 437 | per.readInteger16(s, 1001); 438 | per.readInteger(s); 439 | per.readEnumerates(s); 440 | per.readNumberOfSet(s); 441 | per.readChoice(s); 442 | 443 | if (!per.readOctetStream(s, h221_sc_key, 4)) { 444 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY'); 445 | } 446 | 447 | length = per.readLength(s); 448 | serverSettings = settings(null, { readLength : new type.CallableValue(length) }); 449 | 450 | // Object magic 451 | return serverSettings.read(s).obj.blocks.obj.map(function(e) { 452 | return e.obj.data; 453 | }); 454 | } 455 | 456 | /** 457 | * Read GCC request 458 | * @param s {type.Stream} 459 | * @returns {Array(type.Component)} list of client block 460 | */ 461 | function readConferenceCreateRequest (s) { 462 | per.readChoice(s); 463 | if (!per.readObjectIdentifier(s, t124_02_98_oid)) { 464 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY'); 465 | } 466 | per.readLength(s); 467 | per.readChoice(s); 468 | per.readSelection(s); 469 | per.readNumericString(s, 1); 470 | per.readPadding(s, 1); 471 | 472 | if (per.readNumberOfSet(s) !== 1) { 473 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_SET'); 474 | } 475 | 476 | if (per.readChoice(s) !== 0xc0) { 477 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_CHOICE'); 478 | } 479 | 480 | per.readOctetStream(s, h221_cs_key, 4); 481 | 482 | length = per.readLength(s); 483 | var clientSettings = settings(null, { readLength : new type.CallableValue(length) }); 484 | 485 | // Object magic 486 | return clientSettings.read(s).obj.blocks.obj.map(function(e) { 487 | return e.obj.data; 488 | }); 489 | } 490 | 491 | /** 492 | * Built {type.Componen} from gcc user data 493 | * @param userData {type.Component} GCC data from client 494 | * @returns {type.Component} GCC encoded client user data 495 | */ 496 | function writeConferenceCreateRequest (userData) { 497 | var userDataStream = new type.Stream(userData.size()); 498 | userData.write(userDataStream); 499 | 500 | return new type.Component([ 501 | per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid), 502 | per.writeLength(userData.size() + 14), per.writeChoice(0), 503 | per.writeSelection(0x08), per.writeNumericString("1", 1), per.writePadding(1), 504 | per.writeNumberOfSet(1), per.writeChoice(0xc0), 505 | per.writeOctetStream(new Buffer(h221_cs_key), 4), per.writeOctetStream(userDataStream.getValue()) 506 | ]); 507 | } 508 | 509 | function writeConferenceCreateResponse (userData) { 510 | var userDataStream = new type.Stream(userData.size()); 511 | userData.write(userDataStream); 512 | 513 | return new type.Component([ 514 | per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid), 515 | per.writeLength(userData.size() + 14), per.writeChoice(0x14), 516 | per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(0), 517 | per.writeNumberOfSet(1), per.writeChoice(0xc0), 518 | per.writeOctetStream(new Buffer(h221_sc_key), 4), per.writeOctetStream(userDataStream.getValue()) 519 | ]); 520 | } 521 | 522 | /** 523 | * Module exports 524 | */ 525 | module.exports = { 526 | MessageType : MessageType, 527 | VERSION : VERSION, 528 | KeyboardLayout : KeyboardLayout, 529 | block : block, 530 | clientCoreData : clientCoreData, 531 | clientNetworkData : clientNetworkData, 532 | clientSecurityData : clientSecurityData, 533 | serverCoreData : serverCoreData, 534 | serverSecurityData : serverSecurityData, 535 | serverNetworkData : serverNetworkData, 536 | readConferenceCreateResponse : readConferenceCreateResponse, 537 | readConferenceCreateRequest : readConferenceCreateRequest, 538 | writeConferenceCreateRequest : writeConferenceCreateRequest, 539 | writeConferenceCreateResponse : writeConferenceCreateResponse 540 | }; -------------------------------------------------------------------------------- /lib/protocol/t125/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var mcs = require('./mcs'); 21 | var gcc = require('./gcc'); 22 | 23 | module.exports = { 24 | mcs : mcs, 25 | gcc : gcc 26 | }; 27 | -------------------------------------------------------------------------------- /lib/protocol/t125/mcs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var events = require('events'); 22 | var type = require('../../core').type; 23 | var log = require('../../core').log; 24 | var error = require('../../core').error; 25 | var gcc = require('./gcc'); 26 | var per = require('./per'); 27 | var asn1 = require('../../asn1'); 28 | 29 | var Message = { 30 | MCS_TYPE_CONNECT_INITIAL : 0x65, 31 | MCS_TYPE_CONNECT_RESPONSE : 0x66 32 | }; 33 | 34 | var DomainMCSPDU = { 35 | ERECT_DOMAIN_REQUEST : 1, 36 | DISCONNECT_PROVIDER_ULTIMATUM : 8, 37 | ATTACH_USER_REQUEST : 10, 38 | ATTACH_USER_CONFIRM : 11, 39 | CHANNEL_JOIN_REQUEST : 14, 40 | CHANNEL_JOIN_CONFIRM : 15, 41 | SEND_DATA_REQUEST : 25, 42 | SEND_DATA_INDICATION : 26 43 | }; 44 | 45 | var Channel = { 46 | MCS_GLOBAL_CHANNEL : 1003, 47 | MCS_USERCHANNEL_BASE : 1001 48 | }; 49 | 50 | /** 51 | * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 52 | * @returns {asn1.univ.Sequence} 53 | */ 54 | function DomainParameters(maxChannelIds, maxUserIds, maxTokenIds, 55 | numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion) { 56 | return new asn1.univ.Sequence({ 57 | maxChannelIds : new asn1.univ.Integer(maxChannelIds), 58 | maxUserIds : new asn1.univ.Integer(maxUserIds), 59 | maxTokenIds : new asn1.univ.Integer(maxTokenIds), 60 | numPriorities : new asn1.univ.Integer(numPriorities), 61 | minThoughput : new asn1.univ.Integer(minThoughput), 62 | maxHeight : new asn1.univ.Integer(maxHeight), 63 | maxMCSPDUsize : new asn1.univ.Integer(maxMCSPDUsize), 64 | protocolVersion : new asn1.univ.Integer(protocolVersion) 65 | }); 66 | } 67 | 68 | /** 69 | * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 70 | * @param userData {Buffer} 71 | * @returns {asn1.univ.Sequence} 72 | */ 73 | function ConnectInitial (userData) { 74 | return new asn1.univ.Sequence({ 75 | callingDomainSelector : new asn1.univ.OctetString(new Buffer('\x01', 'binary')), 76 | calledDomainSelector : new asn1.univ.OctetString(new Buffer('\x01', 'binary')), 77 | upwardFlag : new asn1.univ.Boolean(true), 78 | targetParameters : DomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2), 79 | minimumParameters : DomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2), 80 | maximumParameters : DomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2), 81 | userData : new asn1.univ.OctetString(userData) 82 | }).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 101)); 83 | } 84 | 85 | /** 86 | * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 87 | * @returns {asn1.univ.Sequence} 88 | */ 89 | function ConnectResponse (userData) { 90 | return new asn1.univ.Sequence({ 91 | result : new asn1.univ.Enumerate(0), 92 | calledConnectId : new asn1.univ.Integer(0), 93 | domainParameters : DomainParameters(22, 3, 0, 1, 0, 1,0xfff8, 2), 94 | userData : new asn1.univ.OctetString(userData) 95 | }).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 102)); 96 | } 97 | 98 | /** 99 | * Format MCS PDU header packet 100 | * @param mcsPdu {integer} 101 | * @param options {integer} 102 | * @returns {type.UInt8} headers 103 | */ 104 | function writeMCSPDUHeader(mcsPdu, options) { 105 | options = options || 0; 106 | return new type.UInt8((mcsPdu << 2) | options); 107 | } 108 | 109 | /** 110 | * Read MCS PDU header 111 | * @param opcode 112 | * @param mcsPdu 113 | * @returns {Boolean} 114 | */ 115 | function readMCSPDUHeader(opcode, mcsPdu) { 116 | return (opcode >> 2) === mcsPdu; 117 | } 118 | 119 | /** 120 | * Multi-Channel Services 121 | * @param transport {events.EventEmitter} transport layer listen (connect, data) events 122 | * @param recvOpCode {DomainMCSPDU} opcode use in receive automata 123 | * @param sendOpCode {DomainMCSPDU} opcode use to send message 124 | */ 125 | function MCS(transport, recvOpCode, sendOpCode) { 126 | this.transport = transport; 127 | this.recvOpCode = recvOpCode; 128 | this.sendOpCode = sendOpCode; 129 | this.channels = [{id : Channel.MCS_GLOBAL_CHANNEL, name : 'global'}]; 130 | this.channels.find = function(callback) { 131 | for(var i in this) { 132 | if(callback(this[i])) return this[i]; 133 | }; 134 | }; 135 | 136 | // bind events 137 | var self = this; 138 | this.transport.on('close', function () { 139 | self.emit('close'); 140 | }).on('error', function (err) { 141 | self.emit('error', err); 142 | }); 143 | } 144 | 145 | //inherit from Layer 146 | inherits(MCS, events.EventEmitter); 147 | 148 | /** 149 | * Send message to a specific channel 150 | * @param channelName {string} name of channel 151 | * @param data {type.*} message to send 152 | */ 153 | MCS.prototype.send = function(channelName, data) { 154 | var channelId = this.channels.find(function(element) { 155 | if (element.name === channelName) return true; 156 | }).id; 157 | 158 | this.transport.send(new type.Component([ 159 | writeMCSPDUHeader(this.sendOpCode), 160 | per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE), 161 | per.writeInteger16(channelId), 162 | new type.UInt8(0x70), 163 | per.writeLength(data.size()), 164 | data 165 | ])); 166 | }; 167 | 168 | /** 169 | * Main receive function 170 | * @param s {type.Stream} 171 | */ 172 | MCS.prototype.recv = function(s) { 173 | opcode = new type.UInt8().read(s).value; 174 | 175 | if (readMCSPDUHeader(opcode, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM)) { 176 | log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM"); 177 | this.transport.close(); 178 | return 179 | } 180 | else if(!readMCSPDUHeader(opcode, this.recvOpCode)) { 181 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_RECEIVE_OPCODE'); 182 | } 183 | 184 | per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE); 185 | 186 | var channelId = per.readInteger16(s); 187 | 188 | per.readEnumerates(s); 189 | per.readLength(s); 190 | 191 | var channelName = this.channels.find(function(e) { 192 | if (e.id === channelId) return true; 193 | }).name; 194 | 195 | this.emit(channelName, s); 196 | }; 197 | 198 | /** 199 | * Only main channels handle actually 200 | * @param transport {event.EventEmitter} bind connect and data events 201 | * @returns 202 | */ 203 | function Client(transport) { 204 | MCS.call(this, transport, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST); 205 | 206 | // channel context automata 207 | this.channelsConnected = 0; 208 | 209 | // init gcc information 210 | this.clientCoreData = gcc.clientCoreData(); 211 | this.clientNetworkData = gcc.clientNetworkData(new type.Component([])); 212 | this.clientSecurityData = gcc.clientSecurityData(); 213 | 214 | // must be readed from protocol 215 | this.serverCoreData = null; 216 | this.serverSecurityData = null; 217 | this.serverNetworkData = null; 218 | 219 | var self = this; 220 | this.transport.on('connect', function(s) { 221 | self.connect(s); 222 | }); 223 | } 224 | 225 | inherits(Client, MCS); 226 | 227 | /** 228 | * Connect event layer 229 | */ 230 | Client.prototype.connect = function(selectedProtocol) { 231 | this.clientCoreData.obj.serverSelectedProtocol.value = selectedProtocol; 232 | this.sendConnectInitial(); 233 | }; 234 | 235 | /** 236 | * close stack 237 | */ 238 | Client.prototype.close = function() { 239 | this.transport.close(); 240 | }; 241 | 242 | /** 243 | * MCS connect response (server->client) 244 | * @param s {type.Stream} 245 | */ 246 | Client.prototype.recvConnectResponse = function(s) { 247 | var userData = new type.Stream(ConnectResponse().decode(s, asn1.ber).value.userData.value); 248 | var serverSettings = gcc.readConferenceCreateResponse(userData); 249 | // record server gcc block 250 | for(var i in serverSettings) { 251 | if(!serverSettings[i].obj) { 252 | continue; 253 | } 254 | switch(serverSettings[i].obj.__TYPE__) { 255 | case gcc.MessageType.SC_CORE: 256 | this.serverCoreData = serverSettings[i]; 257 | break; 258 | case gcc.MessageType.SC_SECURITY: 259 | this.serverSecurityData = serverSettings[i]; 260 | break; 261 | case gcc.MessageType.SC_NET: 262 | this.serverNetworkData = serverSettings[i]; 263 | break; 264 | default: 265 | log.warn('unhandle server gcc block : ' + serverSettings[i].obj.__TYPE__); 266 | } 267 | } 268 | 269 | // send domain request 270 | this.sendErectDomainRequest(); 271 | // send attach user request 272 | this.sendAttachUserRequest(); 273 | // now wait user confirm from server 274 | var self = this; 275 | this.transport.once('data', function(s) { 276 | self.recvAttachUserConfirm(s); 277 | }); 278 | }; 279 | 280 | /** 281 | * MCS connection automata step 282 | * @param s {type.Stream} 283 | */ 284 | Client.prototype.recvAttachUserConfirm = function(s) { 285 | if (!readMCSPDUHeader(new type.UInt8().read(s).value, DomainMCSPDU.ATTACH_USER_CONFIRM)) { 286 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER'); 287 | } 288 | 289 | if (per.readEnumerates(s) !== 0) { 290 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER'); 291 | } 292 | 293 | this.userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE); 294 | //ask channel for specific user 295 | this.channels.push({ id : this.userId, name : 'user' }); 296 | // channel connect automata 297 | this.connectChannels(); 298 | }; 299 | 300 | /** 301 | * Last state in channel connection automata 302 | * @param s {type.Stream} 303 | */ 304 | Client.prototype.recvChannelJoinConfirm = function(s) { 305 | var opcode = new type.UInt8().read(s).value; 306 | 307 | if (!readMCSPDUHeader(opcode, DomainMCSPDU.CHANNEL_JOIN_CONFIRM)) { 308 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM'); 309 | } 310 | 311 | var confirm = per.readEnumerates(s); 312 | 313 | var userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE); 314 | if (this.userId !== userId) { 315 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID'); 316 | } 317 | 318 | var channelId = per.readInteger16(s); 319 | 320 | if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) { 321 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL'); 322 | } 323 | 324 | this.connectChannels(); 325 | }; 326 | 327 | /** 328 | * First MCS message 329 | */ 330 | Client.prototype.sendConnectInitial = function() { 331 | 332 | var ccReq = gcc.writeConferenceCreateRequest(new type.Component([ 333 | gcc.block(this.clientCoreData), 334 | gcc.block(this.clientNetworkData), 335 | gcc.block(this.clientSecurityData) 336 | ])).toStream().getValue(); 337 | 338 | this.transport.send(ConnectInitial(ccReq).encode(asn1.ber)); 339 | 340 | // next event is connect response 341 | var self = this; 342 | this.transport.once('data', function(s) { 343 | self.recvConnectResponse(s); 344 | }); 345 | }; 346 | 347 | /** 348 | * MCS connection automata step 349 | */ 350 | Client.prototype.sendErectDomainRequest = function() { 351 | this.transport.send(new type.Component([ 352 | writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST), 353 | per.writeInteger(0), 354 | per.writeInteger(0) 355 | ])); 356 | }; 357 | 358 | /** 359 | * MCS connection automata step 360 | */ 361 | Client.prototype.sendAttachUserRequest = function() { 362 | this.transport.send(writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST)); 363 | }; 364 | 365 | /** 366 | * Send a channel join request 367 | * @param channelId {integer} channel id 368 | */ 369 | Client.prototype.sendChannelJoinRequest = function(channelId) { 370 | this.transport.send(new type.Component([ 371 | writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST), 372 | per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE), 373 | per.writeInteger16(channelId) 374 | ])); 375 | }; 376 | 377 | /** 378 | * Connect channels automata 379 | * @param s {type.Stream} 380 | */ 381 | Client.prototype.connectChannels = function(s) { 382 | if(this.channelsConnected == this.channels.length) { 383 | var self = this; 384 | this.transport.on('data', function(s) { 385 | self.recv(s); 386 | }); 387 | 388 | // send client and sever gcc informations 389 | this.emit('connect', 390 | { 391 | core : this.clientCoreData.obj, 392 | security : this.clientSecurityData.obj, 393 | net : this.clientNetworkData.obj 394 | }, 395 | { 396 | core : this.serverCoreData.obj, 397 | security : this.serverSecurityData.obj 398 | }, this.userId, this.channels); 399 | return; 400 | } 401 | 402 | this.sendChannelJoinRequest(this.channels[this.channelsConnected++].id); 403 | 404 | var self = this; 405 | this.transport.once('data', function(s) { 406 | self.recvChannelJoinConfirm(s); 407 | }); 408 | }; 409 | 410 | /** 411 | * Server side of MCS layer 412 | * @param transport 413 | */ 414 | function Server (transport) { 415 | MCS.call(this, transport, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION); 416 | 417 | // must be readed from protocol 418 | this.clientCoreData = null; 419 | this.clientNetworkData = null; 420 | this.clientSecurityData = null; 421 | 422 | // init gcc information 423 | this.serverCoreData = gcc.serverCoreData(); 424 | this.serverSecurityData = gcc.serverSecurityData(); 425 | this.serverNetworkData = gcc.serverNetworkData(new type.Component([])); 426 | 427 | var self = this; 428 | this.transport.on('connect', function (selectedProtocol) { 429 | self.serverCoreData.obj.clientRequestedProtocol.value = selectedProtocol; 430 | }).once('data', function (s) { 431 | self.recvConnectInitial(s); 432 | }); 433 | } 434 | 435 | inherits(Server, MCS); 436 | 437 | /** 438 | * First state of server automata 439 | * @param s {type.Stream} 440 | */ 441 | Server.prototype.recvConnectInitial = function (s) { 442 | var userData = new type.Stream(ConnectInitial().decode(s, asn1.ber).value.userData.value); 443 | var clientSettings = gcc.readConferenceCreateRequest(userData); 444 | // record server gcc block 445 | for(var i in clientSettings) { 446 | if(!clientSettings[i].obj) { 447 | continue; 448 | } 449 | switch(clientSettings[i].obj.__TYPE__) { 450 | case gcc.MessageType.CS_CORE: 451 | this.clientCoreData = clientSettings[i]; 452 | break; 453 | case gcc.MessageType.CS_SECURITY: 454 | this.clientSecurityData = clientSettings[i]; 455 | break; 456 | case gcc.MessageType.CS_NET: 457 | this.clientNetworkData = clientSettings[i]; 458 | for (var i = 0; i < this.clientNetworkData.obj.channelCount.value; i++) { 459 | this.serverNetworkData.obj.channelIdArray.obj.push(new type.UInt16Le( i + 1 + Channel.MCS_GLOBAL_CHANNEL)); 460 | } 461 | break; 462 | default: 463 | log.debug('unhandle client gcc block : ' + clientSettings[i].obj.__TYPE__); 464 | } 465 | } 466 | 467 | this.sendConnectResponse(); 468 | }; 469 | 470 | /** 471 | * State 2 in mcs server connetion automata 472 | */ 473 | Server.prototype.sendConnectResponse = function () { 474 | var ccReq = gcc.writeConferenceCreateResponse(new type.Component([ 475 | gcc.block(this.serverCoreData), 476 | gcc.block(this.serverSecurityData), 477 | gcc.block(this.serverNetworkData), 478 | ])).toStream().getValue(); 479 | 480 | this.transport.send(ConnectResponse(ccReq).encode(asn1.ber)); 481 | 482 | }; 483 | 484 | /** 485 | * Module exports 486 | */ 487 | module.exports = { 488 | Client : Client, 489 | Server : Server 490 | }; 491 | -------------------------------------------------------------------------------- /lib/protocol/t125/per.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var type = require('../../core').type; 21 | var error = require('../../core').error; 22 | 23 | /** 24 | * @param s {type.Stream} read value from stream 25 | * @returns read length from per format 26 | */ 27 | function readLength(s) { 28 | var byte = new type.UInt8().read(s).value; 29 | var size = 0; 30 | if(byte & 0x80) { 31 | byte &= ~0x80; 32 | size = byte << 8; 33 | size += new type.UInt8().read(s).value; 34 | } 35 | else { 36 | size = byte; 37 | } 38 | return size; 39 | } 40 | 41 | /** 42 | * @param value {raw} value to convert to per format 43 | * @returns type objects per encoding value 44 | */ 45 | function writeLength(value) { 46 | if(value > 0x7f) { 47 | return new type.UInt16Be(value | 0x8000); 48 | } 49 | else { 50 | return new type.UInt8(value); 51 | } 52 | } 53 | 54 | /** 55 | * @param s {type.Stream} 56 | * @returns {integer} choice decoding from per encoding 57 | */ 58 | function readChoice(s) { 59 | return new type.UInt8().read(s).value; 60 | } 61 | 62 | /** 63 | * @param choice {integer} 64 | * @returns {type.UInt8} choice per encoded 65 | */ 66 | function writeChoice(choice) { 67 | return new type.UInt8(choice); 68 | } 69 | 70 | /** 71 | * @param s {type.Stream} 72 | * @returns {integer} number represent selection 73 | */ 74 | function readSelection(s) { 75 | return new type.UInt8().read(s).value; 76 | } 77 | 78 | /** 79 | * @param selection {integer} 80 | * @returns {type.UInt8} per encoded selection 81 | */ 82 | function writeSelection(selection) { 83 | return new type.UInt8(selection); 84 | } 85 | 86 | /** 87 | * @param s {type.Stream} 88 | * @returns {integer} number of sets 89 | */ 90 | function readNumberOfSet(s) { 91 | return new type.UInt8().read(s).value; 92 | } 93 | 94 | /** 95 | * @param numberOfSet {integer} 96 | * @returns {type.UInt8} per encoded nuimber of sets 97 | */ 98 | function writeNumberOfSet(numberOfSet) { 99 | return new type.UInt8(numberOfSet); 100 | } 101 | 102 | /** 103 | * @param s {type.Stream} 104 | * @returns {integer} enumerates number 105 | */ 106 | function readEnumerates(s) { 107 | return new type.UInt8().read(s).value; 108 | } 109 | 110 | /** 111 | * @param enumerate {integer} 112 | * @returns {type.UInt8} per encoded enumerate 113 | */ 114 | function writeEnumerates(enumerate) { 115 | return new type.UInt8(enumerate); 116 | } 117 | 118 | /** 119 | * @param s {type.Stream} 120 | * @returns {integer} integer per decoded 121 | */ 122 | function readInteger(s) { 123 | var result; 124 | var size = readLength(s); 125 | switch(size) { 126 | case 1: 127 | result = new type.UInt8(); 128 | break; 129 | case 2: 130 | result = new type.UInt16Be(); 131 | break; 132 | case 4: 133 | result = new type.UInt32Be(); 134 | break; 135 | default: 136 | throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_PER_BAD_INTEGER_LENGTH'); 137 | } 138 | return result.read(s).value; 139 | } 140 | 141 | /** 142 | * @param value {integer} 143 | * @returns {type.Component} per encoded integer 144 | */ 145 | function writeInteger(value) { 146 | if(value <= 0xff) { 147 | return new type.Component([writeLength(1), new type.UInt8(value)]); 148 | } 149 | else if(value < 0xffff) { 150 | return new type.Component([writeLength(2), new type.UInt16Be(value)]); 151 | } 152 | else { 153 | return new type.Component([writeLength(4), new type.UInt32Be(value)]); 154 | } 155 | } 156 | 157 | /** 158 | * @param s {type.Stream} 159 | * @param minimum {integer} increment (default 0) 160 | * @returns {integer} per decoded integer 16 bits 161 | */ 162 | function readInteger16(s, minimum) { 163 | return new type.UInt16Be().read(s).value + (minimum || 0); 164 | } 165 | 166 | /** 167 | * @param value {integer} 168 | * @param minimum {integer} decrement (default 0) 169 | * @returns {type.UInt16Be} per encoded integer 16 bits 170 | */ 171 | function writeInteger16(value, minimum) { 172 | return new type.UInt16Be(value - (minimum || 0)); 173 | } 174 | 175 | /** 176 | * Check object identifier 177 | * @param s {type.Stream} 178 | * @param oid {array} object identifier to check 179 | */ 180 | function readObjectIdentifier(s, oid) { 181 | var size = readLength(s); 182 | if(size !== 5) { 183 | return false; 184 | } 185 | 186 | var a_oid = [0, 0, 0, 0, 0, 0]; 187 | var t12 = new type.UInt8().read(s).value; 188 | a_oid[0] = t12 >> 4; 189 | a_oid[1] = t12 & 0x0f; 190 | a_oid[2] = new type.UInt8().read(s).value; 191 | a_oid[3] = new type.UInt8().read(s).value; 192 | a_oid[4] = new type.UInt8().read(s).value; 193 | a_oid[5] = new type.UInt8().read(s).value; 194 | 195 | for(var i in oid) { 196 | if(oid[i] !== a_oid[i]) return false; 197 | } 198 | 199 | return true; 200 | } 201 | 202 | /** 203 | * @param oid {array} oid to write 204 | * @returns {type.Component} per encoded object identifier 205 | */ 206 | function writeObjectIdentifier(oid) { 207 | 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])]); 208 | } 209 | 210 | /** 211 | * Read as padding... 212 | * @param s {type.Stream} 213 | * @param minValue 214 | */ 215 | function readNumericString(s, minValue) { 216 | var length = readLength(s); 217 | length = (length + minValue + 1) / 2; 218 | s.readPadding(length); 219 | } 220 | 221 | /** 222 | * @param nStr {String} 223 | * @param minValue {integer} 224 | * @returns {type.Component} per encoded numeric string 225 | */ 226 | function writeNumericString(nStr, minValue) { 227 | var length = nStr.length; 228 | var mlength = minValue; 229 | if(length - minValue >= 0) { 230 | mlength = length - minValue; 231 | } 232 | 233 | var result = []; 234 | 235 | for(var i = 0; i < length; i += 2) { 236 | var c1 = nStr.charCodeAt(i); 237 | var c2 = 0; 238 | if(i + 1 < length) { 239 | c2 = nStr.charCodeAt(i + 1); 240 | } 241 | else { 242 | c2 = 0x30; 243 | } 244 | c1 = (c1 - 0x30) % 10; 245 | c2 = (c2 - 0x30) % 10; 246 | 247 | result[result.length] = new type.UInt8((c1 << 4) | c2); 248 | } 249 | 250 | return new type.Component([writeLength(mlength), new type.Component(result)]); 251 | } 252 | 253 | /** 254 | * @param s {type.Stream} 255 | * @param length {integer} length of padding 256 | */ 257 | function readPadding(s, length) { 258 | s.readPadding(length); 259 | } 260 | 261 | /** 262 | * @param length {integer} length of padding 263 | * @returns {type.BinaryString} per encoded padding 264 | */ 265 | function writePadding(length) { 266 | return new type.BinaryString(new Buffer(Array(length + 1).join("\x00"))); 267 | } 268 | 269 | /** 270 | * @param s {type.Stream} 271 | * @param octetStream {String} 272 | * @param minValue {integer} default 0 273 | * @returns {Boolean} true if read octectStream is equal to octetStream 274 | */ 275 | function readOctetStream(s, octetStream, minValue) { 276 | var size = readLength(s) + (minValue || 0); 277 | if(size !== octetStream.length) { 278 | return false; 279 | } 280 | for(var i = 0; i < size; i++) { 281 | var c = new type.UInt8().read(s); 282 | if(octetStream.charCodeAt(i) !== c.value) { 283 | return false; 284 | } 285 | } 286 | 287 | return true; 288 | } 289 | 290 | /** 291 | * @param oStr {String} 292 | * @param minValue {integer} default 0 293 | * @returns {type.Component} per encoded octet stream 294 | */ 295 | function writeOctetStream(oStr, minValue) { 296 | minValue = minValue || 0; 297 | var length = oStr.length; 298 | var mlength = minValue; 299 | 300 | if(length - minValue >= 0) { 301 | mlength = length - minValue; 302 | } 303 | 304 | result = []; 305 | for(var i = 0; i < length; i++) { 306 | result[result.length] = new type.UInt8(oStr[i]); 307 | } 308 | 309 | return new type.Component([writeLength(mlength), new type.Component(result)]); 310 | } 311 | 312 | /** 313 | * Module exports 314 | */ 315 | module.exports = { 316 | readLength : readLength, 317 | writeLength : writeLength, 318 | readChoice : readChoice, 319 | writeChoice : writeChoice, 320 | readSelection : readSelection, 321 | writeSelection : writeSelection, 322 | readNumberOfSet : readNumberOfSet, 323 | writeNumberOfSet : writeNumberOfSet, 324 | readEnumerates : readEnumerates, 325 | writeEnumerates : writeEnumerates, 326 | readInteger : readInteger, 327 | writeInteger : writeInteger, 328 | readInteger16 : readInteger16, 329 | writeInteger16 : writeInteger16, 330 | readObjectIdentifier : readObjectIdentifier, 331 | writeObjectIdentifier : writeObjectIdentifier, 332 | readNumericString : readNumericString, 333 | writeNumericString : writeNumericString, 334 | readPadding : readPadding, 335 | writePadding : writePadding, 336 | readOctetStream : readOctetStream, 337 | writeOctetStream : writeOctetStream 338 | }; -------------------------------------------------------------------------------- /lib/protocol/tpkt.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var type = require('../core').type; 22 | var events = require('events'); 23 | 24 | /** 25 | * Type of tpkt packet 26 | * Fastpath is use to shortcut RDP stack 27 | * @see http://msdn.microsoft.com/en-us/library/cc240621.aspx 28 | * @see http://msdn.microsoft.com/en-us/library/cc240589.aspx 29 | */ 30 | var Action = { 31 | FASTPATH_ACTION_FASTPATH : 0x0, 32 | FASTPATH_ACTION_X224 : 0x3 33 | }; 34 | 35 | /** 36 | * TPKT layer of rdp stack 37 | */ 38 | function TPKT(transport) { 39 | this.transport = transport; 40 | // wait 2 bytes 41 | this.transport.expect(2); 42 | // next state is receive header 43 | var self = this; 44 | this.transport.once('data', function(s) { 45 | self.recvHeader(s); 46 | }).on('close', function() { 47 | self.emit('close'); 48 | }).on('error', function (err) { 49 | self.emit('error', err); 50 | }); 51 | } 52 | 53 | /** 54 | * inherit from a packet layer 55 | */ 56 | inherits(TPKT, events.EventEmitter); 57 | 58 | /** 59 | * Receive correct packet as expected 60 | * @param s {type.Stream} 61 | */ 62 | TPKT.prototype.recvHeader = function(s) { 63 | var version = new type.UInt8().read(s).value; 64 | var self = this; 65 | if(version === Action.FASTPATH_ACTION_X224) { 66 | new type.UInt8().read(s); 67 | 68 | this.transport.expect(2); 69 | this.transport.once('data', function(s) { 70 | self.recvExtendedHeader(s); 71 | }); 72 | } 73 | else { 74 | this.secFlag = ((version >> 6) & 0x3); 75 | var length = new type.UInt8().read(s).value; 76 | if (length & 0x80) { 77 | this.transport.expect(1); 78 | this.transport.once('data', function(s) { 79 | self.recvExtendedFastPathHeader(s, length); 80 | }); 81 | } 82 | else { 83 | this.transport.expect(length - 2); 84 | this.transport.once('data', function(s) { 85 | self.recvFastPath(s); 86 | }); 87 | } 88 | } 89 | }; 90 | 91 | /** 92 | * Receive second part of header packet 93 | * @param s {type.Stream} 94 | */ 95 | TPKT.prototype.recvExtendedHeader = function(s) { 96 | var size = new type.UInt16Be().read(s); 97 | this.transport.expect(size.value - 4); 98 | //next state receive packet 99 | var self = this; 100 | this.transport.once('data', function(s) { 101 | self.recvData(s); 102 | }); 103 | }; 104 | 105 | /** 106 | * Receive data available for presentation layer 107 | * @param s {type.Stream} 108 | */ 109 | TPKT.prototype.recvData = function(s) { 110 | this.emit('data', s); 111 | this.transport.expect(2); 112 | //next state receive header 113 | var self = this; 114 | this.transport.once('data', function(s) { 115 | self.recvHeader(s); 116 | }); 117 | }; 118 | 119 | /** 120 | * Read extended fastpath header 121 | * @param s {type.Stream} 122 | */ 123 | TPKT.prototype.recvExtendedFastPathHeader = function (s, length) { 124 | var rightPart = new type.UInt8().read(s).value; 125 | var leftPart = length & ~0x80; 126 | var packetSize = (leftPart << 8) + rightPart; 127 | 128 | var self = this; 129 | this.transport.expect(packetSize - 3); 130 | this.transport.once('data', function(s) { 131 | self.recvFastPath(s); 132 | }); 133 | }; 134 | 135 | /** 136 | * Read fast path data 137 | * @param s {type.Stream} 138 | */ 139 | TPKT.prototype.recvFastPath = function (s) { 140 | this.emit('fastPathData', this.secFlag, s); 141 | var self = this; 142 | this.transport.expect(2); 143 | this.transport.once('data', function(s) { 144 | self.recvHeader(s); 145 | }); 146 | }; 147 | 148 | /** 149 | * Send message throught TPKT layer 150 | * @param message {type.*} 151 | */ 152 | TPKT.prototype.send = function(message) { 153 | this.transport.send(new type.Component([ 154 | new type.UInt8(Action.FASTPATH_ACTION_X224), 155 | new type.UInt8(0), 156 | new type.UInt16Be(message.size() + 4), 157 | message 158 | ])); 159 | }; 160 | 161 | /** 162 | * close stack 163 | */ 164 | TPKT.prototype.close = function() { 165 | this.transport.close(); 166 | }; 167 | 168 | /** 169 | * Module exports 170 | */ 171 | module.exports = TPKT; 172 | 173 | -------------------------------------------------------------------------------- /lib/protocol/x224.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var inherits = require('util').inherits; 21 | var events = require('events'); 22 | var type = require('../core').type; 23 | var log = require('../core').log; 24 | var error = require('../core').error; 25 | 26 | /** 27 | * Message type present in X224 packet header 28 | */ 29 | var MessageType = { 30 | X224_TPDU_CONNECTION_REQUEST : 0xE0, 31 | X224_TPDU_CONNECTION_CONFIRM : 0xD0, 32 | X224_TPDU_DISCONNECT_REQUEST : 0x80, 33 | X224_TPDU_DATA : 0xF0, 34 | X224_TPDU_ERROR : 0x70 35 | }; 36 | 37 | /** 38 | * Type of negotiation present in negotiation packet 39 | */ 40 | var NegotiationType = { 41 | TYPE_RDP_NEG_REQ : 0x01, 42 | TYPE_RDP_NEG_RSP : 0x02, 43 | TYPE_RDP_NEG_FAILURE : 0x03 44 | }; 45 | 46 | /** 47 | * Protocols available for x224 layer 48 | */ 49 | var Protocols = { 50 | PROTOCOL_RDP : 0x00000000, 51 | PROTOCOL_SSL : 0x00000001, 52 | PROTOCOL_HYBRID : 0x00000002, 53 | PROTOCOL_HYBRID_EX : 0x00000008 54 | }; 55 | 56 | /** 57 | * Use to negotiate security layer of RDP stack 58 | * In node-rdpjs only ssl is available 59 | * @param opt {object} component type options 60 | * @see request -> http://msdn.microsoft.com/en-us/library/cc240500.aspx 61 | * @see response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx 62 | * @see failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx 63 | */ 64 | function negotiation(opt) { 65 | var self = { 66 | type : new type.UInt8(), 67 | flag : new type.UInt8(), 68 | length : new type.UInt16Le(0x0008, { constant : true }), 69 | result : new type.UInt32Le() 70 | }; 71 | return new type.Component(self, opt); 72 | } 73 | 74 | /** 75 | * X224 client connection request 76 | * @param opt {object} component type options 77 | * @see http://msdn.microsoft.com/en-us/library/cc240470.aspx 78 | */ 79 | function clientConnectionRequestPDU(opt, cookie) { 80 | var self = { 81 | len : new type.UInt8(function() { 82 | return new type.Component(self).size() - 1; 83 | }), 84 | code : new type.UInt8(MessageType.X224_TPDU_CONNECTION_REQUEST, { constant : true }), 85 | padding : new type.Component([new type.UInt16Le(), new type.UInt16Le(), new type.UInt8()]), 86 | cookie : cookie || new type.Factory( function (s) { 87 | var offset = 0; 88 | while (true) { 89 | var token = s.buffer.readUInt16LE(s.offset + offset); 90 | if (token === 0x0a0d) { 91 | self.cookie = new type.BinaryString(null, { readLength : new type.CallableValue(offset + 2) }).read(s); 92 | return; 93 | } 94 | else { 95 | offset += 1; 96 | } 97 | } 98 | }, { conditional : function () { 99 | return self.len.value > 14; 100 | }}), 101 | protocolNeg : negotiation({ optional : true }) 102 | }; 103 | 104 | return new type.Component(self, opt); 105 | } 106 | 107 | /** 108 | * X224 Server connection confirm 109 | * @param opt {object} component type options 110 | * @see http://msdn.microsoft.com/en-us/library/cc240506.aspx 111 | */ 112 | function serverConnectionConfirm(opt) { 113 | var self = { 114 | len : new type.UInt8(function() { 115 | return new type.Component(self).size() - 1; 116 | }), 117 | code : new type.UInt8(MessageType.X224_TPDU_CONNECTION_CONFIRM, { constant : true }), 118 | padding : new type.Component([new type.UInt16Le(), new type.UInt16Le(), new type.UInt8()]), 119 | protocolNeg : negotiation({ optional : true }) 120 | }; 121 | 122 | return new type.Component(self, opt); 123 | } 124 | 125 | /** 126 | * Header of each data message from x224 layer 127 | * @returns {type.Component} 128 | */ 129 | function x224DataHeader() { 130 | var self = { 131 | header : new type.UInt8(2), 132 | messageType : new type.UInt8(MessageType.X224_TPDU_DATA, { constant : true }), 133 | separator : new type.UInt8(0x80, { constant : true }) 134 | }; 135 | return new type.Component(self); 136 | } 137 | 138 | /** 139 | * Common X224 Automata 140 | * @param presentation {Layer} presentation layer 141 | */ 142 | function X224(transport) { 143 | this.transport = transport; 144 | this.requestedProtocol = Protocols.PROTOCOL_SSL; 145 | this.selectedProtocol = Protocols.PROTOCOL_SSL; 146 | 147 | var self = this; 148 | this.transport.on('close', function() { 149 | self.emit('close'); 150 | }).on('error', function (err) { 151 | self.emit('error', err); 152 | }); 153 | } 154 | 155 | //inherit from Layer 156 | inherits(X224, events.EventEmitter); 157 | 158 | /** 159 | * Main data received function 160 | * after connection sequence 161 | * @param s {type.Stream} stream formated from transport layer 162 | */ 163 | X224.prototype.recvData = function(s) { 164 | // check header 165 | x224DataHeader().read(s); 166 | this.emit('data', s); 167 | }; 168 | 169 | /** 170 | * Format message from x224 layer to transport layer 171 | * @param message {type} 172 | * @returns {type.Component} x224 formated message 173 | */ 174 | X224.prototype.send = function(message) { 175 | this.transport.send(new type.Component([x224DataHeader(), message])); 176 | }; 177 | 178 | /** 179 | * Client x224 automata 180 | * @param transport {events.EventEmitter} (bind data events) 181 | */ 182 | function Client(transport) { 183 | X224.call(this, transport); 184 | 185 | } 186 | 187 | //inherit from X224 automata 188 | inherits(Client, X224); 189 | 190 | /** 191 | * Client automata connect event 192 | */ 193 | Client.prototype.connect = function() { 194 | var message = clientConnectionRequestPDU(null, new type.BinaryString()); 195 | message.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_REQ; 196 | message.obj.protocolNeg.obj.result.value = this.requestedProtocol; 197 | this.transport.send(message); 198 | 199 | // next state wait connection confirm packet 200 | var self = this; 201 | this.transport.once('data', function(s) { 202 | self.recvConnectionConfirm(s); 203 | }); 204 | }; 205 | 206 | /** 207 | * close stack 208 | */ 209 | Client.prototype.close = function() { 210 | this.transport.close(); 211 | }; 212 | 213 | /** 214 | * Receive connection from server 215 | * @param s {Stream} 216 | */ 217 | Client.prototype.recvConnectionConfirm = function(s) { 218 | var message = serverConnectionConfirm().read(s); 219 | 220 | if(message.obj.protocolNeg.obj.type.value == NegotiationType.TYPE_RDP_NEG_FAILURE) { 221 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NEG_FAILURE', 222 | 'Failure code:' + message.obj.protocolNeg.obj.result.value + " (see https://msdn.microsoft.com/en-us/library/cc240507.aspx)"); 223 | } 224 | 225 | if(message.obj.protocolNeg.obj.type.value == NegotiationType.TYPE_RDP_NEG_RSP) { 226 | this.selectedProtocol = message.obj.protocolNeg.obj.result.value; 227 | } 228 | 229 | if([Protocols.PROTOCOL_HYBRID, Protocols.PROTOCOL_HYBRID_EX].indexOf(this.selectedProtocol) !== -1) { 230 | throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED'); 231 | } 232 | 233 | if(this.selectedProtocol == Protocols.PROTOCOL_RDP) { 234 | log.debug("RDP standard security selected"); 235 | return; 236 | } 237 | 238 | // finish connection sequence 239 | var self = this; 240 | this.transport.on('data', function(s) { 241 | self.recvData(s); 242 | }); 243 | 244 | if(this.selectedProtocol == Protocols.PROTOCOL_SSL) { 245 | log.debug("SSL standard security selected"); 246 | this.transport.transport.startTLS(function() { 247 | self.emit('connect', self.selectedProtocol); 248 | }); 249 | return; 250 | } 251 | }; 252 | 253 | /** 254 | * Server x224 automata 255 | */ 256 | function Server(transport, keyFilePath, crtFilePath) { 257 | X224.call(this, transport); 258 | this.keyFilePath = keyFilePath; 259 | this.crtFilePath = crtFilePath; 260 | var self = this; 261 | this.transport.once('data', function (s) { 262 | self.recvConnectionRequest(s); 263 | }); 264 | } 265 | 266 | //inherit from X224 automata 267 | inherits(Server, X224); 268 | 269 | /** 270 | * @see http://msdn.microsoft.com/en-us/library/cc240470.aspx 271 | * @param s {type.Stream} 272 | */ 273 | Server.prototype.recvConnectionRequest = function (s) { 274 | var request = clientConnectionRequestPDU().read(s); 275 | if (!request.obj.protocolNeg.isReaded) { 276 | throw new Error('NODE_RDP_PROTOCOL_X224_NO_BASIC_SECURITY_LAYER'); 277 | } 278 | 279 | this.requestedProtocol = request.obj.protocolNeg.obj.result.value; 280 | this.selectedProtocol = this.requestedProtocol & Protocols.PROTOCOL_SSL; 281 | 282 | if (!(this.selectedProtocol & Protocols.PROTOCOL_SSL)) { 283 | var confirm = serverConnectionConfirm(); 284 | confirm.obj.protocolNeg.obj.type.value = NegociationType.TYPE_RDP_NEG_FAILURE; 285 | confirm.obj.protocolNeg.obj.result.value = NegotiationFailureCode.SSL_REQUIRED_BY_SERVER; 286 | this.transport.send(confirm); 287 | this.close(); 288 | } 289 | else { 290 | this.sendConnectionConfirm(); 291 | } 292 | }; 293 | 294 | /** 295 | * Start SSL connection if needed 296 | * @see http://msdn.microsoft.com/en-us/library/cc240501.aspx 297 | */ 298 | Server.prototype.sendConnectionConfirm = function () { 299 | var confirm = serverConnectionConfirm(); 300 | confirm.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_RSP; 301 | confirm.obj.protocolNeg.obj.result.value = this.selectedProtocol; 302 | this.transport.send(confirm); 303 | 304 | // finish connection sequence 305 | var self = this; 306 | this.transport.on('data', function(s) { 307 | self.recvData(s); 308 | }); 309 | 310 | this.transport.transport.listenTLS(this.keyFilePath, this.crtFilePath, function() { 311 | log.debug('start SSL connection'); 312 | self.emit('connect', self.requestedProtocol); 313 | }); 314 | }; 315 | 316 | /** 317 | * Module exports 318 | */ 319 | module.exports = { 320 | Client : Client, 321 | Server : Server 322 | }; 323 | -------------------------------------------------------------------------------- /lib/security/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var x509 = require('./x509'); 21 | var rsa = require('./rsa'); 22 | 23 | module.exports = { 24 | x509 : x509, 25 | rsa : rsa 26 | }; 27 | -------------------------------------------------------------------------------- /lib/security/rsa.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | var BigInteger = require('./jsbn'); 21 | 22 | /** 23 | * @param modulus {Buffer} 24 | * @param pubExp {integer} 25 | */ 26 | function publicKey(modulus, pubExp) { 27 | return { 28 | n : modulus, 29 | e : pubExp 30 | } 31 | } 32 | 33 | function encrypt(data, publicKey) { 34 | return new BigInteger(data).modPowInt(publicKey.e, new BigInteger(publicKey.n)).toBuffer(); 35 | } 36 | 37 | /** 38 | * Module Export 39 | */ 40 | module.exports = { 41 | publicKey : publicKey, 42 | encrypt : encrypt 43 | }; -------------------------------------------------------------------------------- /lib/security/x509.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Sylvain Peyrefitte 3 | * 4 | * This file is part of node-rdpjs. 5 | * 6 | * node-rdpjs is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | // https://tools.ietf.org/html/rfc5280 21 | 22 | var asn1 = require('../asn1'); 23 | 24 | /** 25 | * @see https://tools.ietf.org/html/rfc5280 page 20 26 | * @returns {asn1.univ.Choice} 27 | */ 28 | function DirectoryString() { 29 | return new asn1.univ.Choice({ 30 | teletexString : new asn1.univ.T61String(), 31 | printableString : new asn1.univ.PrintableString(), 32 | universalString : new asn1.univ.UniversalString(), 33 | utf8String : new asn1.univ.UTF8String(), 34 | bmpString : new asn1.univ.BMPString(), 35 | ia5String : new asn1.univ.IA5String() 36 | }); 37 | } 38 | 39 | /** 40 | * https://tools.ietf.org/html/rfc5280 page 20 41 | * @returns {asn1.univ.Choice} 42 | */ 43 | function AttributeValue() { 44 | return DirectoryString(); 45 | } 46 | 47 | /** 48 | * @see https://tools.ietf.org/html/rfc5280 page 20 49 | * @returns {asn1.univ.ObjectIdentifier} 50 | */ 51 | function AttributeType() { 52 | return new asn1.univ.ObjectIdentifier(); 53 | } 54 | 55 | /** 56 | * @see https://tools.ietf.org/html/rfc5280 page 20 57 | * @returns {asn1.univ.Sequence} 58 | */ 59 | function AttributeTypeAndValue() { 60 | return new asn1.univ.Sequence({ 61 | type : AttributeType(), 62 | value : AttributeValue() 63 | }); 64 | } 65 | 66 | /** 67 | * https://tools.ietf.org/html/rfc5280 page 116 68 | * @returns {asn1.univ.SetOf} 69 | */ 70 | function RelativeDistinguishedName() { 71 | return new asn1.univ.SetOf(AttributeTypeAndValue); 72 | } 73 | 74 | /** 75 | * https://tools.ietf.org/html/rfc5280 page 116 76 | * @returns {asn1.univ.SequenceOf} 77 | */ 78 | function RDNSequence() { 79 | return new asn1.univ.SequenceOf(RelativeDistinguishedName); 80 | } 81 | 82 | /** 83 | * @see https://tools.ietf.org/html/rfc5280 page 116 84 | * @returns {asn1.univ.Choice} 85 | */ 86 | function Name() { 87 | return new asn1.univ.Choice({ 88 | rdnSequence : RDNSequence() 89 | }); 90 | } 91 | 92 | /** 93 | * @see https://tools.ietf.org/html/rfc5280 page 18 94 | * @returns {asn1.univ.Sequence} 95 | */ 96 | function AlgorithmIdentifier() { 97 | return new asn1.univ.Sequence({ 98 | algorithm : new asn1.univ.ObjectIdentifier(), 99 | parameters : new asn1.univ.Null() 100 | }); 101 | } 102 | 103 | /** 104 | * @see https://tools.ietf.org/html/rfc5280 page 117 105 | * @returns {asn1.univ.Sequence} 106 | */ 107 | function Extension() { 108 | return new asn1.univ.Sequence({ 109 | extnID : new asn1.univ.ObjectIdentifier(), 110 | critical : new asn1.univ.Boolean(), 111 | extnValue : new asn1.univ.OctetString() 112 | }); 113 | } 114 | 115 | /** 116 | * @see https://tools.ietf.org/html/rfc5280 page 117 117 | * @returns {asn1.univ.SequenceOf} 118 | */ 119 | function Extensions() { 120 | return new asn1.univ.SequenceOf(Extension); 121 | } 122 | 123 | /** 124 | * @see https://tools.ietf.org/html/rfc5280 page 117 125 | * @returns {asn1.univ.Choice} 126 | */ 127 | function Time() { 128 | return new asn1.univ.Choice({ 129 | utcTime : new asn1.univ.UTCTime(), 130 | generalTime : new asn1.univ.GeneralizedTime() 131 | }); 132 | } 133 | 134 | /** 135 | * @see https://tools.ietf.org/html/rfc5280 page 117 136 | * @returns {asn1.univ.Sequence} 137 | */ 138 | function Validity() { 139 | return new asn1.univ.Sequence({ 140 | notBefore : Time(), 141 | notAfter : Time() 142 | }); 143 | } 144 | 145 | /** 146 | * @see https://tools.ietf.org/html/rfc5280 page 117 147 | * @returns {asn1.univ.Integer} 148 | */ 149 | function CertificateSerialNumber() { 150 | return new asn1.univ.Integer(); 151 | } 152 | 153 | /** 154 | * @see https://tools.ietf.org/html/rfc5280 page 117 155 | * @returns {asn1.univ.Sequence} 156 | */ 157 | function SubjectPublicKeyInfo() { 158 | return new asn1.univ.Sequence({ 159 | algorithm : AlgorithmIdentifier(), 160 | subjectPublicKey : new asn1.univ.BitString() 161 | }); 162 | } 163 | 164 | /** 165 | * @see https://tools.ietf.org/html/rfc5280 page 117 166 | * @returns {asn1.univ.BitString} 167 | */ 168 | function UniqueIdentifier() { 169 | return new asn1.univ.BitString(); 170 | } 171 | 172 | /** 173 | * @see https://tools.ietf.org/html/rfc5280 page 117 174 | * @returns {asn1.univ.Sequence} 175 | */ 176 | function TbsCertificate() { 177 | return new asn1.univ.Sequence({ 178 | version : CertificateSerialNumber().explicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Constructed, 0)), 179 | serialNumber : new asn1.univ.Integer(), 180 | signature : AlgorithmIdentifier(), 181 | issuer : Name(), 182 | validity : Validity(), 183 | subject : Name(), 184 | subjectPublicKeyInfo : SubjectPublicKeyInfo(), 185 | issuerUniqueID : UniqueIdentifier().implicitTag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Primitive, 1).optional(), 186 | subjectUniqueID : UniqueIdentifier().implicitTag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Primitive, 2).optional(), 187 | extensions : Extensions().implicitTag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Primitive, 3).optional() 188 | }); 189 | } 190 | 191 | /** 192 | * @see https://tools.ietf.org/html/rfc5280 page 117 193 | * @returns {asn1.univ.Sequence} 194 | */ 195 | function X509Certificate() { 196 | return new asn1.univ.Sequence({ 197 | tbsCertificate : TbsCertificate(), 198 | signatureAlgorithm : AlgorithmIdentifier(), 199 | signatureValue : new asn1.univ.BitString() 200 | }); 201 | } 202 | 203 | function RSAPublicKey() { 204 | return new asn1.univ.Sequence({ 205 | modulus : new asn1.univ.Integer(), 206 | publicExponent : new asn1.univ.Integer() 207 | }); 208 | } 209 | 210 | /** 211 | * Module Export 212 | */ 213 | module.exports = { 214 | X509Certificate : X509Certificate, 215 | RSAPublicKey : RSAPublicKey 216 | }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-rdpjs", 3 | "version": "0.3.3", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.2", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 10 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 11 | "optional": true 12 | }, 13 | "brace-expansion": { 14 | "version": "1.1.11", 15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 16 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 17 | "optional": true, 18 | "requires": { 19 | "balanced-match": "^1.0.0", 20 | "concat-map": "0.0.1" 21 | } 22 | }, 23 | "bunyan": { 24 | "version": "1.8.15", 25 | "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", 26 | "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", 27 | "requires": { 28 | "dtrace-provider": "~0.8", 29 | "moment": "^2.19.3", 30 | "mv": "~2", 31 | "safe-json-stringify": "~1" 32 | } 33 | }, 34 | "concat-map": { 35 | "version": "0.0.1", 36 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 37 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 38 | "optional": true 39 | }, 40 | "dtrace-provider": { 41 | "version": "0.8.8", 42 | "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", 43 | "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", 44 | "optional": true, 45 | "requires": { 46 | "nan": "^2.14.0" 47 | } 48 | }, 49 | "glob": { 50 | "version": "6.0.4", 51 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 52 | "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", 53 | "optional": true, 54 | "requires": { 55 | "inflight": "^1.0.4", 56 | "inherits": "2", 57 | "minimatch": "2 || 3", 58 | "once": "^1.3.0", 59 | "path-is-absolute": "^1.0.0" 60 | } 61 | }, 62 | "inflight": { 63 | "version": "1.0.6", 64 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 65 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 66 | "optional": true, 67 | "requires": { 68 | "once": "^1.3.0", 69 | "wrappy": "1" 70 | } 71 | }, 72 | "inherits": { 73 | "version": "2.0.4", 74 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 75 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 76 | "optional": true 77 | }, 78 | "lodash.isnumber": { 79 | "version": "3.0.3", 80 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 81 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 82 | }, 83 | "minimatch": { 84 | "version": "3.1.2", 85 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 86 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 87 | "optional": true, 88 | "requires": { 89 | "brace-expansion": "^1.1.7" 90 | } 91 | }, 92 | "minimist": { 93 | "version": "1.2.7", 94 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", 95 | "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", 96 | "optional": true 97 | }, 98 | "mkdirp": { 99 | "version": "0.5.6", 100 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 101 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 102 | "optional": true, 103 | "requires": { 104 | "minimist": "^1.2.6" 105 | } 106 | }, 107 | "moment": { 108 | "version": "2.29.4", 109 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", 110 | "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", 111 | "optional": true 112 | }, 113 | "mv": { 114 | "version": "2.1.1", 115 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 116 | "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", 117 | "optional": true, 118 | "requires": { 119 | "mkdirp": "~0.5.1", 120 | "ncp": "~2.0.0", 121 | "rimraf": "~2.4.0" 122 | } 123 | }, 124 | "nan": { 125 | "version": "2.17.0", 126 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", 127 | "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", 128 | "optional": true 129 | }, 130 | "ncp": { 131 | "version": "2.0.0", 132 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 133 | "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", 134 | "optional": true 135 | }, 136 | "once": { 137 | "version": "1.4.0", 138 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 139 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 140 | "optional": true, 141 | "requires": { 142 | "wrappy": "1" 143 | } 144 | }, 145 | "path-is-absolute": { 146 | "version": "1.0.1", 147 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 148 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 149 | "optional": true 150 | }, 151 | "rimraf": { 152 | "version": "2.4.5", 153 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 154 | "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", 155 | "optional": true, 156 | "requires": { 157 | "glob": "^6.0.1" 158 | } 159 | }, 160 | "safe-json-stringify": { 161 | "version": "1.2.0", 162 | "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", 163 | "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", 164 | "optional": true 165 | }, 166 | "wrappy": { 167 | "version": "1.0.2", 168 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 169 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 170 | "optional": true 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-rdpjs", 3 | "author": "Sylvain Peyrefitte", 4 | "version": "0.3.3", 5 | "engines": { 6 | "node": ">=8.5.0" 7 | }, 8 | "description": "Remote Desktop Protocol in Node.js", 9 | "license": "AGPL-3.0", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/citronneur/node-rdpjs" 13 | }, 14 | "main": "lib/index.js", 15 | "dependencies": { 16 | "bunyan": "^1.8.15", 17 | "lodash.isnumber": "^3.0.3" 18 | } 19 | } 20 | --------------------------------------------------------------------------------