-
Notifications
You must be signed in to change notification settings - Fork 90
/
client_tls_test.go
584 lines (528 loc) · 21.4 KB
/
client_tls_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
package modbus
import (
"crypto/tls"
"crypto/x509"
"io"
"fmt"
"net"
"testing"
"time"
)
const (
// note: these certs and associated keys are self-signed
// and only meant to be used with this test.
// PLEASE DO NOT USE THEM FOR ANYTHING ELSE, EVER, as they
// do not provide any kind of security.
serverCert string = `-----BEGIN CERTIFICATE-----
MIIFkzCCA3ugAwIBAgIUWnvnN1r9czWyX7TGS+AwTNICd4wwDQYJKoZIhvcNAQEL
BQAwKTEnMCUGA1UEAwwebG9jYWxob3N0IFRFU1QgQ0VSVCBETyBOT1QgVVNFMB4X
DTIwMDgyMTA5NTEyMVoXDTQwMDgxNjA5NTEyMVowKTEnMCUGA1UEAwwebG9jYWxo
b3N0IFRFU1QgQ0VSVCBETyBOT1QgVVNFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEA4D+a4wqvwxhyMhN4Z6EG7pIU1TfL7hV2MH4Izx1sDHGaUu+318SE
Egn85Zn1PbYAvYqlN+Ti3pCH5/tSJJHD4XVGcXtp3Wswt5MTXX8Ny3f1v3ZeQggp
nTy2tyODTulCQBg5L+8FTgJM2mJR0D+dryswiWgDVBLxg5W9p7icff30n/LHtEGd
jTkVbkaG798iGaIIeI6YS1wjMfsPWGWpG9SVoC3bkHN2NL2apecCLsoZpb+DiKdT
1rBG2pNeDseGpSWKwF/2/HeJsw+tD4okbtfYA7uURmRyqv1rxAXmclZXHFHpUL8l
Vt69g+ER0sXmLavM2Jj3iss6RF2MP6ghVUAcaciPbuDCn6+vnxCE6L2Gyr9G6Aur
rOBl/nRj3BHK9agp0fLzhIKgfCKMzCU5mo/UFlJIKbKIRJcdF5LNF3A9wD0K3Rv/
2bIvaXdWwIgUQ+zX3V3cDuMatCs/F2jGE5FejGaNeA7ixfpdCtybBpzGewLB49NB
AIFBboJBdfW3QuqQBM32GFmbwM4cZpdxr97cZTDJh3Age7e8BSWPO195IJEKWNSn
bnWCDNG5J4G6MBf9AfC/ljJCrOIEN4wTXP6EF4vaMq/VWz674j7QvR9q9aKB1lNn
bdKd/LMH+jmgG8bGuy01Tj12/JBgzgG0KI72364wuJlTjneqkTpCncMCAwEAAaOB
sjCBrzAdBgNVHQ4EFgQUEFEWxaWofRhSTd2+ZWmaH14OKAswHwYDVR0jBBgwFoAU
EFEWxaWofRhSTd2+ZWmaH14OKAswDwYDVR0TAQH/BAUwAwEB/zAsBgNVHREEJTAj
gglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAGHBH8AAAEwCwYDVR0PBAQDAgWg
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
BQADggIBACYfWN0rK/OTXDZEQHLWLe6SDailqJSULVRhzxevo3i07MhsQCARofXH
A37b0q/GHhLsKUEsjB+TYip90eKaRV1XDgnMOdbQxVCKY5IVTmobnoj5ZLja1AgD
4I3CTxx3tqxLxRmV1Bre1tIqHnalA6kC6HJAKW+R0biPSccchIjW2ljB1SXBcS2T
RjAsIfbEl9sDhyLl8jaOaOBLPLS5PNgRs4XJ8/ps9dDCNyeOizGVzAsgaeTxXadC
Y505cxoWQR1atYerjqVr0XolCTkefOapsNJzH3YXF2mxKJCVxARv8Ns8e5WwIWw2
r1ESi6M1qca5CutEgbBdUp7NyF44HJ9O3EsG+CFO6XRn6aaUvmin6vufKk29usRm
L3RWqBH1vz3vQzVLfEzXnJwnxDwZWcBrGx3RjKAL+O+hWHc3Qh6+AfI0yRX4j0MR
7IMHESf2xkCtw58w1t+OA1GBZ7hBX4zRiAQ89hk8UzRMw45yQ3cPkAp9u+PhrY1i
9dcDqvPueaSDoRMl7VvHyQ+2SeQF7mc3Xx6iAm9HPBmuVWVpX32g9jbu0xfzWhng
DXf3U5zg6BsG3gR5omPwbApKBlGckRY+ZuarhxPeczBx6KVIOKgvafybKrCsbso2
oq2sBRSZveoEKZDOmZpsUP2jYrcgrybnurcoN6g1Chl28V5rNITd
-----END CERTIFICATE-----`
serverKey string = `-----BEGIN PRIVATE KEY-----
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
yAqUh6zMzzcee67KOWWZMTuPQuu1n/m1
-----END PRIVATE KEY-----`
clientCert string = `
-----BEGIN CERTIFICATE-----
MIIFXzCCA0egAwIBAgIUQzQeLPGsr6OmD5NtrsiYMFVwATwwDQYJKoZIhvcNAQEL
BQAwJjEkMCIGA1UEAwwbVEVTVCBDTElFTlQgQ0VSVCBETyBOT1QgVVNFMB4XDTIw
MDgyMTA5NTI0NVoXDTQwMDgxNjA5NTI0NVowJjEkMCIGA1UEAwwbVEVTVCBDTElF
TlQgQ0VSVCBETyBOT1QgVVNFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA5yBIvheS8d2T8lBn9BOsdi1mMmhUHyqxdx9YFgwIV0NYb9s3/J83Jf/Focob
DM4fdy7iuSECX8/KUoymQmn26ivzmI4iLJ0LsBbUhTzMO9lo82Vg18E4Ab4GQVMz
LWcxnt1wt2EYJ5nq72c1h9K27pIecDDZ9DtBD1j0d3cuoo8HIVzsUfZRp80H9b5H
WuY2nMPZC3jp6HlsVSHCbkuscs/d7dDGK4tsanmyqVfBNmJhNKvo2GwMM9vf82li
dh6OwrqUNeMXkDU8vQ/xMGWfZ+Xpu0vXx3pQ5SX3WVDZFMuk7/mMBZwUhnXh+yjC
R1MVIRJvjijnkFzSSXctoysl/Mrc3QW5QmPmoa8KVWL8pSc5oaMMdX9bXo9omPf1
XlmjKMvEUu2IbUQcaDFtVAKzR0UGcYEFh/QCIu7WV0pA24DfiX3r0Lv1GHHB6X+3
+zUH7ZcaajzVQM/crCB/VLQiZODg6EgFtx1woll5hES/I6l9Me7UKySlxY4wTjIk
k/cwIN6R0dHGDny30fIkOl0vseo6SKtIkApYfe2tAASatbNdpkJQbMjUkm1IlcYj
ZZdn7yssjLjmAWn6pz19GbW2sAGQ/D4k/rN2hOpCc3NSP2FMIeWwp6TUHUG2ZFck
79j/Fo2bPNTPiFWxUW6h6gWHDgZg8UFz3FXvaeUM0URFOjsCAwEAAaOBhDCBgTAd
BgNVHQ4EFgQUZkTFhQZ4vaia2hiIQrZnSfx2nOAwHwYDVR0jBBgwFoAUZkTFhQZ4
vaia2hiIQrZnSfx2nOAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCBaAwEwYD
VR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOC
AgEAhDVloT4TqZL66A/N8GSbiAALYVM4VoQlaiYiNrtwcNBAKrveN/RJlVxvYC9f
ybsvHz+wf/UAkWsQqd7pacKMlctNYoavpooO2ketQErpz+Ysmb+yGlL5u9DwQI36
bsw/sxZjA4uunEM3yySVZf2k5j97HzBhhv24dlgxCPyu5tOAj83bM2QLTc5H7/KZ
ZEhMcrXN0+QUI9np3WYPKAPMJNODSMGD8mMqpjRufxDH0jhPhX4R4qvhHT+/OrLE
CwLTwtgZ8BnRS2b16QEGpvT7bu5EWZda4vgXQEeuMpEgUmwPOm2JS9QZguXrhA6u
Jd/12gbNEowQCt0qig1K2/ouYc3YKvCq/GuDPZnVq0nXEgSom4+g4UpU92zHARSy
CjfEW+rD9ay0ipzl6wxV09ZoQOoFwztf/AO89gl2CDtcw1J+mB8KcP2Pme+lWZ9m
mj7+ed+lubE5kBIK/H2EojEUceGmdluqD/T6bUaAR6edLuS0z4MKFTNlbbZq9QiS
vb6vr137SqCw56gFvYzxxOS2037QHAHk9dZz4+ik6BLXOQmHY1s59y/iAV3CrWwf
wVi6BS05QtOQW1nzeUU4DyMz4aAuBs88iGqDlipzkMreyYTG/66WpKCp/nezSn5H
cufNpBGKcE0Ww/H/GgMvKe/nB7HEJQqoAxVDeq75WFiHQrs=
-----END CERTIFICATE-----
`
clientKey string = `-----BEGIN PRIVATE KEY-----
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
v8+pZi41bah3Pfqgp0Q9IkGcCk8JVnAbc0syYy0CggEATeNNedXh3DJmSG2ijOQX
Kbdb/asDErzFnWQd6RX/W6JG645KEfS1wo/9OBKEgIRANrP7wl3kXtxiu3EHZ5xD
obGAhSpHv6qdPvaNNIoBZvmf+I+0sNkQJ8BFTstZVslBZRsMv23D3vmNjgvUvKyr
Wa86tabN8H4ahnp4XYV4HtwTcdOqSy+Z72qcw83RWGj6owS3iOPDrCLEnihgibMd
9F726pWyyaU1Omnq4PjwEMUD67GFKBqeAQRtt2597LeNAAASB/HzGiXwPij71a2t
QijspXUDPzDwqAzI0D5tkSxT/+gNwL5ilpVQwx1bOdhOP6RoJVEnz83GYvsOBN+F
EQKCAQEAo9j9MG+VCz+loz4fUXIJjC63ypfuRfxTCAIBMn4HzohP8chEcQBlWLCH
t0WcguYnwsuxGR4Rhx02UZCx3qNxiroBZ9w1NqTk947ZjKuNzqI7IpIqvtJ18op6
QgQu8piNkf0/etAO0e6IjbZe4WfJCeKsAqE4vCV43baaSiHN/0pfYi6LLJ2YmTF/
+sYY43naHg3zQTL4JbL4c58ebe4ADj4wIdNJ+/H5JgQf6r14iNjpyc6BJOjFuPyx
EJHQKb6499HKFua3QuH/kA6Ogfm9o3Lnwx/VO1lPLFteTv1fBKK00C00SkmyIe1p
iaKCVjivzjP1s/q6adzOOZVlVwm7Xw==
-----END PRIVATE KEY-----
`
)
// TestTCPOVerTLSClient tests the TLS layer of the modbus client.
func TestTCPoverTLSClient(t *testing.T) {
var err error
var client *ModbusClient
var serverKeyPair tls.Certificate
var clientKeyPair tls.Certificate
var clientCp *x509.CertPool
var serverCp *x509.CertPool
var serverHostPort string
var serverChan chan string
var regs []uint16
serverChan = make(chan string)
// load server and client keypairs
serverKeyPair, err = tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
if err != nil {
t.Errorf("failed to load test server key pair: %v", err)
return
}
clientKeyPair, err = tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil {
t.Errorf("failed to load test client key pair: %v", err)
return
}
// start with an empty client cert pool initially to reject the server
// certificate
clientCp = x509.NewCertPool()
// start with an empty server cert pool initially to reject the client
// certificate
serverCp = x509.NewCertPool()
// start a mock modbus TLS server
go runMockTLSServer(t, serverKeyPair, serverCp, serverChan)
// wait for the test server goroutine to signal its readiness
// and network location
serverHostPort = <-serverChan
// attempt to create a client without specifying any TLS configuration
// parameter: should fail
client, err = NewClient(&ClientConfiguration{
URL: fmt.Sprintf("tcp+tls://%s", serverHostPort),
})
if err != ErrConfigurationError {
t.Errorf("NewClient() should have failed with %v, got: %v",
ErrConfigurationError, err)
}
// attempt to create a client without specifying any TLS server
// cert/CA: should fail
client, err = NewClient(&ClientConfiguration{
URL: fmt.Sprintf("tcp+tls://%s", serverHostPort),
TLSClientCert: &clientKeyPair,
})
if err != ErrConfigurationError {
t.Errorf("NewClient() should have failed with %v, got: %v",
ErrConfigurationError, err)
}
// attempt to create a client with both client cert+key and server
// cert/CA: should succeed
client, err = NewClient(&ClientConfiguration{
URL: fmt.Sprintf("tcp+tls://%s", serverHostPort),
TLSClientCert: &clientKeyPair,
TLSRootCAs: clientCp,
})
if err != nil {
t.Errorf("NewClient() should have succeeded, got: %v", err)
}
// connect to the server: should fail with a TLS error as the server cert
// is not yet trusted by the client
err = client.Open()
if err == nil {
t.Errorf("Open() should have failed")
}
// now load the server certificate into the client's trusted cert pool
// to get the client to accept the server's certificate
if !clientCp.AppendCertsFromPEM([]byte(serverCert)) {
t.Errorf("failed to load test server cert into cert pool")
}
// connect to the server: should succeed
// note: client certificates are verified after the handshake procedure
// has completed, so Open() won't fail even though the client cert
// is rejected by the server.
// (see RFC 8446 section 4.6.2 Post Handshake Authentication)
err = client.Open()
if err != nil {
t.Errorf("Open() should have succeeded, got: %v", err)
}
// attempt to read two registers: since the client cert won't pass
// the validation step yet (no cert in server cert pool),
// expect a tls error
regs, err = client.ReadRegisters(0x1000, 2, INPUT_REGISTER)
if err == nil {
t.Errorf("ReadRegisters() should have failed")
}
client.Close()
// now place the client cert in the server's authorized client list
// to get the client cert past the validation procedure
if !serverCp.AppendCertsFromPEM([]byte(clientCert)) {
t.Errorf("failed to load test client cert into cert pool")
}
// connect to the server: should succeed
err = client.Open()
if err != nil {
t.Errorf("Open() should have succeeded, got: %v", err)
}
// attempt to read two registers: should succeed
regs, err = client.ReadRegisters(0x1000, 2, INPUT_REGISTER)
if err != nil {
t.Errorf("ReadRegisters() should have succeeded, got: %v", err)
}
if regs[0] != 0x1234 {
t.Errorf("expected 0x1234 in 1st reg, saw: 0x%04x", regs[0])
}
if regs[1] != 0x5678 {
t.Errorf("expected 0x5678 in 2nd reg, saw: 0x%04x", regs[1])
}
// attempt to read another: should succeed
regs, err = client.ReadRegisters(0x1002, 1, HOLDING_REGISTER)
if err != nil {
t.Errorf("ReadRegisters() should have succeeded, got: %v", err)
}
if regs[0] != 0xaabb {
t.Errorf("expected 0xaabb in 1st reg, saw: 0x%04x", regs[0])
}
// close the connection: should succeed
err = client.Close()
if err != nil {
t.Errorf("Close() should have succeeded, got: %v", err)
}
return
}
func TestTLSClientOnServerTimeout(t *testing.T) {
var err error
var client *ModbusClient
var server *ModbusServer
var serverKeyPair tls.Certificate
var clientKeyPair tls.Certificate
var clientCp *x509.CertPool
var serverCp *x509.CertPool
var th *tlsTestHandler
var reg uint16
th = &tlsTestHandler{}
// load server and client keypairs
serverKeyPair, err = tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
if err != nil {
t.Errorf("failed to load test server key pair: %v", err)
return
}
clientKeyPair, err = tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil {
t.Errorf("failed to load test client key pair: %v", err)
return
}
// add those keypairs to their corresponding cert pool
clientCp = x509.NewCertPool()
if !clientCp.AppendCertsFromPEM([]byte(serverCert)) {
t.Errorf("failed to load test server cert into cert pool")
}
serverCp = x509.NewCertPool()
if !serverCp.AppendCertsFromPEM([]byte(clientCert)) {
t.Errorf("failed to load client cert into cert pool")
}
// load the server cert into the client CA cert pool to get the server cert
// accepted by clients
clientCp = x509.NewCertPool()
if !clientCp.AppendCertsFromPEM([]byte(serverCert)) {
t.Errorf("failed to load test server cert into cert pool")
}
server, err = NewServer(&ServerConfiguration{
URL: "tcp+tls://[::1]:5802",
MaxClients: 10,
TLSServerCert: &serverKeyPair,
TLSClientCAs: serverCp,
// disconnect idle clients after 500ms
Timeout: 500 * time.Millisecond,
}, th)
if err != nil {
t.Errorf("failed to create server: %v", err)
}
err = server.Start()
if err != nil {
t.Errorf("failed to start server: %v", err)
}
// create the modbus client
client, err = NewClient(&ClientConfiguration{
URL: "tcp+tls://localhost:5802",
TLSClientCert: &clientKeyPair,
TLSRootCAs: clientCp,
})
if err != nil {
t.Errorf("failed to create client: %v", err)
}
// connect to the server: should succeed
err = client.Open()
if err != nil {
t.Errorf("Open() should have succeeded, got: %v", err)
}
// write a value to register #3: should succeed
err = client.WriteRegister(3, 0x0199)
if err != nil {
t.Errorf("Write() should have succeeded, got: %v", err)
}
// attempt to read the value back: should succeed
reg, err = client.ReadRegister(3, HOLDING_REGISTER)
if err != nil {
t.Errorf("ReadRegisters() should have succeeded, got: %v", err)
}
if reg != 0x0199 {
t.Errorf("expected 0x0199 in reg #3, saw: 0x%04x", reg)
}
// pause for longer than the server's configured timeout to end up with
// an open client with a closed underlying TCP socket
time.Sleep(1 * time.Second)
// attempt a read: should fail
_, err = client.ReadRegister(3, INPUT_REGISTER)
if err == nil {
t.Errorf("ReadRegister() should have failed")
}
// cleanup
client.Close()
server.Stop()
return
}
// runMockTLSServer spins a test TLS server for use with TestTCPoverTLSClient.
func runMockTLSServer(t *testing.T, serverKeyPair tls.Certificate,
serverCp *x509.CertPool, serverChan chan string) {
var err error
var listener net.Listener
var sock net.Conn
var reqCount uint
var clientCount uint
var buf []byte
// let the OS pick an available port on the loopback interface
listener, err = tls.Listen("tcp", "localhost:0", &tls.Config{
// the server will use serverKeyPair (key+cert) to
// authenticate to the client
Certificates: []tls.Certificate{serverKeyPair},
// the server will use the certpool to authenticate the
// client-side cert
ClientCAs: serverCp,
// request client-side authentication and client cert validation
ClientAuth: tls.RequireAndVerifyClientCert,
})
if err != nil {
t.Errorf("failed to start test server listener: %v", err)
}
defer listener.Close()
// let the main test goroutine know which port the OS picked
serverChan <- listener.Addr().String()
for err == nil {
// accept client connections
sock, err = listener.Accept()
if err != nil {
t.Errorf("failed to accept client conn: %v", err)
break
}
// only proceed with clients passing the tls handshake
// note: this will reject any client whose cert does not pass the
// verification step
err = sock.(*tls.Conn).Handshake()
if err != nil {
sock.Close()
err = nil
continue
}
clientCount++
if clientCount > 2 {
t.Errorf("expected 2 client conns, saw: %v", clientCount)
}
// expect MBAP (modbus/tcp) messages inside the TLS tunnel
for {
// expect 12 bytes per request
buf = make([]byte, 12)
_, err = sock.Read(buf)
if err != nil {
// ignore EOF errors (clients disconnecting)
if err != io.EOF {
t.Errorf("failed to read client request: %v", err)
}
sock.Close()
break
}
reqCount++
switch reqCount {
case 1:
for i, b := range []byte{
0x00, 0x01, // txn id
0x00, 0x00, // protocol id
0x00, 0x06, // length
0x01, 0x04, // unit id + function code
0x10, 0x00, // start address
0x00, 0x02, // quantity
} {
if b != buf[i] {
t.Errorf("expected 0x%02x at pos %v, saw 0x%02x",
b, i, buf[i])
}
}
// send a reply
_, err = sock.Write([]byte{
0x00, 0x01, // txn id
0x00, 0x00, // protocol id
0x00, 0x07, // length
0x01, 0x04, // unit id + function code
0x04, // byte count
0x12, 0x34, // reg #0
0x56, 0x78, // reg #1
})
if err != nil {
t.Errorf("failed to write reply: %v", err)
}
case 2:
for i, b := range []byte{
0x00, 0x02, // txn id
0x00, 0x00, // protocol id
0x00, 0x06, // length
0x01, 0x03, // unit id + function code
0x10, 0x02, // start address
0x00, 0x01, // quantity
} {
if b != buf[i] {
t.Errorf("expected 0x%02x at pos %v, saw 0x%02x",
b, i, buf[i])
}
}
// send a reply
_, err = sock.Write([]byte{
0x00, 0x02, // txn id
0x00, 0x00, // protocol id
0x00, 0x05, // length
0x01, 0x03, // unit id + function code
0x02, // byte count
0xaa, 0xbb, // reg #0
})
if err != nil {
t.Errorf("failed to write reply: %v", err)
}
// stop the server after the 2nd request
listener.Close()
default:
t.Errorf("unexpected request id %v", reqCount)
return
}
}
}
}