| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /*
- * Copyright (c) 2014-2015 Sylvain Peyrefitte
- *
- * This file is part of node-rdpjs.
- *
- * node-rdpjs is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- var inherits = require('util').inherits;
- var events = require('events');
- var type = require('../../core').type;
- var log = require('../../core').log;
- var error = require('../../core').error;
- var gcc = require('./gcc');
- var per = require('./per');
- var asn1 = require('../../asn1');
- var cliprdr = require('../pdu/cliprdr');
- var Message = {
- MCS_TYPE_CONNECT_INITIAL : 0x65,
- MCS_TYPE_CONNECT_RESPONSE : 0x66
- };
- var DomainMCSPDU = {
- ERECT_DOMAIN_REQUEST : 1,
- DISCONNECT_PROVIDER_ULTIMATUM : 8,
- ATTACH_USER_REQUEST : 10,
- ATTACH_USER_CONFIRM : 11,
- CHANNEL_JOIN_REQUEST : 14,
- CHANNEL_JOIN_CONFIRM : 15,
- SEND_DATA_REQUEST : 25,
- SEND_DATA_INDICATION : 26
- };
- var Channel = {
- MCS_GLOBAL_CHANNEL: 1003,
- MCS_USERCHANNEL_BASE: 1001,
- MCS_CLIPRDR_CHANNEL: 1005
- };
- /**
- * Channel Defined
- */
- const RdpdrChannelDef = new type.Component({
- name: new type.BinaryString(Buffer.from('rdpdr' + '\x00\x00\x00', 'binary'), { readLength: new type.CallableValue(8) }),
- options: new type.UInt32Le(0x80800000)
- });
- const RdpsndChannelDef = new type.Component({
- name: new type.BinaryString(Buffer.from('rdpsnd' + '\x00\x00', 'binary'), { readLength: new type.CallableValue(8) }),
- options: new type.UInt32Le(0xc0000000)
- });
- const CliprdrChannelDef = new type.Component({
- name: new type.BinaryString(Buffer.from('cliprdr' + '\x00', 'binary'), { readLength: new type.CallableValue(8) }),
- // CHANNEL_OPTION_INITIALIZED |
- // CHANNEL_OPTION_ENCRYPT_RDP |
- // CHANNEL_OPTION_COMPRESS_RDP |
- // CHANNEL_OPTION_SHOW_PROTOCOL
- options: new type.UInt32Le(0xc0a00000)
- });
- /**
- * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
- * @returns {asn1.univ.Sequence}
- */
- function DomainParameters(maxChannelIds, maxUserIds, maxTokenIds,
- numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion) {
- return new asn1.univ.Sequence({
- maxChannelIds : new asn1.univ.Integer(maxChannelIds),
- maxUserIds : new asn1.univ.Integer(maxUserIds),
- maxTokenIds : new asn1.univ.Integer(maxTokenIds),
- numPriorities : new asn1.univ.Integer(numPriorities),
- minThoughput : new asn1.univ.Integer(minThoughput),
- maxHeight : new asn1.univ.Integer(maxHeight),
- maxMCSPDUsize : new asn1.univ.Integer(maxMCSPDUsize),
- protocolVersion : new asn1.univ.Integer(protocolVersion)
- });
- }
- /**
- * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
- * @param userData {Buffer}
- * @returns {asn1.univ.Sequence}
- */
- function ConnectInitial (userData) {
- return new asn1.univ.Sequence({
- callingDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
- calledDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
- upwardFlag : new asn1.univ.Boolean(true),
- targetParameters : DomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2),
- minimumParameters : DomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2),
- maximumParameters : DomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2),
- userData : new asn1.univ.OctetString(userData)
- }).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 101));
- }
- /**
- * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
- * @returns {asn1.univ.Sequence}
- */
- function ConnectResponse (userData) {
- return new asn1.univ.Sequence({
- result : new asn1.univ.Enumerate(0),
- calledConnectId : new asn1.univ.Integer(0),
- domainParameters : DomainParameters(22, 3, 0, 1, 0, 1,0xfff8, 2),
- userData : new asn1.univ.OctetString(userData)
- }).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 102));
- }
- /**
- * Format MCS PDU header packet
- * @param mcsPdu {integer}
- * @param options {integer}
- * @returns {type.UInt8} headers
- */
- function writeMCSPDUHeader(mcsPdu, options) {
- options = options || 0;
- return new type.UInt8((mcsPdu << 2) | options);
- }
- /**
- * Read MCS PDU header
- * @param opcode
- * @param mcsPdu
- * @returns {Boolean}
- */
- function readMCSPDUHeader(opcode, mcsPdu) {
- return (opcode >> 2) === mcsPdu;
- }
- /**
- * Multi-Channel Services
- * @param transport {events.EventEmitter} transport layer listen (connect, data) events
- * @param recvOpCode {DomainMCSPDU} opcode use in receive automata
- * @param sendOpCode {DomainMCSPDU} opcode use to send message
- */
- function MCS(transport, recvOpCode, sendOpCode) {
- this.transport = transport;
- this.recvOpCode = recvOpCode;
- this.sendOpCode = sendOpCode;
- this.channels = [
- { id: Channel.MCS_GLOBAL_CHANNEL, name: 'global' },
- { id: Channel.MCS_CLIPRDR_CHANNEL, name: 'cliprdr' }
- ];
- this.channels.find = function(callback) {
- for(var i in this) {
- if(callback(this[i])) return this[i];
- };
- };
-
- // bind events
- var self = this;
- this.transport.on('close', function () {
- self.emit('close');
- }).on('error', function (err) {
- self.emit('error', err);
- });
- }
- //inherit from Layer
- inherits(MCS, events.EventEmitter);
- /**
- * Send message to a specific channel
- * @param channelName {string} name of channel
- * @param data {type.*} message to send
- */
- MCS.prototype.send = function(channelName, data) {
- var channelId = this.channels.find(function(element) {
- if (element.name === channelName) return true;
- }).id;
-
- this.transport.send(new type.Component([
- writeMCSPDUHeader(this.sendOpCode),
- per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
- per.writeInteger16(channelId),
- new type.UInt8(0x70),
- per.writeLength(data.size()),
- data
- ]));
- };
- /**
- * Main receive function
- * @param s {type.Stream}
- */
- MCS.prototype.recv = function(s) {
- opcode = new type.UInt8().read(s).value;
-
- if (readMCSPDUHeader(opcode, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM)) {
- log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM");
- this.transport.close();
- return
- }
- else if(!readMCSPDUHeader(opcode, this.recvOpCode)) {
- throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_RECEIVE_OPCODE');
- }
- per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
-
- var channelId = per.readInteger16(s);
-
- per.readEnumerates(s);
- per.readLength(s);
-
- var channelName = this.channels.find(function(e) {
- if (e.id === channelId) return true;
- }).name;
-
- this.emit(channelName, s);
- };
- /**
- * Only main channels handle actually
- * @param transport {event.EventEmitter} bind connect and data events
- * @returns
- */
- function Client(transport) {
- MCS.call(this, transport, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST);
-
- // channel context automata
- this.channelsConnected = 0;
-
- // init gcc information
- this.clientCoreData = gcc.clientCoreData();
- // cliprdr channel
- this.clientNetworkData = gcc.clientNetworkData(new type.Component([RdpdrChannelDef, CliprdrChannelDef, RdpsndChannelDef]));
- this.clientSecurityData = gcc.clientSecurityData();
-
- // must be read from protocol
- this.serverCoreData = null;
- this.serverSecurityData = null;
- this.serverNetworkData = null;
-
- var self = this;
- this.transport.on('connect', function(s) {
- self.connect(s);
- });
- }
- inherits(Client, MCS);
- /**
- * Connect event layer
- */
- Client.prototype.connect = function(selectedProtocol) {
- this.clientCoreData.obj.serverSelectedProtocol.value = selectedProtocol;
- this.sendConnectInitial();
- };
- /**
- * close stack
- */
- Client.prototype.close = function() {
- this.transport.close();
- };
- /**
- * MCS connect response (server->client)
- * @param s {type.Stream}
- */
- Client.prototype.recvConnectResponse = function(s) {
- var userData = new type.Stream(ConnectResponse().decode(s, asn1.ber).value.userData.value);
- var serverSettings = gcc.readConferenceCreateResponse(userData);
- // record server gcc block
- for(var i in serverSettings) {
- if(!serverSettings[i].obj) {
- continue;
- }
- switch(serverSettings[i].obj.__TYPE__) {
- case gcc.MessageType.SC_CORE:
- this.serverCoreData = serverSettings[i];
- break;
- case gcc.MessageType.SC_SECURITY:
- this.serverSecurityData = serverSettings[i];
- break;
- case gcc.MessageType.SC_NET:
- this.serverNetworkData = serverSettings[i];
- break;
- default:
- log.warn('unhandle server gcc block : ' + serverSettings[i].obj.__TYPE__);
- }
- }
-
- // send domain request
- this.sendErectDomainRequest();
- // send attach user request
- this.sendAttachUserRequest();
- // now wait user confirm from server
- var self = this;
- this.transport.once('data', function(s) {
- self.recvAttachUserConfirm(s);
- });
- };
- /**
- * MCS connection automata step
- * @param s {type.Stream}
- */
- Client.prototype.recvAttachUserConfirm = function(s) {
- if (!readMCSPDUHeader(new type.UInt8().read(s).value, DomainMCSPDU.ATTACH_USER_CONFIRM)) {
- throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER');
- }
-
- if (per.readEnumerates(s) !== 0) {
- throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER');
- }
-
- this.userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
- //ask channel for specific user
- this.channels.push({ id : this.userId, name : 'user' });
- // channel connect automata
- this.connectChannels();
- };
- /**
- * Last state in channel connection automata
- * @param s {type.Stream}
- */
- Client.prototype.recvChannelJoinConfirm = function(s) {
- var opcode = new type.UInt8().read(s).value;
-
- if (!readMCSPDUHeader(opcode, DomainMCSPDU.CHANNEL_JOIN_CONFIRM)) {
- throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM');
- }
-
- var confirm = per.readEnumerates(s);
-
- var userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
- if (this.userId !== userId) {
- throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID');
- }
-
- var channelId = per.readInteger16(s);
- //if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
- if ((confirm !== 0) && (channelId === Channel.MCS_CLIPRDR_CHANNEL || channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
- throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
- }
-
- this.connectChannels();
- };
- /**
- * First MCS message
- */
- Client.prototype.sendConnectInitial = function() {
-
- var ccReq = gcc.writeConferenceCreateRequest(new type.Component([
- gcc.block(this.clientCoreData),
- gcc.block(this.clientNetworkData),
- gcc.block(this.clientSecurityData)
- ])).toStream().getValue();
-
- this.transport.send(ConnectInitial(ccReq).encode(asn1.ber));
-
- // next event is connect response
- var self = this;
- this.transport.once('data', function(s) {
- self.recvConnectResponse(s);
- });
- };
- /**
- * MCS connection automata step
- */
- Client.prototype.sendErectDomainRequest = function() {
- this.transport.send(new type.Component([
- writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST),
- per.writeInteger(0),
- per.writeInteger(0)
- ]));
- };
- /**
- * MCS connection automata step
- */
- Client.prototype.sendAttachUserRequest = function() {
- this.transport.send(writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST));
- };
- /**
- * Send a channel join request
- * @param channelId {integer} channel id
- */
- Client.prototype.sendChannelJoinRequest = function(channelId) {
- this.transport.send(new type.Component([
- writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST),
- per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
- per.writeInteger16(channelId)
- ]));
- };
- /**
- * Connect channels automata
- * @param s {type.Stream}
- */
- Client.prototype.connectChannels = function(s) {
- if(this.channelsConnected == this.channels.length) {
- var self = this;
- this.transport.on('data', function(s) {
- self.recv(s);
- });
-
- // send client and sever gcc information
- this.emit('connect',
- {
- core : this.clientCoreData.obj,
- security : this.clientSecurityData.obj,
- net : this.clientNetworkData.obj
- },
- {
- core : this.serverCoreData.obj,
- security : this.serverSecurityData.obj
- }, this.userId, this.channels);
- return;
- }
-
- this.sendChannelJoinRequest(this.channels[this.channelsConnected++].id);
-
- var self = this;
- this.transport.once('data', function(s) {
- self.recvChannelJoinConfirm(s);
- });
- };
- /**
- * Server side of MCS layer
- * @param transport
- */
- function Server (transport) {
- MCS.call(this, transport, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION);
-
- // must be readed from protocol
- this.clientCoreData = null;
- this.clientNetworkData = null;
- this.clientSecurityData = null;
-
- // init gcc information
- this.serverCoreData = gcc.serverCoreData();
- this.serverSecurityData = gcc.serverSecurityData();
- this.serverNetworkData = gcc.serverNetworkData(new type.Component([]));
-
- var self = this;
- this.transport.on('connect', function (selectedProtocol) {
- self.serverCoreData.obj.clientRequestedProtocol.value = selectedProtocol;
- }).once('data', function (s) {
- self.recvConnectInitial(s);
- });
- }
- inherits(Server, MCS);
- /**
- * First state of server automata
- * @param s {type.Stream}
- */
- Server.prototype.recvConnectInitial = function (s) {
- var userData = new type.Stream(ConnectInitial().decode(s, asn1.ber).value.userData.value);
- var clientSettings = gcc.readConferenceCreateRequest(userData);
- // record server gcc block
- for(var i in clientSettings) {
- if(!clientSettings[i].obj) {
- continue;
- }
- switch(clientSettings[i].obj.__TYPE__) {
- case gcc.MessageType.CS_CORE:
- this.clientCoreData = clientSettings[i];
- break;
- case gcc.MessageType.CS_SECURITY:
- this.clientSecurityData = clientSettings[i];
- break;
- case gcc.MessageType.CS_NET:
- this.clientNetworkData = clientSettings[i];
- for (var i = 0; i < this.clientNetworkData.obj.channelCount.value; i++) {
- this.serverNetworkData.obj.channelIdArray.obj.push(new type.UInt16Le( i + 1 + Channel.MCS_GLOBAL_CHANNEL));
- }
- break;
- default:
- log.debug('unhandle client gcc block : ' + clientSettings[i].obj.__TYPE__);
- }
- }
-
- this.sendConnectResponse();
- };
- /**
- * State 2 in mcs server connection automata
- */
- Server.prototype.sendConnectResponse = function () {
- var ccReq = gcc.writeConferenceCreateResponse(new type.Component([
- gcc.block(this.serverCoreData),
- gcc.block(this.serverSecurityData),
- gcc.block(this.serverNetworkData),
- ])).toStream().getValue();
-
- this.transport.send(ConnectResponse(ccReq).encode(asn1.ber));
-
- };
- /**
- * Module exports
- */
- module.exports = {
- Client : Client,
- Server : Server
- };
|