@homebridge/dbus-native 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +124 -0
- package/bin/dbus2js.js +118 -0
- package/index.js +155 -0
- package/lib/address-x11.js +38 -0
- package/lib/align.js +12 -0
- package/lib/bus.js +384 -0
- package/lib/constants.js +54 -0
- package/lib/dbus-buffer.js +182 -0
- package/lib/handshake.js +137 -0
- package/lib/header-signature.json +20 -0
- package/lib/hello-message.js +16 -0
- package/lib/introspect.js +201 -0
- package/lib/marshall.js +108 -0
- package/lib/marshallers.js +336 -0
- package/lib/message.js +118 -0
- package/lib/portforward.js +38 -0
- package/lib/readline.js +23 -0
- package/lib/server-handshake.js +37 -0
- package/lib/server.js +18 -0
- package/lib/signature.js +61 -0
- package/lib/stdifaces.js +203 -0
- package/lib/unmarshall.js +9 -0
- package/package.json +90 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
2
|
+
const align = require('./align').align;
|
|
3
|
+
const parseSignature = require('../lib/signature');
|
|
4
|
+
const Long = require('long');
|
|
5
|
+
/**
|
|
6
|
+
* MakeSimpleMarshaller
|
|
7
|
+
* @param signature - the signature of the data you want to check
|
|
8
|
+
* @returns a simple marshaller with the "check" method
|
|
9
|
+
*
|
|
10
|
+
* check returns nothing - it only raises errors if the data is
|
|
11
|
+
* invalid for the signature
|
|
12
|
+
*/
|
|
13
|
+
var MakeSimpleMarshaller = function(signature) {
|
|
14
|
+
var marshaller = {};
|
|
15
|
+
function checkValidString(data) {
|
|
16
|
+
if (typeof data !== 'string') {
|
|
17
|
+
throw new Error(`Data: ${data} was not of type string`);
|
|
18
|
+
} else if (data.indexOf('\0') !== -1) {
|
|
19
|
+
throw new Error('String contains null byte');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function checkValidSignature(data) {
|
|
24
|
+
if (data.length > 0xff) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Data: ${data} is too long for signature type (${data.length} > 255)`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var parenCount = 0;
|
|
31
|
+
for (var ii = 0; ii < data.length; ++ii) {
|
|
32
|
+
if (parenCount > 32) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Maximum container type nesting exceeded in signature type:${data}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
switch (data[ii]) {
|
|
38
|
+
case '(':
|
|
39
|
+
++parenCount;
|
|
40
|
+
break;
|
|
41
|
+
case ')':
|
|
42
|
+
--parenCount;
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
/* no-op */
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
parseSignature(data);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
switch (signature) {
|
|
53
|
+
case 'o':
|
|
54
|
+
// object path
|
|
55
|
+
// TODO: verify object path here?
|
|
56
|
+
case 's': // eslint-disable-line no-fallthrough
|
|
57
|
+
//STRING
|
|
58
|
+
marshaller.check = function(data) {
|
|
59
|
+
checkValidString(data);
|
|
60
|
+
};
|
|
61
|
+
marshaller.marshall = function(ps, data) {
|
|
62
|
+
this.check(data);
|
|
63
|
+
// utf8 string
|
|
64
|
+
align(ps, 4);
|
|
65
|
+
const buff = Buffer.from(data, 'utf8');
|
|
66
|
+
ps
|
|
67
|
+
.word32le(buff.length)
|
|
68
|
+
.put(buff)
|
|
69
|
+
.word8(0);
|
|
70
|
+
ps._offset += 5 + buff.length;
|
|
71
|
+
};
|
|
72
|
+
break;
|
|
73
|
+
case 'g':
|
|
74
|
+
//SIGNATURE
|
|
75
|
+
marshaller.check = function(data) {
|
|
76
|
+
checkValidString(data);
|
|
77
|
+
checkValidSignature(data);
|
|
78
|
+
};
|
|
79
|
+
marshaller.marshall = function(ps, data) {
|
|
80
|
+
this.check(data);
|
|
81
|
+
// signature
|
|
82
|
+
const buff = Buffer.from(data, 'ascii');
|
|
83
|
+
ps
|
|
84
|
+
.word8(data.length)
|
|
85
|
+
.put(buff)
|
|
86
|
+
.word8(0);
|
|
87
|
+
ps._offset += 2 + buff.length;
|
|
88
|
+
};
|
|
89
|
+
break;
|
|
90
|
+
case 'y':
|
|
91
|
+
//BYTE
|
|
92
|
+
marshaller.check = function(data) {
|
|
93
|
+
checkInteger(data);
|
|
94
|
+
checkRange(0x00, 0xff, data);
|
|
95
|
+
};
|
|
96
|
+
marshaller.marshall = function(ps, data) {
|
|
97
|
+
this.check(data);
|
|
98
|
+
ps.word8(data);
|
|
99
|
+
ps._offset++;
|
|
100
|
+
};
|
|
101
|
+
break;
|
|
102
|
+
case 'b':
|
|
103
|
+
//BOOLEAN
|
|
104
|
+
marshaller.check = function(data) {
|
|
105
|
+
checkBoolean(data);
|
|
106
|
+
};
|
|
107
|
+
marshaller.marshall = function(ps, data) {
|
|
108
|
+
this.check(data);
|
|
109
|
+
// booleans serialised as 0/1 unsigned 32 bit int
|
|
110
|
+
data = data ? 1 : 0;
|
|
111
|
+
align(ps, 4);
|
|
112
|
+
ps.word32le(data);
|
|
113
|
+
ps._offset += 4;
|
|
114
|
+
};
|
|
115
|
+
break;
|
|
116
|
+
case 'n':
|
|
117
|
+
//INT16
|
|
118
|
+
marshaller.check = function(data) {
|
|
119
|
+
checkInteger(data);
|
|
120
|
+
checkRange(-0x7fff - 1, 0x7fff, data);
|
|
121
|
+
};
|
|
122
|
+
marshaller.marshall = function(ps, data) {
|
|
123
|
+
this.check(data);
|
|
124
|
+
align(ps, 2);
|
|
125
|
+
const buff = Buffer.alloc(2);
|
|
126
|
+
buff.writeInt16LE(parseInt(data), 0);
|
|
127
|
+
ps.put(buff);
|
|
128
|
+
ps._offset += 2;
|
|
129
|
+
};
|
|
130
|
+
break;
|
|
131
|
+
case 'q':
|
|
132
|
+
//UINT16
|
|
133
|
+
marshaller.check = function(data) {
|
|
134
|
+
checkInteger(data);
|
|
135
|
+
checkRange(0, 0xffff, data);
|
|
136
|
+
};
|
|
137
|
+
marshaller.marshall = function(ps, data) {
|
|
138
|
+
this.check(data);
|
|
139
|
+
align(ps, 2);
|
|
140
|
+
ps.word16le(data);
|
|
141
|
+
ps._offset += 2;
|
|
142
|
+
};
|
|
143
|
+
break;
|
|
144
|
+
case 'i':
|
|
145
|
+
//INT32
|
|
146
|
+
marshaller.check = function(data) {
|
|
147
|
+
checkInteger(data);
|
|
148
|
+
checkRange(-0x7fffffff - 1, 0x7fffffff, data);
|
|
149
|
+
};
|
|
150
|
+
marshaller.marshall = function(ps, data) {
|
|
151
|
+
this.check(data);
|
|
152
|
+
align(ps, 4);
|
|
153
|
+
const buff = Buffer.alloc(4);
|
|
154
|
+
buff.writeInt32LE(parseInt(data), 0);
|
|
155
|
+
ps.put(buff);
|
|
156
|
+
ps._offset += 4;
|
|
157
|
+
};
|
|
158
|
+
break;
|
|
159
|
+
case 'u':
|
|
160
|
+
//UINT32
|
|
161
|
+
marshaller.check = function(data) {
|
|
162
|
+
checkInteger(data);
|
|
163
|
+
checkRange(0, 0xffffffff, data);
|
|
164
|
+
};
|
|
165
|
+
marshaller.marshall = function(ps, data) {
|
|
166
|
+
this.check(data);
|
|
167
|
+
// 32 t unsigned int
|
|
168
|
+
align(ps, 4);
|
|
169
|
+
ps.word32le(data);
|
|
170
|
+
ps._offset += 4;
|
|
171
|
+
};
|
|
172
|
+
break;
|
|
173
|
+
case 't':
|
|
174
|
+
//UINT64
|
|
175
|
+
marshaller.check = function(data) {
|
|
176
|
+
return checkLong(data, false);
|
|
177
|
+
};
|
|
178
|
+
marshaller.marshall = function(ps, data) {
|
|
179
|
+
data = this.check(data);
|
|
180
|
+
align(ps, 8);
|
|
181
|
+
ps.word32le(data.low);
|
|
182
|
+
ps.word32le(data.high);
|
|
183
|
+
ps._offset += 8;
|
|
184
|
+
};
|
|
185
|
+
break;
|
|
186
|
+
case 'x':
|
|
187
|
+
//INT64
|
|
188
|
+
marshaller.check = function(data) {
|
|
189
|
+
return checkLong(data, true);
|
|
190
|
+
};
|
|
191
|
+
marshaller.marshall = function(ps, data) {
|
|
192
|
+
data = this.check(data);
|
|
193
|
+
align(ps, 8);
|
|
194
|
+
ps.word32le(data.low);
|
|
195
|
+
ps.word32le(data.high);
|
|
196
|
+
ps._offset += 8;
|
|
197
|
+
};
|
|
198
|
+
break;
|
|
199
|
+
case 'd':
|
|
200
|
+
//DOUBLE
|
|
201
|
+
marshaller.check = function(data) {
|
|
202
|
+
if (typeof data !== 'number') {
|
|
203
|
+
throw new Error(`Data: ${data} was not of type number`);
|
|
204
|
+
} else if (Number.isNaN(data)) {
|
|
205
|
+
throw new Error(`Data: ${data} was not a number`);
|
|
206
|
+
} else if (!Number.isFinite(data)) {
|
|
207
|
+
throw new Error('Number outside range');
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
marshaller.marshall = function(ps, data) {
|
|
211
|
+
this.check(data);
|
|
212
|
+
align(ps, 8);
|
|
213
|
+
const buff = Buffer.alloc(8);
|
|
214
|
+
buff.writeDoubleLE(parseFloat(data), 0);
|
|
215
|
+
ps.put(buff);
|
|
216
|
+
ps._offset += 8;
|
|
217
|
+
};
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
throw new Error(`Unknown data type format: ${signature}`);
|
|
221
|
+
}
|
|
222
|
+
return marshaller;
|
|
223
|
+
};
|
|
224
|
+
exports.MakeSimpleMarshaller = MakeSimpleMarshaller;
|
|
225
|
+
|
|
226
|
+
var checkRange = function(minValue, maxValue, data) {
|
|
227
|
+
if (data > maxValue || data < minValue) {
|
|
228
|
+
throw new Error('Number outside range');
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
var checkInteger = function(data) {
|
|
233
|
+
if (typeof data !== 'number') {
|
|
234
|
+
throw new Error(`Data: ${data} was not of type number`);
|
|
235
|
+
}
|
|
236
|
+
if (Math.floor(data) !== data) {
|
|
237
|
+
throw new Error(`Data: ${data} was not an integer`);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
var checkBoolean = function(data) {
|
|
242
|
+
if (!(typeof data === 'boolean' || data === 0 || data === 1))
|
|
243
|
+
throw new Error(`Data: ${data} was not of type boolean`);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// This is essentially a tweaked version of 'fromValue' from Long.js with error checking.
|
|
247
|
+
// This can take number or string of decimal characters or 'Long' instance (or Long-style object with props low,high,unsigned).
|
|
248
|
+
var makeLong = function(val, signed) {
|
|
249
|
+
if (val instanceof Long) return val;
|
|
250
|
+
if (val instanceof Number) val = val.valueOf();
|
|
251
|
+
if (typeof val === 'number') {
|
|
252
|
+
try {
|
|
253
|
+
// Long.js won't alert you to precision loss in passing more than 53 bit ints through a double number, so we check here
|
|
254
|
+
checkInteger(val);
|
|
255
|
+
if (signed) {
|
|
256
|
+
checkRange(-0x1fffffffffffff, 0x1fffffffffffff, val);
|
|
257
|
+
} else {
|
|
258
|
+
checkRange(0, 0x1fffffffffffff, val);
|
|
259
|
+
}
|
|
260
|
+
} catch (e) {
|
|
261
|
+
e.message += ' (Number type can only carry 53 bit integer)';
|
|
262
|
+
throw e;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
return Long.fromNumber(val, !signed);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
e.message = `Error converting number to 64bit integer "${e.message}"`;
|
|
268
|
+
throw e;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (typeof val === 'string' || val instanceof String) {
|
|
272
|
+
var radix = 10;
|
|
273
|
+
val = val.trim().toUpperCase(); // remove extra whitespace and make uppercase (for hex)
|
|
274
|
+
if (val.substring(0, 2) === '0X') {
|
|
275
|
+
radix = 16;
|
|
276
|
+
val = val.substring(2);
|
|
277
|
+
} else if (val.substring(0, 3) === '-0X') {
|
|
278
|
+
// unusual, but just in case?
|
|
279
|
+
radix = 16;
|
|
280
|
+
val = `-${val.substring(3)}`;
|
|
281
|
+
}
|
|
282
|
+
val = val.replace(/^0+(?=\d)/, ''); // dump leading zeroes
|
|
283
|
+
var data;
|
|
284
|
+
try {
|
|
285
|
+
data = Long.fromString(val, !signed, radix);
|
|
286
|
+
} catch (e) {
|
|
287
|
+
e.message = `Error converting string to 64bit integer '${e.message}'`;
|
|
288
|
+
throw e;
|
|
289
|
+
}
|
|
290
|
+
// If string represents a number outside of 64 bit range, it can quietly overflow.
|
|
291
|
+
// We assume if things converted correctly the string coming out of Long should match what went into it.
|
|
292
|
+
if (data.toString(radix).toUpperCase() !== val)
|
|
293
|
+
throw new Error(
|
|
294
|
+
`Data: '${val}' did not convert correctly to ${
|
|
295
|
+
signed ? 'signed' : 'unsigned'
|
|
296
|
+
} 64 bit`
|
|
297
|
+
);
|
|
298
|
+
return data;
|
|
299
|
+
}
|
|
300
|
+
// Throws for non-objects, converts non-instanceof Long:
|
|
301
|
+
try {
|
|
302
|
+
return Long.fromBits(val.low, val.high, val.unsigned);
|
|
303
|
+
} catch (e) {
|
|
304
|
+
e.message = `Error converting object to 64bit integer '${e.message}'`;
|
|
305
|
+
throw e;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
var checkLong = function(data, signed) {
|
|
310
|
+
if (!Long.isLong(data)) {
|
|
311
|
+
data = makeLong(data, signed);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Do we enforce that Long.js object unsigned/signed match the field even if it is still in range?
|
|
315
|
+
// Probably, might help users avoid unintended bugs?
|
|
316
|
+
if (signed) {
|
|
317
|
+
if (data.unsigned)
|
|
318
|
+
throw new Error(
|
|
319
|
+
'Longjs object is unsigned, but marshalling into signed 64 bit field'
|
|
320
|
+
);
|
|
321
|
+
if (data.gt(Long.MAX_VALUE) || data.lt(Long.MIN_VALUE)) {
|
|
322
|
+
throw new Error(`Data: ${data} was out of range (64-bit signed)`);
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
if (!data.unsigned)
|
|
326
|
+
throw new Error(
|
|
327
|
+
'Longjs object is signed, but marshalling into unsigned 64 bit field'
|
|
328
|
+
);
|
|
329
|
+
// NOTE: data.gt(Long.MAX_UNSIGNED_VALUE) will catch if Long.js object is a signed value but is still within unsigned range!
|
|
330
|
+
// Since we are enforcing signed type matching between Long.js object and field, this note should not matter.
|
|
331
|
+
if (data.gt(Long.MAX_UNSIGNED_VALUE) || data.lt(0)) {
|
|
332
|
+
throw new Error(`Data: ${data} was out of range (64-bit unsigned)`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return data;
|
|
336
|
+
};
|
package/lib/message.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
2
|
+
const marshall = require('./marshall');
|
|
3
|
+
const constants = require('./constants');
|
|
4
|
+
const DBusBuffer = require('./dbus-buffer');
|
|
5
|
+
|
|
6
|
+
const headerSignature = require('./header-signature.json');
|
|
7
|
+
|
|
8
|
+
module.exports.unmarshalMessages = function messageParser(
|
|
9
|
+
stream,
|
|
10
|
+
onMessage,
|
|
11
|
+
opts
|
|
12
|
+
) {
|
|
13
|
+
var state = 0; // 0: header, 1: fields + body
|
|
14
|
+
var header, fieldsAndBody;
|
|
15
|
+
var fieldsLength, fieldsLengthPadded;
|
|
16
|
+
var fieldsAndBodyLength = 0;
|
|
17
|
+
var bodyLength = 0;
|
|
18
|
+
stream.on('readable', function() {
|
|
19
|
+
while (1) {
|
|
20
|
+
if (state === 0) {
|
|
21
|
+
header = stream.read(16);
|
|
22
|
+
if (!header) break;
|
|
23
|
+
state = 1;
|
|
24
|
+
|
|
25
|
+
fieldsLength = header.readUInt32LE(12);
|
|
26
|
+
fieldsLengthPadded = ((fieldsLength + 7) >> 3) << 3;
|
|
27
|
+
bodyLength = header.readUInt32LE(4);
|
|
28
|
+
fieldsAndBodyLength = fieldsLengthPadded + bodyLength;
|
|
29
|
+
} else {
|
|
30
|
+
fieldsAndBody = stream.read(fieldsAndBodyLength);
|
|
31
|
+
if (!fieldsAndBody) break;
|
|
32
|
+
state = 0;
|
|
33
|
+
|
|
34
|
+
var messageBuffer = new DBusBuffer(fieldsAndBody, undefined, opts);
|
|
35
|
+
var unmarshalledHeader = messageBuffer.readArray(
|
|
36
|
+
headerSignature[0].child[0],
|
|
37
|
+
fieldsLength
|
|
38
|
+
);
|
|
39
|
+
messageBuffer.align(3);
|
|
40
|
+
var headerName;
|
|
41
|
+
var message = {};
|
|
42
|
+
message.serial = header.readUInt32LE(8);
|
|
43
|
+
|
|
44
|
+
for (var i = 0; i < unmarshalledHeader.length; ++i) {
|
|
45
|
+
headerName = constants.headerTypeName[unmarshalledHeader[i][0]];
|
|
46
|
+
message[headerName] = unmarshalledHeader[i][1][1][0];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
message.type = header[1];
|
|
50
|
+
message.flags = header[2];
|
|
51
|
+
|
|
52
|
+
if (bodyLength > 0 && message.signature) {
|
|
53
|
+
message.body = messageBuffer.read(message.signature);
|
|
54
|
+
}
|
|
55
|
+
onMessage(message);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// given buffer which contains entire message deserialise it
|
|
62
|
+
// TODO: factor out common code
|
|
63
|
+
module.exports.unmarshall = function unmarshall(buff, opts) {
|
|
64
|
+
var msgBuf = new DBusBuffer(buff, undefined, opts);
|
|
65
|
+
var headers = msgBuf.read('yyyyuua(yv)');
|
|
66
|
+
var message = {};
|
|
67
|
+
for (var i = 0; i < headers[6].length; ++i) {
|
|
68
|
+
var headerName = constants.headerTypeName[headers[6][i][0]];
|
|
69
|
+
message[headerName] = headers[6][i][1][1][0];
|
|
70
|
+
}
|
|
71
|
+
message.type = headers[1];
|
|
72
|
+
message.flags = headers[2];
|
|
73
|
+
message.serial = headers[5];
|
|
74
|
+
msgBuf.align(3);
|
|
75
|
+
message.body = msgBuf.read(message.signature);
|
|
76
|
+
return message;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
module.exports.marshall = function marshallMessage(message) {
|
|
80
|
+
if (!message.serial) throw new Error('Missing or invalid serial');
|
|
81
|
+
var flags = message.flags || 0;
|
|
82
|
+
var type = message.type || constants.messageType.methodCall;
|
|
83
|
+
var bodyLength = 0;
|
|
84
|
+
var bodyBuff;
|
|
85
|
+
if (message.signature && message.body) {
|
|
86
|
+
bodyBuff = marshall(message.signature, message.body);
|
|
87
|
+
bodyLength = bodyBuff.length;
|
|
88
|
+
}
|
|
89
|
+
var header = [
|
|
90
|
+
constants.endianness.le,
|
|
91
|
+
type,
|
|
92
|
+
flags,
|
|
93
|
+
constants.protocolVersion,
|
|
94
|
+
bodyLength,
|
|
95
|
+
message.serial
|
|
96
|
+
];
|
|
97
|
+
var headerBuff = marshall('yyyyuu', header);
|
|
98
|
+
var fields = [];
|
|
99
|
+
constants.headerTypeName.forEach(function(fieldName) {
|
|
100
|
+
var fieldVal = message[fieldName];
|
|
101
|
+
if (fieldVal) {
|
|
102
|
+
fields.push([
|
|
103
|
+
constants.headerTypeId[fieldName],
|
|
104
|
+
[constants.fieldSignature[fieldName], fieldVal]
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
var fieldsBuff = marshall('a(yv)', [fields], 12);
|
|
109
|
+
var headerLenAligned =
|
|
110
|
+
((headerBuff.length + fieldsBuff.length + 7) >> 3) << 3;
|
|
111
|
+
var messageLen = headerLenAligned + bodyLength;
|
|
112
|
+
var messageBuff = Buffer.alloc(messageLen);
|
|
113
|
+
headerBuff.copy(messageBuff);
|
|
114
|
+
fieldsBuff.copy(messageBuff, headerBuff.length);
|
|
115
|
+
if (bodyLength > 0) bodyBuff.copy(messageBuff, headerLenAligned);
|
|
116
|
+
|
|
117
|
+
return messageBuff;
|
|
118
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const net = require('net');
|
|
2
|
+
const abs = require('abstract-socket');
|
|
3
|
+
const hexy = require('hexy').hexy;
|
|
4
|
+
|
|
5
|
+
var address = process.env.DBUS_SESSION_BUS_ADDRESS;
|
|
6
|
+
var m = address.match(/abstract=([^,]+)/);
|
|
7
|
+
|
|
8
|
+
net
|
|
9
|
+
.createServer(function(s) {
|
|
10
|
+
var buff = '';
|
|
11
|
+
var connected = false;
|
|
12
|
+
var cli = abs.createConnection(`\0${m[1]}`);
|
|
13
|
+
s.on('data', function(d) {
|
|
14
|
+
if (connected) {
|
|
15
|
+
cli.write(d);
|
|
16
|
+
} else {
|
|
17
|
+
buff += d.toString();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
setTimeout(function() {
|
|
21
|
+
console.log('CONNECTED!');
|
|
22
|
+
connected = true;
|
|
23
|
+
cli.write(buff);
|
|
24
|
+
}, 100);
|
|
25
|
+
cli.pipe(s);
|
|
26
|
+
|
|
27
|
+
cli.on('data', function(b) {
|
|
28
|
+
console.log(hexy(b, { prefix: 'from client ' }));
|
|
29
|
+
});
|
|
30
|
+
s.on('data', function(b) {
|
|
31
|
+
console.log(hexy(b, { prefix: 'from server ' }));
|
|
32
|
+
});
|
|
33
|
+
})
|
|
34
|
+
.listen(3334, function() {
|
|
35
|
+
console.log(
|
|
36
|
+
'Server started. connect with DBUS_SESSION_BUS_ADDRESS=tcp:host=127.0.0.1,port=3334'
|
|
37
|
+
);
|
|
38
|
+
});
|
package/lib/readline.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
2
|
+
|
|
3
|
+
module.exports = function readOneLine(stream, cb) {
|
|
4
|
+
var bytes = [];
|
|
5
|
+
function readable() {
|
|
6
|
+
while (1) {
|
|
7
|
+
var buf = stream.read(1);
|
|
8
|
+
if (!buf) return;
|
|
9
|
+
var b = buf[0];
|
|
10
|
+
if (b === 0x0a) {
|
|
11
|
+
try {
|
|
12
|
+
cb(Buffer.from(bytes));
|
|
13
|
+
} catch (error) {
|
|
14
|
+
stream.emit('error', error);
|
|
15
|
+
}
|
|
16
|
+
stream.removeListener('readable', readable);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
bytes.push(b);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
stream.on('readable', readable);
|
|
23
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
2
|
+
const readLine = require('./readline');
|
|
3
|
+
|
|
4
|
+
module.exports = function serverHandshake(stream, opts, cb) {
|
|
5
|
+
stream.name = 'SERVER SERVER';
|
|
6
|
+
readLine(stream, function(hello) {
|
|
7
|
+
console.log(['hello string: ', hello.toString(), hello]);
|
|
8
|
+
stream.write('REJECTED EXTERNAL DBUS_COOKIE_SHA1 ANONYMOUS\r\n');
|
|
9
|
+
readLine(stream, function() {
|
|
10
|
+
stream.write(
|
|
11
|
+
`DATA ${Buffer.from(
|
|
12
|
+
'org_freedesktop_general 642038150 b9ce247a275f427c8586e4c9de9bb951'
|
|
13
|
+
).toString('hex')}\r\n`
|
|
14
|
+
);
|
|
15
|
+
readLine(stream, function() {
|
|
16
|
+
stream.write(
|
|
17
|
+
'OK 6f72675f667265656465736b746f705f67656e6572616c20353631303331333937206239636532343761323735663432376338353836653463396465396262393531\r\n'
|
|
18
|
+
);
|
|
19
|
+
readLine(stream, function(begin) {
|
|
20
|
+
console.log(['AFTER begin: ', begin.toString()]);
|
|
21
|
+
cb(null);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// cookie: 561031397 1410749774 3a83c8200f930e7af4de135e8abd299b681a1f44dbb85399
|
|
29
|
+
|
|
30
|
+
// 1539856202
|
|
31
|
+
|
|
32
|
+
// server: org_freedesktop_general 561031397 b9ce247a275f427c8586e4c9de9bb951
|
|
33
|
+
// client: bwFSDjS0TJerqb0l 82986a987194788803d7da2a4b00e801cff9bdfd
|
|
34
|
+
// 82986a987194788803d7da2a4b00e801cff9bdfd = sha1(b9ce247a275f427c8586e4c9de9bb951:bwFSDjS0TJerqb0l:3a83c8200f930e7af4de135e8abd299b681a1f44dbb85399)
|
|
35
|
+
// server: OK e12a29dd7ffe3effac5eb95054123f80
|
|
36
|
+
|
|
37
|
+
//dbus.write('DATA 6f72675f667265656465736b746f705f67656e6572616c203636383430 31303032 203733653733313762383630356537323937623438303233376336353234343533\r\n');
|
package/lib/server.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const dbus = require('../index');
|
|
2
|
+
const net = require('net');
|
|
3
|
+
|
|
4
|
+
module.exports.createServer = function(handler) {
|
|
5
|
+
function Server() {
|
|
6
|
+
var id = 123;
|
|
7
|
+
this.server = net.createServer(function(socket) {
|
|
8
|
+
socket.idd = id;
|
|
9
|
+
id++;
|
|
10
|
+
|
|
11
|
+
var dbusConn = dbus.createConnection({ stream: socket, server: true });
|
|
12
|
+
if (handler) handler(dbusConn);
|
|
13
|
+
// TODO: inherit from EE this.emit('connect', dbusConn);
|
|
14
|
+
});
|
|
15
|
+
this.listen = this.server.listen.bind(this.server);
|
|
16
|
+
}
|
|
17
|
+
return new Server();
|
|
18
|
+
};
|
package/lib/signature.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// parse signature from string to tree
|
|
2
|
+
|
|
3
|
+
var match = {
|
|
4
|
+
'{': '}',
|
|
5
|
+
'(': ')'
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
var knownTypes = {};
|
|
9
|
+
'(){}ybnqiuxtdsogarvehm*?@&^'.split('').forEach(function(c) {
|
|
10
|
+
knownTypes[c] = true;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
module.exports = function parseSignature(signature) {
|
|
14
|
+
var index = 0;
|
|
15
|
+
function next() {
|
|
16
|
+
if (index < signature.length) {
|
|
17
|
+
var c = signature[index];
|
|
18
|
+
++index;
|
|
19
|
+
return c;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseOne(c) {
|
|
25
|
+
function checkNotEnd(c) {
|
|
26
|
+
if (!c) throw new Error('Bad signature: unexpected end');
|
|
27
|
+
return c;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!knownTypes[c])
|
|
31
|
+
throw new Error(`Unknown type: "${c}" in signature "${signature}"`);
|
|
32
|
+
|
|
33
|
+
var ele;
|
|
34
|
+
var res = { type: c, child: [] };
|
|
35
|
+
switch (c) {
|
|
36
|
+
case 'a': // array
|
|
37
|
+
ele = next();
|
|
38
|
+
checkNotEnd(ele);
|
|
39
|
+
res.child.push(parseOne(ele));
|
|
40
|
+
return res;
|
|
41
|
+
case '{': // dict entry
|
|
42
|
+
case '(': // struct
|
|
43
|
+
while ((ele = next()) !== null && ele !== match[c])
|
|
44
|
+
res.child.push(parseOne(ele));
|
|
45
|
+
checkNotEnd(ele);
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
return res;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var ret = [];
|
|
52
|
+
var c;
|
|
53
|
+
while ((c = next()) !== null) ret.push(parseOne(c));
|
|
54
|
+
return ret;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// command-line test
|
|
58
|
+
//console.log(JSON.stringify(module.exports(process.argv[2]), null, 4));
|
|
59
|
+
//var tree = module.exports('a(ssssbbbbbbbbuasa{ss}sa{sv})a(ssssssbbssa{ss}sa{sv})a(ssssssbsassa{sv})');
|
|
60
|
+
//console.log(tree);
|
|
61
|
+
//console.log(fromTree(tree))
|