@tier0/node-red-contrib-opcda-client 1.0.0 → 1.0.2

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.
@@ -0,0 +1,30 @@
1
+ const opcda = require('node-opc-da');
2
+ const { OPCServer } = opcda;
3
+ const { ComServer, Session, Clsid } = opcda.dcom;
4
+
5
+ async function test(domain) {
6
+ console.log('\n--- Testing with domain="' + domain + '" ---');
7
+ try {
8
+ var session = new Session();
9
+ session = session.createSession(domain, 'Administrator', 'Supos@1304');
10
+ session.setGlobalSocketTimeout(10000);
11
+
12
+ var comServer = new ComServer(
13
+ new Clsid('7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729'),
14
+ '192.168.31.75', session);
15
+
16
+ await comServer.init();
17
+ console.log('SUCCESS with domain="' + domain + '"');
18
+ await comServer.closeStub();
19
+ } catch (e) {
20
+ var code = typeof e === 'number' ? '0x' + (e >>> 0).toString(16) : (e.message || String(e));
21
+ console.log('FAILED:', code);
22
+ }
23
+ }
24
+
25
+ (async () => {
26
+ await test('DESKTOP-BBD7VBL');
27
+ await test('.');
28
+ await test('');
29
+ await test('WORKGROUP');
30
+ })();
package/test-direct.js ADDED
@@ -0,0 +1,53 @@
1
+ const opcda = require('/usr/src/node-red/node_modules/node-opc-da/src/index.js');
2
+ const dcom = require('node-dcom');
3
+ const { ComServer, Session, Clsid } = dcom;
4
+
5
+ const address = '192.168.31.75';
6
+ const domain = 'DESKTOP-BBD7VBL';
7
+ const username = 'Administrator';
8
+ const password = 'Supos@1304';
9
+ const clsidStr = '7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729';
10
+
11
+ async function main() {
12
+ console.log('=== OPC DA Direct Connection Test ===');
13
+
14
+ let comSession, comServer;
15
+ try {
16
+ comSession = new Session();
17
+ comSession = comSession.createSession(domain, username, password);
18
+ comSession.setGlobalSocketTimeout(15000);
19
+ console.log('Session: domain=' + comSession.getDomain() + ' user=' + comSession.getUserName());
20
+
21
+ comServer = new ComServer(new Clsid(clsidStr), address, comSession);
22
+ console.log('Connecting...');
23
+ await comServer.init();
24
+ console.log('Connected!');
25
+
26
+ const comObject = await comServer.createInstance();
27
+ console.log('Instance created');
28
+
29
+ const server = new opcda.OPCServer();
30
+ await server.init(comObject);
31
+ console.log('OPCServer initialized');
32
+
33
+ const status = await server.getStatus();
34
+ console.log('Server Status:', JSON.stringify(status, null, 2));
35
+
36
+ const browser = await server.getBrowser();
37
+ console.log('Got browser');
38
+
39
+ const items = await browser.browse();
40
+ console.log('Items found:', items.length);
41
+ if (items.length > 0) {
42
+ console.log('First 5 items:', items.slice(0, 5));
43
+ }
44
+
45
+ console.log('\n=== SUCCESS ===');
46
+ } catch (err) {
47
+ console.error('ERROR:', err.message || err);
48
+ console.error('Stack:', err.stack);
49
+ }
50
+ process.exit(0);
51
+ }
52
+
53
+ main();
@@ -0,0 +1,100 @@
1
+ // MS-NLMP Appendix A test vectors for NTLMv2
2
+ // Reference: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/
3
+ const Crypto = require('crypto');
4
+
5
+ // Force legacy provider for MD4
6
+ process.env.NODE_OPTIONS = '--openssl-legacy-provider';
7
+
8
+ const Responses = require('/usr/src/node-red/node_modules/node-dcom/dcom/rpc/security/responses.js');
9
+
10
+ const r = new Responses();
11
+
12
+ // MS-NLMP test vectors
13
+ const User = 'User';
14
+ const UserDom = 'Domain';
15
+ const Passwd = 'Password';
16
+ const ServerChallenge = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
17
+ const ClientChallenge = [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa];
18
+ const Time = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
19
+
20
+ // Expected values from MS-NLMP spec
21
+ const EXPECTED_NT_HASH = 'a4f49c406510bdcab6824ee7c30fd852';
22
+ const EXPECTED_NTLMv2_HASH = '0c868a403bfd7a93a3001ef22ef02e3f';
23
+
24
+ // Test 1: NT Hash (MD4 of UTF-16LE password)
25
+ console.log('=== Test 1: NT Hash ===');
26
+ const ntHash = r.ntlmHash(Passwd);
27
+ const ntHashHex = Buffer.from(ntHash).toString('hex');
28
+ console.log('Computed:', ntHashHex);
29
+ console.log('Expected:', EXPECTED_NT_HASH);
30
+ console.log('Match:', ntHashHex === EXPECTED_NT_HASH ? 'PASS' : 'FAIL');
31
+
32
+ // Test 2: NTLMv2 Hash
33
+ console.log('\n=== Test 2: NTLMv2 Hash ===');
34
+ const ntlmv2Hash = r.ntlmv2Hash(UserDom, User, Passwd);
35
+ const ntlmv2HashHex = Buffer.from(ntlmv2Hash).toString('hex');
36
+ console.log('Computed:', ntlmv2HashHex);
37
+ console.log('Expected:', EXPECTED_NTLMv2_HASH);
38
+ console.log('Match:', ntlmv2HashHex === EXPECTED_NTLMv2_HASH ? 'PASS' : 'FAIL');
39
+
40
+ // Test 3: LMv2 Response
41
+ console.log('\n=== Test 3: LMv2 Response ===');
42
+ const lmv2 = r.getLMv2Response(UserDom, User, Passwd, ServerChallenge, ClientChallenge);
43
+ const lmv2Hex = Buffer.from(lmv2).toString('hex');
44
+ console.log('Computed:', lmv2Hex);
45
+ // Expected from MS-NLMP: d6e6152ea25d03b7c6ba6629c2d6aaf0aaaaaaaaaaaaaaaa
46
+ const EXPECTED_LMv2 = 'd6e6152ea25d03b7c6ba6629c2d6aaf0aaaaaaaaaaaaaaaa';
47
+ console.log('Expected:', EXPECTED_LMv2);
48
+ console.log('Match:', lmv2Hex === EXPECTED_LMv2 ? 'PASS' : 'FAIL');
49
+
50
+ // Test 4: hmacMD5 argument order verification
51
+ console.log('\n=== Test 4: HMAC-MD5 argument order ===');
52
+ const testHmac = r.hmacMD5([0x01, 0x02, 0x03], [0x04, 0x05, 0x06]);
53
+ console.log('hmacMD5([1,2,3], [4,5,6]) =', Buffer.from(testHmac).toString('hex'));
54
+ // Verify: HMAC-MD5(key=040506, data=010203)
55
+ const verifyHmac = Crypto.createHmac('md5', Buffer.from([0x04, 0x05, 0x06]));
56
+ verifyHmac.update(Buffer.from([0x01, 0x02, 0x03]));
57
+ console.log('Crypto.HMAC(key=040506, data=010203) =', verifyHmac.digest('hex'));
58
+
59
+ // Test 5: NTLMv2 Response with known blob
60
+ console.log('\n=== Test 5: NTLMv2 Response ===');
61
+ // Build a minimal target info for testing
62
+ const targetInfo = [
63
+ 0x02, 0x00, 0x0c, 0x00, // MsvAvNbDomainName, len=12
64
+ 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, // "Domain" in UTF-16LE
65
+ 0x01, 0x00, 0x0c, 0x00, // MsvAvNbComputerName, len=12
66
+ 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, // "Server" in UTF-16LE
67
+ 0x00, 0x00, 0x00, 0x00 // MsvAvEOL
68
+ ];
69
+
70
+ // Test blob creation
71
+ const blob = r.createBlob(targetInfo, ClientChallenge);
72
+ console.log('Blob length:', blob.length);
73
+ console.log('Blob header (first 8):', Buffer.from(blob.slice(0, 8)).toString('hex'));
74
+ console.log('Blob timestamp (next 8):', Buffer.from(blob.slice(8, 16)).toString('hex'));
75
+ console.log('Blob clientNonce (next 8):', Buffer.from(blob.slice(16, 24)).toString('hex'));
76
+
77
+ // Verify blob structure
78
+ console.log('\nBlob structure check:');
79
+ console.log('Byte 0-1 (signature):', blob[0] === 1 && blob[1] === 1 ? 'PASS (0x0101)' : 'FAIL');
80
+ console.log('Byte 2-7 (zeros):', blob.slice(2, 8).every(b => b === 0) ? 'PASS' : 'FAIL');
81
+ console.log('Byte 16-23 (client nonce):', Buffer.from(blob.slice(16, 24)).toString('hex') === 'aaaaaaaaaaaaaaaa' ? 'PASS' : 'FAIL');
82
+ console.log('Byte 24-27 (Z4):', blob.slice(24, 28).every(b => b === 0) ? 'PASS' : 'FAIL');
83
+
84
+ // Verify the full NTLMv2 response computation
85
+ const retval = r.getNTLMv2Response(UserDom, User, Passwd, targetInfo, ServerChallenge, ClientChallenge);
86
+ console.log('\nNTLMv2Response length:', retval[0].length);
87
+ console.log('NTProofStr:', Buffer.from(retval[0].slice(0, 16)).toString('hex'));
88
+
89
+ // Test session base key
90
+ const ntProofStr = retval[0].slice(0, 16);
91
+ const hashForSession = r.ntlmv2Hash(UserDom, User, Passwd);
92
+ const sessionBaseKey = r.hmacMD5(ntProofStr, hashForSession);
93
+ console.log('SessionBaseKey:', Buffer.from(sessionBaseKey).toString('hex'));
94
+
95
+ // Now test with actual credentials
96
+ console.log('\n\n=== Test with actual credentials ===');
97
+ const actualNtHash = r.ntlmHash('Supos@1304');
98
+ console.log('NT Hash (Supos@1304):', Buffer.from(actualNtHash).toString('hex'));
99
+ const actualV2Hash = r.ntlmv2Hash('DESKTOP-BBD7VBL', 'Administrator', 'Supos@1304');
100
+ console.log('NTLMv2 Hash:', Buffer.from(actualV2Hash).toString('hex'));
@@ -0,0 +1,77 @@
1
+ const Crypto = require('crypto');
2
+
3
+ // Verify DES update() vs final() behavior in this Node.js version
4
+ console.log('=== Node.js version:', process.version, '===');
5
+ console.log('=== OpenSSL version:', process.versions.openssl, '===\n');
6
+
7
+ // Test 1: DES-ECB update() behavior
8
+ console.log('--- Test 1: DES-ECB update vs final ---');
9
+ const key1 = Buffer.from('0123456789ABCDEF', 'hex');
10
+ const data1 = Buffer.from('4E6F772069732074', 'hex');
11
+ const des1 = Crypto.createCipheriv('des-ecb', key1, '');
12
+ const update1 = des1.update(data1);
13
+ const final1 = des1.final();
14
+ console.log('update() returned', update1.length, 'bytes:', update1.toString('hex'));
15
+ console.log('final() returned', final1.length, 'bytes:', final1.toString('hex'));
16
+ console.log('Expected update=3fa40e8a984d4815 (8 bytes)\n');
17
+
18
+ // Test 2: Verify NT hash
19
+ console.log('--- Test 2: NT Hash of Supos@1304 ---');
20
+ const md4 = Crypto.createHash('md4');
21
+ md4.update(Buffer.from('Supos@1304', 'utf16le'));
22
+ const ntHash = md4.digest();
23
+ console.log('NT Hash:', ntHash.toString('hex'));
24
+ console.log('Length:', ntHash.length, '\n');
25
+
26
+ // Test 3: NTLM2 Session Response with known values
27
+ console.log('--- Test 3: Full NTLM2 Session Response ---');
28
+ const Responses = require('node-opc-da/node_modules/node-dcom/dcom/rpc/security/responses.js');
29
+ const r = new Responses();
30
+
31
+ const fakeChallenge = Buffer.from('0123456789abcdef', 'hex');
32
+ const fakeClientNonce = Buffer.from('ffffff0011223344', 'hex');
33
+ const testResponse = r.getNTLM2SessionResponse('Password', fakeChallenge, fakeClientNonce);
34
+ console.log('Response type:', typeof testResponse, testResponse.constructor.name);
35
+ console.log('Response length:', testResponse.length);
36
+ console.log('Response hex:', testResponse.toString('hex'));
37
+
38
+ // Independent verification of the same computation
39
+ const ntHash2 = Crypto.createHash('md4').update(Buffer.from('Password', 'utf16le')).digest();
40
+ console.log('\nIndependent NT hash:', ntHash2.toString('hex'));
41
+ const sessionNonce = Buffer.concat([fakeChallenge, fakeClientNonce]);
42
+ const sessionHash = Crypto.createHash('md5').update(sessionNonce).digest().slice(0, 8);
43
+ console.log('Session hash:', sessionHash.toString('hex'));
44
+
45
+ // Manual DES with NT hash
46
+ function createDESKey(bytes, offset) {
47
+ let keyBytes = bytes.slice(offset, 7 + offset);
48
+ let material = Buffer.alloc(8);
49
+ material[0] = keyBytes[0];
50
+ material[1] = ((keyBytes[0] << 7) & 0xff | ((keyBytes[1] & 0xff) >>> 1));
51
+ material[2] = ((keyBytes[1] << 6) & 0xff | ((keyBytes[2] & 0xff) >>> 2));
52
+ material[3] = ((keyBytes[2] << 5) & 0xff | ((keyBytes[3] & 0xff) >>> 3));
53
+ material[4] = ((keyBytes[3] << 4) & 0xff | ((keyBytes[4] & 0xff) >>> 4));
54
+ material[5] = ((keyBytes[4] << 3) & 0xff | ((keyBytes[5] & 0xff) >>> 5));
55
+ material[6] = ((keyBytes[5] << 2) & 0xff | ((keyBytes[6] & 0xff) >>> 6));
56
+ material[7] = ((keyBytes[6] << 1));
57
+ // odd parity
58
+ for (let i = 0; i < 8; i++) {
59
+ let b = material[i];
60
+ let needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
61
+ if (needsParity) material[i] |= 0x01;
62
+ else material[i] &= 0xfe;
63
+ }
64
+ return material;
65
+ }
66
+
67
+ const padded = Buffer.concat([ntHash2, Buffer.alloc(5)]);
68
+ let manualResult = Buffer.alloc(0);
69
+ for (let off of [0, 7, 14]) {
70
+ const k = createDESKey(padded, off);
71
+ const d = Crypto.createCipheriv('des-ecb', k, '');
72
+ const enc = d.update(sessionHash);
73
+ d.final();
74
+ manualResult = Buffer.concat([manualResult, enc]);
75
+ }
76
+ console.log('Manual response:', manualResult.toString('hex'));
77
+ console.log('Match:', testResponse.toString('hex') === manualResult.toString('hex') ? 'YES' : 'NO');
@@ -0,0 +1,204 @@
1
+ {
2
+ "UNS": [
3
+ {
4
+ "type": "path",
5
+ "name": "opcda",
6
+ "children": [
7
+ {
8
+ "type": "path",
9
+ "name": "通道_1",
10
+ "children": [
11
+ {
12
+ "type": "path",
13
+ "name": "设备_1",
14
+ "children": [
15
+ {
16
+ "dataType": "METRIC",
17
+ "displayName": "Metric",
18
+ "type": "path",
19
+ "name": "Metric",
20
+ "children": [
21
+ {
22
+ "topicType": "METRIC",
23
+ "generateDashboard": "TRUE",
24
+ "writeData": "FALSE",
25
+ "name": "标记_1",
26
+ "enableHistory": "TRUE",
27
+ "dataType": "TIME_SEQUENCE_TYPE",
28
+ "fields": [
29
+ {
30
+ "systemField": true,
31
+ "unique": true,
32
+ "type": "DATETIME",
33
+ "name": "timeStamp"
34
+ },
35
+ {
36
+ "tbValueName": "tag",
37
+ "systemField": true,
38
+ "unique": true,
39
+ "type": "LONG",
40
+ "name": "tag"
41
+ },
42
+ {
43
+ "index": "double_1",
44
+ "type": "FLOAT",
45
+ "name": "value"
46
+ },
47
+ {
48
+ "systemField": true,
49
+ "type": "LONG",
50
+ "name": "quality"
51
+ }
52
+ ],
53
+ "type": "topic",
54
+ "mockData": "FALSE"
55
+ },
56
+ {
57
+ "topicType": "METRIC",
58
+ "generateDashboard": "TRUE",
59
+ "writeData": "FALSE",
60
+ "name": "标记_2",
61
+ "enableHistory": "TRUE",
62
+ "dataType": "TIME_SEQUENCE_TYPE",
63
+ "fields": [
64
+ {
65
+ "systemField": true,
66
+ "unique": true,
67
+ "type": "DATETIME",
68
+ "name": "timeStamp"
69
+ },
70
+ {
71
+ "tbValueName": "tag",
72
+ "systemField": true,
73
+ "unique": true,
74
+ "type": "LONG",
75
+ "name": "tag"
76
+ },
77
+ {
78
+ "index": "double_1",
79
+ "type": "FLOAT",
80
+ "name": "value"
81
+ },
82
+ {
83
+ "systemField": true,
84
+ "type": "LONG",
85
+ "name": "quality"
86
+ }
87
+ ],
88
+ "type": "topic",
89
+ "mockData": "FALSE"
90
+ }
91
+ ]
92
+ }
93
+ ]
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "type": "path",
99
+ "name": "模拟器示例",
100
+ "children": [
101
+ {
102
+ "type": "path",
103
+ "name": "功能",
104
+ "children": [
105
+ {
106
+ "dataType": "METRIC",
107
+ "displayName": "Metric",
108
+ "type": "path",
109
+ "name": "Metric",
110
+ "children": [
111
+ {
112
+ "topicType": "METRIC",
113
+ "generateDashboard": "TRUE",
114
+ "writeData": "FALSE",
115
+ "name": "Sine1",
116
+ "enableHistory": "TRUE",
117
+ "dataType": "TIME_SEQUENCE_TYPE",
118
+ "fields": [
119
+ {
120
+ "systemField": true,
121
+ "unique": true,
122
+ "type": "DATETIME",
123
+ "name": "timeStamp"
124
+ },
125
+ {
126
+ "tbValueName": "tag",
127
+ "systemField": true,
128
+ "unique": true,
129
+ "type": "LONG",
130
+ "name": "tag"
131
+ },
132
+ {
133
+ "index": "double_1",
134
+ "type": "FLOAT",
135
+ "name": "value"
136
+ },
137
+ {
138
+ "systemField": true,
139
+ "type": "LONG",
140
+ "name": "quality"
141
+ }
142
+ ],
143
+ "type": "topic",
144
+ "mockData": "FALSE"
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ "type": "path",
154
+ "name": "_System",
155
+ "children": [
156
+ {
157
+ "dataType": "METRIC",
158
+ "displayName": "Metric",
159
+ "type": "path",
160
+ "name": "Metric",
161
+ "children": [
162
+ {
163
+ "topicType": "METRIC",
164
+ "generateDashboard": "TRUE",
165
+ "writeData": "FALSE",
166
+ "name": "_Time",
167
+ "enableHistory": "TRUE",
168
+ "dataType": "TIME_SEQUENCE_TYPE",
169
+ "fields": [
170
+ {
171
+ "systemField": true,
172
+ "unique": true,
173
+ "type": "DATETIME",
174
+ "name": "timeStamp"
175
+ },
176
+ {
177
+ "tbValueName": "tag",
178
+ "systemField": true,
179
+ "unique": true,
180
+ "type": "LONG",
181
+ "name": "tag"
182
+ },
183
+ {
184
+ "index": "double_1",
185
+ "type": "FLOAT",
186
+ "name": "value"
187
+ },
188
+ {
189
+ "systemField": true,
190
+ "type": "LONG",
191
+ "name": "quality"
192
+ }
193
+ ],
194
+ "type": "topic",
195
+ "mockData": "FALSE"
196
+ }
197
+ ]
198
+ }
199
+ ]
200
+ }
201
+ ]
202
+ }
203
+ ]
204
+ }