node-opcua-packet-analyzer 2.165.0 → 2.167.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/dist/source/index.d.ts +2 -1
- package/dist/source/index.js +2 -2
- package/dist/source/index.js.map +1 -1
- package/dist/source/packet_analyzer/packet_analyzer.d.ts +17 -4
- package/dist/source/packet_analyzer/packet_analyzer.js +54 -46
- package/dist/source/packet_analyzer/packet_analyzer.js.map +1 -1
- package/dist/test_helpers/compare_obj_by_encoding.d.ts +2 -2
- package/dist/test_helpers/compare_obj_by_encoding.js +1 -1
- package/dist/test_helpers/compare_obj_by_encoding.js.map +1 -1
- package/dist/test_helpers/encode_decode_round_trip_test.d.ts +13 -16
- package/dist/test_helpers/encode_decode_round_trip_test.js +21 -19
- package/dist/test_helpers/encode_decode_round_trip_test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/source/index.ts +10 -1
- package/source/packet_analyzer/packet_analyzer.ts +107 -69
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
import { inspect
|
|
1
|
+
import { inspect } from "node:util";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { assert } from "node-opcua-assert";
|
|
4
4
|
|
|
5
5
|
import { decodeByte, decodeExpandedNodeId, decodeNodeId, decodeUInt32 } from "node-opcua-basic-types";
|
|
6
6
|
import { BinaryStream } from "node-opcua-binary-stream";
|
|
7
7
|
import { hexDump } from "node-opcua-debug";
|
|
8
|
-
import {
|
|
8
|
+
import { type ConstructorFunc, getStandardDataTypeFactory, type IBaseUAObject } from "node-opcua-factory";
|
|
9
|
+
import type { ExpandedNodeId } from "node-opcua-nodeid";
|
|
9
10
|
import { buffer_ellipsis } from "node-opcua-utils";
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
-
|
|
12
|
+
const displayWidth = 132;
|
|
13
|
+
const separator = "-".repeat(49);
|
|
13
14
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
return (s + " ").substring(0, Math.max(s.length, width));
|
|
15
|
+
function padNumber(n: number, width: number): string {
|
|
16
|
+
return n.toString().padEnd(width);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/** Encoding info maps enum key names ↔ bitmask values */
|
|
20
|
+
type EncodingInfo = Record<string, number | string>;
|
|
21
|
+
|
|
22
|
+
function display_encoding_mask(padding: string, encodingMask: number, encodingInfo: EncodingInfo): void {
|
|
20
23
|
for (const v in encodingInfo) {
|
|
21
|
-
if (!Object.
|
|
24
|
+
if (!Object.hasOwn(encodingInfo, v)) {
|
|
22
25
|
continue;
|
|
23
26
|
}
|
|
24
27
|
const enumKey = encodingInfo[v];
|
|
@@ -26,85 +29,117 @@ function display_encoding_mask(padding: string, encodingMask: any, encodingInfo:
|
|
|
26
29
|
continue;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
const mask = encodingInfo[enumKey];
|
|
32
|
+
const mask = encodingInfo[enumKey] as number;
|
|
30
33
|
const bit = Math.log(mask) / Math.log(2);
|
|
31
34
|
|
|
32
|
-
const bits =
|
|
35
|
+
const bits = Array<string>(9).fill(".");
|
|
33
36
|
bits[bit] = (encodingMask & mask) === mask ? "Y" : "n";
|
|
34
37
|
|
|
35
|
-
console.log(padding
|
|
38
|
+
console.log(`${padding} ${bits.join("")}` + ` <- has ${enumKey} 0x${mask.toString(16)}`);
|
|
36
39
|
}
|
|
37
|
-
// DataValueEncodingByte
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
function hex_block(start: number, end: number, buffer: Buffer) {
|
|
42
|
+
function hex_block(start: number, end: number, buffer: Buffer): string {
|
|
41
43
|
const n = end - start;
|
|
42
44
|
const strBuf = buffer_ellipsis(buffer);
|
|
43
45
|
return (
|
|
44
|
-
chalk.cyan("s:") +
|
|
46
|
+
chalk.cyan("s:") +
|
|
47
|
+
padNumber(start, 4) +
|
|
48
|
+
chalk.cyan(" e:") +
|
|
49
|
+
padNumber(end, 4) +
|
|
50
|
+
chalk.cyan(" n:") +
|
|
51
|
+
padNumber(n, 4) +
|
|
52
|
+
" " +
|
|
53
|
+
chalk.yellow(strBuf)
|
|
45
54
|
);
|
|
46
55
|
}
|
|
47
56
|
|
|
57
|
+
type TraceOperation = "start" | "end" | "start_array" | "end_array" | "start_element" | "end_element" | "member";
|
|
58
|
+
|
|
59
|
+
interface TracerMethods {
|
|
60
|
+
dump: (title: string, value: unknown) => void;
|
|
61
|
+
encoding_byte: (encodingMask: number, valueEnum: EncodingInfo, start: number, end: number) => void;
|
|
62
|
+
trace: (operation: TraceOperation, name: string, value: unknown, start: number, end: number, fieldType: string) => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
interface Tracer {
|
|
49
66
|
name?: string;
|
|
50
|
-
tracer:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
tracer: TracerMethods;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface Encodeable {
|
|
71
|
+
encodingDefaultBinary: unknown;
|
|
72
|
+
encode?: (stream: BinaryStream) => void;
|
|
55
73
|
}
|
|
74
|
+
|
|
75
|
+
function isEncodeable(val: unknown): val is Encodeable {
|
|
76
|
+
return val != null && typeof (val as Encodeable).encode === "function";
|
|
77
|
+
}
|
|
78
|
+
|
|
56
79
|
function make_tracer(buffer: Buffer, padding: number, offset?: number): Tracer {
|
|
57
|
-
padding
|
|
58
|
-
offset
|
|
80
|
+
padding ??= 0;
|
|
81
|
+
offset ??= 0;
|
|
59
82
|
|
|
60
|
-
const pad = () => "
|
|
83
|
+
const pad = (): string => "".padEnd(padding);
|
|
61
84
|
|
|
62
|
-
function _display(str: string, hexInfo?: string) {
|
|
63
|
-
hexInfo
|
|
85
|
+
function _display(str: string, hexInfo?: string): void {
|
|
86
|
+
hexInfo ??= "";
|
|
64
87
|
// account for ESC codes for colors
|
|
65
|
-
const nbColorAttributes =
|
|
88
|
+
const nbColorAttributes = str.split("").filter((c) => c === "\u001b").length;
|
|
66
89
|
const extra = nbColorAttributes * 5;
|
|
67
|
-
|
|
90
|
+
const line = (pad() + str).padEnd(displayWidth + extra);
|
|
91
|
+
console.log(`${line}|${hexInfo}`);
|
|
68
92
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for (const line of
|
|
93
|
+
|
|
94
|
+
function display(str: string, hexInfo?: string): void {
|
|
95
|
+
for (const line of str.split("\n")) {
|
|
72
96
|
_display(line, hexInfo);
|
|
73
97
|
}
|
|
74
98
|
}
|
|
75
99
|
|
|
76
|
-
function display_encodeable(value:
|
|
100
|
+
function display_encodeable(value: Encodeable, buffer1: Buffer, start: number, end: number): void {
|
|
77
101
|
const bufferExtract = buffer1.subarray(start, end);
|
|
78
102
|
const stream = new BinaryStream(bufferExtract);
|
|
79
103
|
const nodeId = decodeNodeId(stream);
|
|
80
|
-
const encodingMask = decodeByte(stream);
|
|
104
|
+
const encodingMask = decodeByte(stream);
|
|
81
105
|
const length = decodeUInt32(stream);
|
|
82
106
|
|
|
83
|
-
display(chalk.green(" ExpandedNodId =")
|
|
84
|
-
display(chalk.green(" encoding mask =")
|
|
85
|
-
display(chalk.green(" length =")
|
|
86
|
-
analyzePacket(
|
|
107
|
+
display(`${chalk.green(" ExpandedNodId =")} ${nodeId}`);
|
|
108
|
+
display(`${chalk.green(" encoding mask =")} ${encodingMask}`);
|
|
109
|
+
display(`${chalk.green(" length =")} ${length}`);
|
|
110
|
+
analyzePacket(
|
|
111
|
+
bufferExtract.subarray(stream.length),
|
|
112
|
+
value.encodingDefaultBinary as ObjectMessage,
|
|
113
|
+
padding + 2,
|
|
114
|
+
start + stream.length
|
|
115
|
+
);
|
|
87
116
|
}
|
|
88
117
|
|
|
89
118
|
return {
|
|
90
119
|
tracer: {
|
|
91
|
-
dump: (title: string, value:
|
|
120
|
+
dump: (title: string, value: unknown): void => display(`${title} ${chalk.green(String(value))}`),
|
|
92
121
|
|
|
93
|
-
encoding_byte: (encodingMask:
|
|
122
|
+
encoding_byte: (encodingMask: number, valueEnum: EncodingInfo, start: number, end: number): void => {
|
|
94
123
|
assert(valueEnum);
|
|
95
124
|
const b = buffer.subarray(start, end);
|
|
96
125
|
display(" 012345678", hex_block(start, end, b));
|
|
97
126
|
display_encoding_mask(pad(), encodingMask, valueEnum);
|
|
98
127
|
},
|
|
99
128
|
|
|
100
|
-
trace: (
|
|
129
|
+
trace: (
|
|
130
|
+
operation: TraceOperation,
|
|
131
|
+
name: string,
|
|
132
|
+
value: unknown,
|
|
133
|
+
start: number,
|
|
134
|
+
end: number,
|
|
135
|
+
fieldType: string
|
|
136
|
+
): void => {
|
|
101
137
|
const b = buffer.subarray(start, end);
|
|
102
|
-
let _hexDump = "";
|
|
103
138
|
|
|
104
139
|
switch (operation) {
|
|
105
140
|
case "start":
|
|
106
141
|
padding += 2;
|
|
107
|
-
display(name
|
|
142
|
+
display(name);
|
|
108
143
|
break;
|
|
109
144
|
|
|
110
145
|
case "end":
|
|
@@ -112,58 +147,58 @@ function make_tracer(buffer: Buffer, padding: number, offset?: number): Tracer {
|
|
|
112
147
|
break;
|
|
113
148
|
|
|
114
149
|
case "start_array":
|
|
115
|
-
display(
|
|
150
|
+
display(`.${name} (length = ${value}) [`, hex_block(start, end, b));
|
|
116
151
|
padding += 2;
|
|
117
152
|
break;
|
|
118
153
|
|
|
119
154
|
case "end_array":
|
|
120
155
|
padding -= 2;
|
|
121
|
-
display(
|
|
156
|
+
display(`] // ${name}`);
|
|
122
157
|
break;
|
|
123
158
|
|
|
124
159
|
case "start_element":
|
|
125
|
-
display(
|
|
160
|
+
display(` #${value} {`);
|
|
126
161
|
padding += 2;
|
|
127
162
|
break;
|
|
128
163
|
|
|
129
164
|
case "end_element":
|
|
130
165
|
padding -= 2;
|
|
131
|
-
display(
|
|
166
|
+
display(` } // # ${value}`);
|
|
132
167
|
break;
|
|
133
168
|
|
|
134
|
-
case "member":
|
|
135
|
-
display(
|
|
169
|
+
case "member": {
|
|
170
|
+
display(`.${name} : ${fieldType}`);
|
|
136
171
|
|
|
137
|
-
_hexDump = "";
|
|
138
172
|
if (value instanceof Buffer) {
|
|
139
|
-
|
|
140
|
-
console.log(_hexDump);
|
|
173
|
+
console.log(hexDump(value));
|
|
141
174
|
value = "<BUFFER>";
|
|
142
175
|
}
|
|
143
176
|
|
|
144
|
-
if (value
|
|
177
|
+
if (isEncodeable(value)) {
|
|
145
178
|
if (fieldType === "ExtensionObject") {
|
|
146
179
|
display_encodeable(value, buffer, start, end);
|
|
147
180
|
} else {
|
|
148
|
-
|
|
149
|
-
display(str);
|
|
181
|
+
display(String(value) || "<empty>");
|
|
150
182
|
}
|
|
151
183
|
} else {
|
|
152
|
-
display(
|
|
184
|
+
display(` ${value}`, hex_block(start, end, b));
|
|
153
185
|
}
|
|
154
186
|
break;
|
|
187
|
+
}
|
|
155
188
|
}
|
|
156
189
|
}
|
|
157
190
|
}
|
|
158
191
|
};
|
|
159
192
|
}
|
|
160
193
|
|
|
161
|
-
interface AnalyzePacketOptions {
|
|
194
|
+
export interface AnalyzePacketOptions {
|
|
195
|
+
[key: string]: unknown;
|
|
196
|
+
}
|
|
162
197
|
|
|
163
198
|
export interface ObjectMessage {
|
|
164
199
|
encode(stream: BinaryStream): void;
|
|
165
200
|
decode(stream: BinaryStream): void;
|
|
166
|
-
decodeDebug(stream: BinaryStream, options:
|
|
201
|
+
decodeDebug(stream: BinaryStream, options: Tracer): void;
|
|
167
202
|
}
|
|
168
203
|
|
|
169
204
|
export function analyzePacket(
|
|
@@ -184,15 +219,15 @@ export function analyseExtensionObject(
|
|
|
184
219
|
customOptions?: AnalyzePacketOptions
|
|
185
220
|
): void {
|
|
186
221
|
const stream = new BinaryStream(buffer);
|
|
187
|
-
let id;
|
|
188
|
-
let objMessage;
|
|
222
|
+
let id: ExpandedNodeId | undefined;
|
|
223
|
+
let objMessage: ObjectMessage | undefined;
|
|
189
224
|
try {
|
|
190
225
|
id = decodeExpandedNodeId(stream);
|
|
191
|
-
objMessage = getStandardDataTypeFactory().constructObject(id);
|
|
226
|
+
objMessage = getStandardDataTypeFactory().constructObject(id) as unknown as ObjectMessage;
|
|
192
227
|
} catch (err) {
|
|
193
228
|
console.log(id);
|
|
194
229
|
console.log(err);
|
|
195
|
-
console.log("Cannot read decodeExpandedNodeId
|
|
230
|
+
console.log("Cannot read decodeExpandedNodeId on stream " + stream.buffer.toString("hex"));
|
|
196
231
|
}
|
|
197
232
|
_internalAnalyzePacket(buffer, stream, objMessage, padding, customOptions, offset);
|
|
198
233
|
}
|
|
@@ -204,10 +239,12 @@ function _internalAnalyzePacket(
|
|
|
204
239
|
padding: number,
|
|
205
240
|
customOptions?: AnalyzePacketOptions,
|
|
206
241
|
offset?: number
|
|
207
|
-
) {
|
|
242
|
+
): void {
|
|
208
243
|
let options = make_tracer(buffer, padding, offset);
|
|
209
244
|
options.name = "message";
|
|
210
|
-
if (customOptions)
|
|
245
|
+
if (customOptions) {
|
|
246
|
+
options = { ...options, ...customOptions };
|
|
247
|
+
}
|
|
211
248
|
try {
|
|
212
249
|
if (objMessage) {
|
|
213
250
|
objMessage.decodeDebug(stream, options);
|
|
@@ -216,29 +253,30 @@ function _internalAnalyzePacket(
|
|
|
216
253
|
}
|
|
217
254
|
} catch (err) {
|
|
218
255
|
console.log(" Error in ", err);
|
|
219
|
-
if (
|
|
256
|
+
if (err instanceof Error) {
|
|
220
257
|
console.log(" Error in ", err.stack);
|
|
221
258
|
}
|
|
222
259
|
console.log(" objMessage ", inspect(objMessage, { colors: true }));
|
|
223
260
|
}
|
|
224
261
|
}
|
|
225
262
|
|
|
226
|
-
export function analyze_object_binary_encoding(obj:
|
|
263
|
+
export function analyze_object_binary_encoding(obj: IBaseUAObject): void {
|
|
227
264
|
assert(obj);
|
|
228
265
|
|
|
229
266
|
const size = obj.binaryStoreSize();
|
|
230
|
-
console.log(
|
|
267
|
+
console.log(separator);
|
|
231
268
|
console.log(" size = ", size);
|
|
232
269
|
const stream = new BinaryStream(size);
|
|
233
270
|
obj.encode(stream);
|
|
234
271
|
|
|
235
272
|
stream.rewind();
|
|
236
|
-
console.log(
|
|
273
|
+
console.log(separator);
|
|
237
274
|
if (stream.buffer.length < 256) {
|
|
238
275
|
console.log(hexDump(stream.buffer));
|
|
239
|
-
console.log(
|
|
276
|
+
console.log(separator);
|
|
240
277
|
}
|
|
241
278
|
|
|
242
|
-
const
|
|
243
|
-
|
|
279
|
+
const Ctor = obj.constructor as ConstructorFunc;
|
|
280
|
+
const reloadedObject = new Ctor();
|
|
281
|
+
analyzePacket(stream.buffer, reloadedObject as unknown as ObjectMessage, 0);
|
|
244
282
|
}
|