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
|
# OPC UA Discovery
A small C project that demonstrates OPC UA server discovery using the
[open62541](https://www.open62541.org/) library. Three programs work together:
- **ServerLDS** — Local Discovery Server that other servers register with
- **ServerRegister** — a server that periodically registers itself with the LDS
- **Client** — queries the LDS for servers, lists endpoints, or reads the current time from a server
## Prerequisites
- CMake 4.0+
- A C11 compiler (GCC or Clang)
- OpenSSL development libraries (`libssl-dev` / `openssl-devel`)
- `openssl` CLI (for generating certificates)
## Getting started
Clone the repository with its submodule:
```sh
git clone --recursive https://git.tvcloud.fr/opcua_c
cd opcua_c
```
### Generate certificates
The programs use TLS certificates for mutual authentication. ServerLDS and
ServerRegister can also run without certificates (SecurityPolicy#None only) by
omitting the `certificate`, `privateKey`, and `trustStore` keys from their
config files.
For encrypted operation, four identities are needed. Each call to
`tools/generate_certificate.sh` creates a self-signed RSA-2048 certificate
(`<name>_cert.der`) and private key (`<name>_key.der`) in the given directory:
```sh
tools/generate_certificate.sh certs ServerLDS
tools/generate_certificate.sh certs ServerRegister
tools/generate_certificate.sh certs ServerRegisterClient
tools/generate_certificate.sh certs Client
```
| Identity | Used by | Purpose |
|----------|---------|---------|
| `ServerLDS` | ServerLDS | Server certificate for the LDS |
| `ServerRegister` | ServerRegister | Server certificate for the registering server |
| `ServerRegisterClient` | ServerRegister | Client certificate used when connecting to the LDS |
| `Client` | Client | Client certificate for all client operations |
### Populate the trust stores
OPC UA applications only accept connections from peers whose certificate is in
their trust store. Create the trust store directories and symlink each peer's
certificate:
```sh
mkdir -p certs/trust/{server_lds,server_register,server_register_client,client}
ln -s ../../ServerRegisterClient_cert.der ../../Client_cert.der \
certs/trust/server_lds/
ln -s ../../ServerLDS_cert.der ../../Client_cert.der \
certs/trust/server_register/
ln -s ../../ServerLDS_cert.der \
certs/trust/server_register_client/
ln -s ../../ServerLDS_cert.der ../../ServerRegister_cert.der \
certs/trust/client/
```
### Build
```sh
cmake -B build
cmake --build build --parallel
```
open62541 is fetched from the submodule and built automatically — the first
build takes a bit longer.
## Running
Start the programs in order, each in its own terminal, from the project root:
```sh
# 1. Local Discovery Server
build/ServerLDS config/server_lds.conf
# 2. Register Server (connects to the LDS on port 4840)
build/ServerRegister config/server_register.conf \
config/server_register_client.conf opc.tcp://localhost:4840
# 3. Find registered servers via the LDS
build/Client config/client.conf find-servers opc.tcp://localhost:4840
# 4. List endpoints on the registered server
build/Client config/client.conf get-endpoints opc.tcp://localhost:4841
# 5. Read the current time from the registered server
build/Client config/client.conf read-time opc.tcp://localhost:4841
```
All three programs accept an optional log level as the last argument
(`trace`, `debug`, `info`, `warning`, `error`, `fatal`). The default is `info`.
## Tests
Integration tests exercise three combinations of security and authentication:
| Test | Security | Auth |
|------|----------|------|
| `unsecure_anonymous` | None / None | anonymous |
| `secure_anonymous` | SignAndEncrypt / Basic256Sha256 | anonymous |
| `secure_user` | SignAndEncrypt / Basic256Sha256 | user/password |
Run all tests:
```sh
ctest --test-dir build --output-on-failure
```
### Memory leak check
Rebuild with AddressSanitizer, run the tests, then switch back:
```sh
# ASan build
cmake -B build \
-DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address"
cmake --build build --parallel
ASAN_OPTIONS="detect_leaks=1" ctest --test-dir build --output-on-failure
# Restore normal build
cmake -B build -DCMAKE_C_FLAGS="" -DCMAKE_EXE_LINKER_FLAGS=""
cmake --build build --parallel
```
## Configuration
Programs are configured through plain text files (`key = value`, one per line).
Example configs are in `config/`.
|