node-opcua-transport 2.51.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.
Files changed (69) hide show
  1. package/.mocharc.yml +10 -0
  2. package/LICENSE +20 -0
  3. package/dist/source/AcknowledgeMessage.d.ts +27 -0
  4. package/dist/source/AcknowledgeMessage.js +79 -0
  5. package/dist/source/AcknowledgeMessage.js.map +1 -0
  6. package/dist/source/HelloMessage.d.ts +27 -0
  7. package/dist/source/HelloMessage.js +95 -0
  8. package/dist/source/HelloMessage.js.map +1 -0
  9. package/dist/source/TCPErrorMessage.d.ts +18 -0
  10. package/dist/source/TCPErrorMessage.js +47 -0
  11. package/dist/source/TCPErrorMessage.js.map +1 -0
  12. package/dist/source/client_tcp_transport.d.ts +68 -0
  13. package/dist/source/client_tcp_transport.js +315 -0
  14. package/dist/source/client_tcp_transport.js.map +1 -0
  15. package/dist/source/index.d.ts +11 -0
  16. package/dist/source/index.js +24 -0
  17. package/dist/source/index.js.map +1 -0
  18. package/dist/source/message_builder_base.d.ts +61 -0
  19. package/dist/source/message_builder_base.js +207 -0
  20. package/dist/source/message_builder_base.js.map +1 -0
  21. package/dist/source/server_tcp_transport.d.ts +45 -0
  22. package/dist/source/server_tcp_transport.js +232 -0
  23. package/dist/source/server_tcp_transport.js.map +1 -0
  24. package/dist/source/tcp_transport.d.ts +117 -0
  25. package/dist/source/tcp_transport.js +350 -0
  26. package/dist/source/tcp_transport.js.map +1 -0
  27. package/dist/source/tools.d.ts +13 -0
  28. package/dist/source/tools.js +99 -0
  29. package/dist/source/tools.js.map +1 -0
  30. package/dist/source/utils.d.ts +2 -0
  31. package/dist/source/utils.js +9 -0
  32. package/dist/source/utils.js.map +1 -0
  33. package/dist/test-fixtures/fixture_full_tcp_packets.d.ts +21 -0
  34. package/dist/test-fixtures/fixture_full_tcp_packets.js +413 -0
  35. package/dist/test-fixtures/fixture_full_tcp_packets.js.map +1 -0
  36. package/dist/test-fixtures/index.d.ts +1 -0
  37. package/dist/test-fixtures/index.js +14 -0
  38. package/dist/test-fixtures/index.js.map +1 -0
  39. package/dist/test_helpers/direct_transport.d.ts +14 -0
  40. package/dist/test_helpers/direct_transport.js +63 -0
  41. package/dist/test_helpers/direct_transport.js.map +1 -0
  42. package/dist/test_helpers/fake_server.d.ts +15 -0
  43. package/dist/test_helpers/fake_server.js +56 -0
  44. package/dist/test_helpers/fake_server.js.map +1 -0
  45. package/dist/test_helpers/half_com_channel.d.ts +10 -0
  46. package/dist/test_helpers/half_com_channel.js +35 -0
  47. package/dist/test_helpers/half_com_channel.js.map +1 -0
  48. package/dist/test_helpers/index.d.ts +4 -0
  49. package/dist/test_helpers/index.js +17 -0
  50. package/dist/test_helpers/index.js.map +1 -0
  51. package/dist/test_helpers/socket_transport.d.ts +8 -0
  52. package/dist/test_helpers/socket_transport.js +30 -0
  53. package/dist/test_helpers/socket_transport.js.map +1 -0
  54. package/package.json +50 -0
  55. package/source/AcknowledgeMessage.ts +112 -0
  56. package/source/HelloMessage.ts +133 -0
  57. package/source/TCPErrorMessage.ts +57 -0
  58. package/source/client_tcp_transport.ts +366 -0
  59. package/source/index.ts +11 -0
  60. package/source/message_builder_base.ts +263 -0
  61. package/source/server_tcp_transport.ts +284 -0
  62. package/source/tcp_transport.ts +450 -0
  63. package/source/tools.ts +113 -0
  64. package/source/utils.ts +4 -0
  65. package/test_helpers/direct_transport.ts +78 -0
  66. package/test_helpers/fake_server.ts +71 -0
  67. package/test_helpers/half_com_channel.ts +38 -0
  68. package/test_helpers/index.ts +4 -0
  69. package/test_helpers/socket_transport.ts +34 -0
