EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
type.js
Go to the documentation of this file.
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 <http://www.gnu.org/licenses/>.
18 */
19
20var inherits = require('util').inherits;
21var log = require('./log');
22var error = require('./error');
23
24/**
25 * Stream wrapper around buffer type
26 * @param i {Buffer | integer} size of init buffer
27 * @returns
28 */
29function Stream(i) {
30 this.offset = 0;
31 if (i instanceof Buffer) {
32 this.buffer = i;
33 }
34 else {
35 this.buffer = Buffer.alloc(i || 8192);
36 }
37}
38
39/**
40 * Return length of available data in stream
41 * @returns {Number} length of available data in stream
42 */
43Stream.prototype.availableLength = function() {
44 return this.buffer.length - this.offset;
45};
46
47/**
48 * increment offset
49 * @param length {integer} length of padding
50 */
51Stream.prototype.readPadding = function(length) {
52 this.offset += length;
53};
54
55/**
56 * Format string buffer
57 * @returns {string} buffer stringified
58 */
59Stream.prototype.getValue = function() {
60 return this.buffer;
61};
62
63/**
64 * @param value {object | function} inner value
65 * @returns
66 */
67function CallableValue(value) {
68 if(value) {
69 this.value = value;
70 }
71}
72
73/**
74 * For syntaxic purpose
75 */
76Object.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 */
94function Type(opt) {
95 CallableValue.call(this);
96 this.opt = opt || {};
97 this.isReaded = false;
98 this.isWritten = false;
99}
100
101inherits(Type, CallableValue);
102
103/**
104 * Write type into binary stream s
105 * @param {type.Stream} s binary stream
106 */
107Type.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 */
123Type.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, OLD:" + oldValue + ", NEW:" + this.value);
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 */
170Type.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 */
181Type.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 */
192function Component(obj, opt) {
193 Type.call(this, opt);
194 this.obj = obj;
195}
196
197//inherit from type
198inherits(Component, Type);
199
200/**
201 * ignore criterion
202 * @param i {string} index name in obj
203 * @returns {Boolean} true if can be ignore
204 */
205Component.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 */
221Component.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 */
240Component.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 */
272Component.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 */
290function 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
299inherits(SingleType, Type);
300
301/**
302 * Write SingleType value into stream
303 * @param s
304 */
305SingleType.prototype.writeValue = function(s) {
306 var safeValue = this.value;
307 if (typeof safeValue === 'number') { // FIX: Sanitize coordinates to prevent crashes
308 safeValue = Math.round(safeValue); // Round to nearest integer (fixes -1.01 issues)
309 if (safeValue < 0) safeValue = 0; // Clamp to 0 if negative
310 // nbBytes is 1 (UInt8), 2 (UInt16), or 4 (UInt32)
311 var max = Math.pow(2, this.nbBytes * 8) - 1;
312 if (safeValue > max) safeValue = max; // Clamp to max value allowed by the buffer size (prevents overflow crashes)
313 }
314 this.writeBufferCallback.call(s.buffer, safeValue, s.offset);
315 s.offset += this._size_();
316};
317
318/**
319 * Read SingleType value into stream
320 * @param {Stream} s from read stream
321 */
322SingleType.prototype.readValue = function(s) {
323 this.value = this.readBufferCallback.call(s.buffer, s.offset);
324 s.offset += this._size_();
325};
326
327/**
328 * Size of single type
329 * @returns Size of single type
330 */
331SingleType.prototype._size_ = function() {
332 return this.nbBytes;
333};
334
335/**
336 * Integer on 1 byte
337 * @param {number | function} value of type
338 * @param {object} opt Type parameter
339 * @returns
340 */
341function UInt8(value, opt) {
342 SingleType.call(this, value, 1, Buffer.prototype.readUInt8, Buffer.prototype.writeUInt8, opt);
343}
344
345//inherit from type
346inherits(UInt8, SingleType);
347
348/**
349 * Integer on 2 bytes in Little Endian
350 * @param {number | function} value to write or compare if constant
351 * @param {object} opt Type parameter
352 * @returns
353 */
354function UInt16Le(value, opt) {
355 SingleType.call(this, value, 2, Buffer.prototype.readUInt16LE, Buffer.prototype.writeUInt16LE, opt);
356}
357
358//inherit from type
359inherits(UInt16Le, SingleType);
360
361/**
362 * Integer on 2 bytes in Big Endian
363 * @param {number | function} value to write or compare if constant
364 * @param {object} opt Type parameter
365 * @returns
366 */
367function UInt16Be(value, opt) {
368 SingleType.call(this, value, 2, Buffer.prototype.readUInt16BE, Buffer.prototype.writeUInt16BE, opt);
369}
370
371//inherit from type
372inherits(UInt16Be, SingleType);
373
374/**
375 * Integer on 4 bytes in Little Endian
376 * @param {number | function} value to write or compare if constant
377 * @param {object} opt Type parameter
378 * @returns
379 */
380function UInt32Le(value, opt) {
381 SingleType.call(this, value, 4, Buffer.prototype.readUInt32LE, Buffer.prototype.writeUInt32LE, opt);
382}
383
384//inherit from type
385inherits(UInt32Le, SingleType);
386
387/**
388 * Integer on 4 bytes in Big Endian
389 * @param {number | function} value to write or compare if constant
390 * @param {object} opt Type parameter
391 * @returns
392 */
393function UInt32Be(value, opt) {
394 SingleType.call(this, value, 4, Buffer.prototype.readUInt32BE, Buffer.prototype.writeUInt32BE, opt);
395}
396
397//inherit from type
398inherits(UInt32Be, SingleType);
399
400/**
401 * @param value {Buffer} javascript source string
402 * @param opt {object} type options
403 * .readLength {type} length for reading operation
404 * @returns {type.BinaryString}
405 */
406function BinaryString(value, opt) {
407 Type.call(this, opt);
408 this.value = value || Buffer.alloc(0);
409}
410
411//inherit from type
412inherits(BinaryString, Type);
413
414/**
415 * Write value into string
416 * @param s {type.Stream}
417 */
418BinaryString.prototype.writeValue = function(s) {
419 this.value.copy(s.buffer, s.offset);
420 s.offset += this._size_();
421};
422
423/**
424 * Read string from offset to read length if specified or end of stream
425 * @param s {type.Stream}
426 */
427BinaryString.prototype.readValue = function(s) {
428 if(this.opt.readLength) {
429 this.value = s.buffer.slice(s.offset, s.offset + this.opt.readLength.value);
430 }
431 else {
432 this.value = s.buffer.slice(s.offset);
433 }
434 s.offset += this._size_();
435};
436
437/**
438 * @returns {integer} length of string
439 */
440BinaryString.prototype._size_ = function() {
441 return this.value.length;
442};
443
444/**
445 * Dynamic built type depend on factory function
446 * @param message {object} parent object
447 * @param field {string} name of object field
448 * @param factory {function} factory use to built new type
449 * @param opt {object} type options
450 */
451function Factory(factory, opt) {
452 Type.call(this, opt);
453 this.factory = factory;
454}
455
456//inherit from type
457inherits(Factory, Type);
458
459/**
460 * build type and write into stream
461 * @param s {Stream} input stream
462 */
463Factory.prototype.writeValue = function(s) {
464 this.factory(s);
465};
466
467/**
468 * build type and read from stream
469 * @param s {Stream} input stream
470 */
471Factory.prototype.readValue = function(s) {
472 this.factory(s);
473};
474
475/**
476 * must be never called
477 */
478Factory.prototype._size_ = function() {
479 throw new error.FatalError('NODE_RDP_CORE_TYPE_FACTORY_TYPE_HAVE_NO_SIZE');
480};
481
482/**
483 * Module exports
484 */
485module.exports = {
486 Stream : Stream,
487 Component : Component,
488 UInt8 : UInt8,
489 UInt16Le : UInt16Le,
490 UInt16Be : UInt16Be,
491 UInt32Le : UInt32Le,
492 UInt32Be : UInt32Be,
493 BinaryString : BinaryString,
494 CallableValue : CallableValue,
495 Factory : Factory
496};