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
|
# CLAUDE.md — OPC UA Discovery Project
## Project Overview
C project exploring OPC UA discovery using the open62541 library (git submodule at `deps/open62541`). CMake build system (C11). Source files live in `src/`, certificates in `certs/`, helper scripts in `tools/`.
## Build
```sh
cmake --build build --parallel
```
If the build directory doesn't exist yet or dependencies need reconfiguring:
```sh
cmake -B build
cmake --build build --parallel
```
open62541 is built automatically via `cmake/BuildDeps.cmake` with `UA_ENABLE_ENCRYPTION=OPENSSL` and `UA_ENABLE_DISCOVERY=ON`, installed to `build/deps/open62541-install`. The open62541 build directory is `build/deps/open62541`.
After building, verify that `compile_commands.json` is symlinked from `build/` to the project root.
### Building Documentation
To build the open62541 HTML documentation (requires `python3-sphinx`, `python3-sphinx-rtd-theme`, `graphviz`):
```sh
cmake -B build -DBUILD_DOC=ON
cmake --build build --target doc
```
Output goes to `build/deps/open62541/doc/index.html`.
## Code Style
- Follow the `.clang-format` file in the project root (GNU-based style).
- Key points: 2-space indent, braces on own line (`BreakBeforeBraces: GNU`), space before parens (`SpaceBeforeParens: Always`), pointer star on the right (`PointerAlignment: Right`), 79-column limit, return type on its own line for definitions.
- Do **not** reformat code you didn't change.
## Workflow Preferences
- **Ask before committing.** Never commit without explicit confirmation.
- **Ask when ambiguous.** If a task or requirement is unclear, ask rather than guess.
- **Plan mode:** Ask before entering plan mode — don't assume it's needed.
- **Validation:** After making changes, confirm the project compiles with `cmake --build build --parallel`. When asked to run or test, start all three programs (LDS, ServerRegister, ClientFindServers) using the commands in the **Running** section below, then verify the client output.
- **Verbosity:** Give detailed explanations of what was done and why.
## Certificates
Generate DER certificates with `tools/generate_certificate.sh <certs_dir> <name>`. This creates `<name>_cert.der` and `<name>_key.der` in the given directory. Four identities are needed:
```sh
tools/generate_certificate.sh certs ServerLDS
tools/generate_certificate.sh certs ServerRegister
tools/generate_certificate.sh certs ServerRegisterClient
tools/generate_certificate.sh certs ClientFindServers
```
Existing certs live in `certs/`. Only regenerate if missing.
## Running
Each program takes a single argument: a configuration file. Example config files are in `config/`. Start programs in order in separate terminals from the project root:
**1. Local Discovery Server (LDS)**
```sh
build/ServerLDS config/server_lds.conf
```
**2. Register Server**
```sh
build/ServerRegister config/server_register.conf
```
**3. Find Servers Client**
```sh
build/ClientFindServers config/client_find_servers.conf
```
## Configuration Files
Config files use a simple `key = value` format, one entry per line. Lines starting with `#` are comments. Blank lines are ignored. Repeated keys are used for list values (e.g. multiple `trustList` entries).
### ServerLDS Keys
| Key | Required | Description |
|-----|----------|-------------|
| `port` | yes | Server port number |
| `applicationUri` | yes | OPC UA application URI |
| `certificate` | yes | Path to server certificate (.der) |
| `privateKey` | yes | Path to server private key (.der) |
| `cleanupTimeout` | yes | Seconds before stale registrations are removed (must be > 10) |
| `authMode` | yes | `anonymous` or `user` |
| `username` | if user | Username for authentication |
| `password` | if user | Password for authentication |
| `trustList` | no | Trusted certificate path (repeat for multiple) |
### ServerRegister Keys
| Key | Required | Description |
|-----|----------|-------------|
| `port` | yes | Server port number |
| `applicationUri` | yes | OPC UA application URI |
| `serverCertificate` | yes | Path to server certificate (.der) |
| `serverPrivateKey` | yes | Path to server private key (.der) |
| `clientCertificate` | yes | Path to client certificate for LDS connection (.der) |
| `clientPrivateKey` | yes | Path to client private key for LDS connection (.der) |
| `discoveryEndpoint` | yes | LDS endpoint URL (e.g. `opc.tcp://localhost:4840`) |
| `registerInterval` | yes | Seconds between re-registrations |
| `securityMode` | yes | `None`, `Sign`, or `SignAndEncrypt` |
| `securityPolicy` | yes | `None`, `Basic256Sha256`, `Aes256_Sha256_RsaPss`, `Aes128_Sha256_RsaOaep`, or `ECC_nistP256` |
| `serverAuthMode` | yes | Auth for clients connecting to this server: `anonymous` or `user` |
| `serverUsername` | if user | Server-side username |
| `serverPassword` | if user | Server-side password |
| `clientAuthMode` | yes | Auth for LDS connection: `anonymous` or `user` |
| `clientUsername` | if user | Client-side username (for LDS) |
| `clientPassword` | if user | Client-side password (for LDS) |
| `trustList` | no | Trusted certificate path (repeat for multiple) |
### ClientFindServers Keys
| Key | Required | Description |
|-----|----------|-------------|
| `discoveryEndpoint` | yes | LDS endpoint URL (e.g. `opc.tcp://localhost:4840`) |
| `applicationUri` | yes | OPC UA application URI |
| `certificate` | yes | Path to client certificate (.der) |
| `privateKey` | yes | Path to client private key (.der) |
| `securityMode` | yes | `None`, `Sign`, or `SignAndEncrypt` |
| `securityPolicy` | yes | `None`, `Basic256Sha256`, `Aes256_Sha256_RsaPss`, `Aes128_Sha256_RsaOaep`, or `ECC_nistP256` |
| `authMode` | yes | `anonymous` or `user` |
| `username` | if user | Username for session auth |
| `password` | if user | Password for session auth |
| `trustList` | no | Trusted certificate path (repeat for multiple) |
## Testing
Integration tests use CTest and cover 6 combinations of security mode/policy and authentication:
| Test | Security | Auth |
|------|----------|------|
| `none_anon` | None / None | anonymous |
| `none_user` | None / None | user |
| `basic256sha256_anon` | SignAndEncrypt / Basic256Sha256 | anonymous |
| `basic256sha256_user` | SignAndEncrypt / Basic256Sha256 | user |
| `aes128_anon` | SignAndEncrypt / Aes128_Sha256_RsaOaep | anonymous |
| `aes128_user` | SignAndEncrypt / Aes128_Sha256_RsaOaep | user |
Run all tests:
```sh
ctest --test-dir build --output-on-failure
```
Each test starts ServerLDS (port 14840) and ServerRegister (port 14841), runs ClientFindServers, then validates:
1. Client exits with code 0
2. FindServers output contains `urn:bobink.ServerRegister`
3. Client read the current time (`date is:`)
4. Endpoint listing contains the expected security policy
To add a new test case: create a directory under `tests/` with 3 config files (`server_lds.conf`, `server_register.conf`, `client_find_servers.conf`), then add a `"name;ExpectedPolicy"` entry to the `INTEGRATION_TESTS` list in `CMakeLists.txt`.
## Project Structure
| Path | Purpose |
|------|---------|
| `src/common.h` / `src/common.c` | Shared helpers: `loadFile()`, `createSecureServer()`, `createSecureClientConfig()`, `parseSecurityMode()`, `resolveSecurityPolicyUri()` |
| `src/config.h` / `src/config.c` | Config file parser: `configLoad()`, `configGet()`, `configRequire()`, `configRequireInt()`, `configGetAll()`, `configFree()` |
| `src/server_lds.c` | Local Discovery Server |
| `src/server_register.c` | Server that registers with LDS |
| `src/client_find_servers.c` | Client that queries LDS and displays endpoints |
| `config/` | Example configuration files for all three programs |
| `tests/` | Integration tests: `run_test.sh` helper and per-test config directories |
| `certs/` | TLS certificates for server, client, and LDS |
| `tools/` | Helper scripts |
| `cmake/BuildDeps.cmake` | Configures, builds, and installs open62541 |
| `deps/open62541` | open62541 git submodule |
|