@@ -0,0 +1,284 @@
1
+ /**
2
+ * @module node-opcua-transport
3
+ */
4
+ // tslint:disable:class-name
5
+ // system
6
+ import * as chalk from "chalk";
7
+ import { Socket } from "net";
8
+ import { assert } from "node-opcua-assert";
9
+
10
+ // opcua requires
11
+ import { BinaryStream } from "node-opcua-binary-stream";
12
+ import { verify_message_chunk } from "node-opcua-chunkmanager";
13
+ import { StatusCode, StatusCodes } from "node-opcua-status-code";
14
+ import { ErrorCallback } from "node-opcua-status-code";
15
+
16
+ // this package requires
17
+ import { AcknowledgeMessage } from "./AcknowledgeMessage";
18
+ import { HelloMessage } from "./HelloMessage";
19
+ import { TCP_transport } from "./tcp_transport";
20
+ import { TCPErrorMessage } from "./TCPErrorMessage";
21
+ import { decodeMessage, packTcpMessage } from "./tools";
22
+
23
+ import * as debug from "node-opcua-debug";
24
+ import { doTraceHelloAck } from "./utils";
25
+
26
+ const hexDump = debug.hexDump;
27
+ const debugLog = debug.make_debugLog(__filename);
28
+ const errorLog = debug.make_errorLog(__filename);
29
+ const doDebug = debug.checkDebugFlag(__filename);
30
+
31
+ type CallbackFunc = (err: null | Error) => void;
32
+
33
+ function clamp_value(value: number, minVal: number, maxVal: number): number {
34
+ assert(minVal < maxVal);
35
+ if (value === 0) {
36
+ return maxVal;
37
+ }
38
+ if (value < minVal) {
39
+ return minVal;
40
+ }
41
+ /* istanbul ignore next*/
42
+ if (value >= maxVal) {
43
+ return maxVal;
44
+ }
45
+ return value;
46
+ }
47
+
48
+ const minimumBufferSize = 8192;
49
+
50
+ /**
51
+ * @class ServerTCP_transport
52
+ * @extends TCP_transport
53
+ * @constructor
54
+ *
55
+ */
56
+ export class ServerTCP_transport extends TCP_transport {
57
+ public static throttleTime: number = 1000;
58
+
59
+ public receiveBufferSize: number;
60
+ public sendBufferSize: number;
61
+ public maxMessageSize: number;
62
+ public maxChunkCount: number;
63
+ public protocolVersion: number;
64
+
65
+ private _aborted: number;
66
+ private _helloReceived: boolean;
67
+
68
+ constructor() {
69
+ super();
70
+ this._aborted = 0;
71
+ this._helloReceived = false;
72
+ this.receiveBufferSize = 0;
73
+ this.sendBufferSize = 0;
74
+ this.maxMessageSize = 0;
75
+ this.maxChunkCount = 0;
76
+ this.protocolVersion = 0;
77
+ }
78
+
79
+ protected _write_chunk(messageChunk: Buffer): void {
80
+ if (this.sendBufferSize > 0 && messageChunk.length > this.sendBufferSize) {
81
+ errorLog(
82
+ "write chunk exceed sendBufferSize messageChunk length = ",
83
+ messageChunk.length,
84
+ "sendBufferSize = ",
85
+ this.sendBufferSize
86
+ );
87
+ }
88
+ super._write_chunk(messageChunk);
89
+ }
90
+ /**
91
+ * Initialize the server transport.
92
+ *
93
+ *
94
+ * The ServerTCP_transport initialization process starts by waiting for the client to send a "HEL" message.
95
+ *
96
+ * The ServerTCP_transport replies with a "ACK" message and then start waiting for further messages of any size.
97
+ *
98
+ * The callback function received an error:
99
+ * - if no message from the client is received within the ```self.timeout``` period,
100
+ * - or, if the connection has dropped within the same interval.
101
+ * - if the protocol version specified within the HEL message is invalid or is greater
102
+ * than ```self.protocolVersion```
103
+ *
104
+ *
105
+ */
106
+ public init(socket: Socket, callback: ErrorCallback) {
107
+ if (debugLog) {
108
+ debugLog(chalk.cyan("init socket"));
109
+ }
110
+ assert(!this._socket, "init already called!");
111
+ assert(typeof callback === "function", "expecting a valid callback ");
112
+ this._install_socket(socket);
113
+ this._install_HEL_message_receiver(callback);
114
+ }
115
+
116
+ public abortWithError(statusCode: StatusCode, extraErrorDescription: string, callback: ErrorCallback) {
117
+ return this._abortWithError(statusCode, extraErrorDescription, callback);
118
+ }
119
+ private _abortWithError(statusCode: StatusCode, extraErrorDescription: string, callback: ErrorCallback) {
120
+ if (debugLog) {
121
+ debugLog(chalk.cyan("_abortWithError"));
122
+ }
123
+
124
+ assert(typeof callback === "function", "expecting a callback");
125
+
126
+ /* istanbul ignore else */
127
+ if (!this._aborted) {
128
+ this._aborted = 1;
129
+ setTimeout(() => {
130
+ // send the error message and close the connection
131
+ assert(StatusCodes.hasOwnProperty(statusCode.name));
132
+
133
+ /* istanbul ignore next*/
134
+ if (doDebug) {
135
+ debugLog(chalk.red(" Server aborting because ") + chalk.cyan(statusCode.name));
136
+ debugLog(chalk.red(" extraErrorDescription ") + chalk.cyan(extraErrorDescription));
137
+ }
138
+
139
+ const errorResponse = new TCPErrorMessage({
140
+ reason: statusCode.description,
141
+ statusCode
142
+ });
143
+
144
+ const messageChunk = packTcpMessage("ERR", errorResponse);
145
+
146
+ this.write(messageChunk);
147
+
148
+ this.disconnect(() => {
149
+ this._aborted = 2;
150
+ callback(new Error(extraErrorDescription + " StatusCode = " + statusCode.name));
151
+ });
152
+ }, ServerTCP_transport.throttleTime);
153
+ } else {
154
+ callback(new Error(statusCode.name));
155
+ }
156
+ }
157
+
158
+ private _send_ACK_response(helloMessage: HelloMessage) {
159
+ assert(helloMessage.receiveBufferSize >= minimumBufferSize);
160
+ assert(helloMessage.sendBufferSize >= minimumBufferSize);
161
+
162
+ this.receiveBufferSize = clamp_value(helloMessage.receiveBufferSize, 8192, 512 * 1024);
163
+ this.sendBufferSize = clamp_value(helloMessage.sendBufferSize, 8192, 512 * 1024);
164
+ this.maxMessageSize = clamp_value(helloMessage.maxMessageSize, 100000, 64 * 1024 * 1024);
165
+ this.maxChunkCount = clamp_value(helloMessage.maxChunkCount, 0, 65535);
166
+
167
+ // istanbul ignore next
168
+ if (doTraceHelloAck) {
169
+ console.log(`received Hello \n${helloMessage.toString()}`);
170
+ }
171
+
172
+ debugLog("Client accepts only message of size => ", this.maxMessageSize);
173
+
174
+ const acknowledgeMessage = new AcknowledgeMessage({
175
+ maxChunkCount: this.maxChunkCount,
176
+ maxMessageSize: this.maxMessageSize,
177
+ protocolVersion: this.protocolVersion,
178
+ receiveBufferSize: this.receiveBufferSize,
179
+ sendBufferSize: this.sendBufferSize
180
+ });
181
+
182
+ // istanbul ignore next
183
+ if (doTraceHelloAck) {
184
+ console.log(`sending Ack \n${acknowledgeMessage.toString()}`);
185
+ }
186
+
187
+ const messageChunk = packTcpMessage("ACK", acknowledgeMessage);
188
+
189
+ /* istanbul ignore next*/
190
+ if (doDebug) {
191
+ verify_message_chunk(messageChunk);
192
+ debugLog("server send: " + chalk.yellow("ACK"));
193
+ debugLog("server send: " + hexDump(messageChunk));
194
+ debugLog("acknowledgeMessage=", acknowledgeMessage);
195
+ }
196
+
197
+ // send the ACK reply
198
+ this.write(messageChunk);
199
+ }
200
+
201
+ private _install_HEL_message_receiver(callback: ErrorCallback) {
202
+ if (debugLog) {
203
+ debugLog(chalk.cyan("_install_HEL_message_receiver "));
204
+ }
205
+ this._install_one_time_message_receiver((err?: Error | null, data?: Buffer) => {
206
+ if (err) {
207
+ this._abortWithError(StatusCodes.BadConnectionRejected, err.message, callback);
208
+ } else {
209
+ // handle the HEL message
210
+ this._on_HEL_message(data!, callback);
211
+ }
212
+ });
213
+ }
214
+
215
+ private _on_HEL_message(data: Buffer, callback: ErrorCallback) {
216
+ if (debugLog) {
217
+ debugLog(chalk.cyan("_on_HEL_message"));
218
+ }
219
+ assert(!this._helloReceived);
220
+ const stream = new BinaryStream(data);
221
+ const msgType = data.slice(0, 3).toString("ascii");
222
+
223
+ /* istanbul ignore next*/
224
+ if (doDebug) {
225
+ debugLog("SERVER received " + chalk.yellow(msgType));
226
+ debugLog("SERVER received " + hexDump(data));
227
+ }
228
+
229
+ if (msgType === "HEL") {
230
+ try {
231
+ assert(data.length >= 24);
232
+ const helloMessage = decodeMessage(stream, HelloMessage) as HelloMessage;
233
+ assert(isFinite(this.protocolVersion));
234
+
235
+ // OPCUA Spec 1.03 part 6 - page 41
236
+ // The Server shall always accept versions greater than what it supports.
237
+ if (helloMessage.protocolVersion !== this.protocolVersion) {
238
+ debugLog(
239
+ `warning ! client sent helloMessage.protocolVersion = ` +
240
+ ` 0x${helloMessage.protocolVersion.toString(16)} ` +
241
+ `whereas server protocolVersion is 0x${this.protocolVersion.toString(16)}`
242
+ );
243
+ }
244
+
245
+ if (helloMessage.protocolVersion === 0xdeadbeef || helloMessage.protocolVersion < this.protocolVersion) {
246
+ // Note: 0xDEADBEEF is our special version number to simulate BadProtocolVersionUnsupported in tests
247
+ // invalid protocol version requested by client
248
+ return this._abortWithError(
249
+ StatusCodes.BadProtocolVersionUnsupported,
250
+ "Protocol Version Error" + this.protocolVersion,
251
+ callback
252
+ );
253
+ }
254
+
255
+ // OPCUA Spec 1.04 part 6 - page 45
256
+ // UASC is designed to operate with different TransportProtocols that may have limited buffer
257
+ // sizes. For this reason, OPC UA Secure Conversation will break OPC UA Messages into several
258
+ // pieces (called ‘MessageChunks’) that are smaller than the buffer size allowed by the
259
+ // TransportProtocol. UASC requires a TransportProtocol buffer size that is at least 8 192 bytes
260
+ if (helloMessage.receiveBufferSize < minimumBufferSize || helloMessage.sendBufferSize < minimumBufferSize) {
261
+ return this._abortWithError(
262
+ StatusCodes.BadConnectionRejected,
263
+ "Buffer size too small (should be at least " + minimumBufferSize,
264
+ callback
265
+ );
266
+ }
267
+ // the helloMessage shall only be received once.
268
+ this._helloReceived = true;
269
+ this._send_ACK_response(helloMessage);
270
+ } catch (err) {
271
+ // connection rejected because of malformed message
272
+ return this._abortWithError(StatusCodes.BadConnectionRejected, err instanceof Error ? err.message: "", callback);
273
+ }
274
+ callback(); // no Error
275
+ } else {
276
+ // invalid packet , expecting HEL
277
+ /* istanbul ignore next*/
278
+ if (doDebug) {
279
+ debugLog(chalk.red("BadCommunicationError ") + "Expecting 'HEL' message to initiate communication");
280
+ }
281
+ this._abortWithError(StatusCodes.BadCommunicationError, "Expecting 'HEL' message to initiate communication", callback);
282
+ }
283
+ }
284
+ }