aboutsummaryrefslogtreecommitdiffstats
path: root/src/client_find_servers.c
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-02-17 19:06:22 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-02-17 19:06:22 +0100
commit827e90e0daabe32e058e08dd2a253425898a7e7a (patch)
treeecd3f31da63890ac029b7929eade88f38e078b3d /src/client_find_servers.c
parente4ba24b3d24fdce36bc9dbd3c2c8f00b0ec23335 (diff)
downloadBobinkCOpcUa-827e90e0daabe32e058e08dd2a253425898a7e7a.tar.gz
BobinkCOpcUa-827e90e0daabe32e058e08dd2a253425898a7e7a.zip
Replace ClientFindServers with unified Client, use trust store directories
Replace the single-purpose ClientFindServers program with a unified Client that supports three operations via CLI: find-servers, get-endpoints, and read-time. This simplifies the architecture by using one client binary with a single config file instead of a monolithic program that did everything in one run. Split the ServerRegister config into separate server and client config files so the LDS-registration credentials are isolated from the server's own settings. The discovery URL moves from config to a CLI argument. Replace repeated trustList config entries with a single trustStore directory path. Each program now points to a directory under certs/trust/ containing .der files, so adding or removing trust is a file-copy operation rather than editing every config file. Add loadTrustStore()/freeTrustStore() to common.c and remove the now-unused configGetAll() from the config parser. Simplify the test matrix from 6 to 4 cases (security and auth are orthogonal, so the full 3x2 matrix is unnecessary). Update run_test.sh to invoke the new Client three times and use port-polling instead of sleep.
Diffstat (limited to 'src/client_find_servers.c')
-rw-r--r--src/client_find_servers.c438
1 files changed, 0 insertions, 438 deletions
diff --git a/src/client_find_servers.c b/src/client_find_servers.c
deleted file mode 100644
index 2212026..0000000
--- a/src/client_find_servers.c
+++ /dev/null
@@ -1,438 +0,0 @@
-/**
- * @file client_find_servers.c
- * @brief OPC UA client that queries a Local Discovery Server for registered
- * servers.
- *
- * This program connects to an LDS and calls the FindServers service to
- * retrieve all registered servers. It then queries each server's endpoints
- * using the GetEndpoints service and displays the results in a human-readable
- * format.
- */
-
-#include "common.h"
-#include "config.h"
-
-#include <open62541/client_highlevel.h>
-#include <open62541/plugin/log_stdout.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-/* ========================================================================
- * Discovery Service Calls
- * ======================================================================== */
-
-/**
- * Calls the FindServers service on the LDS and prints all discovered servers.
- *
- * @param client The OPC UA client instance.
- * @param discoveryServerEndpoint The LDS endpoint URL.
- * @param applicationDescriptionArraySize Output: number of servers found.
- * @param applicationDescriptionArray Output: array of server descriptions.
- * @return UA_STATUSCODE_GOOD on success, error code otherwise.
- */
-static UA_StatusCode
-findServers (UA_Client *client, const char *discoveryServerEndpoint,
- size_t *applicationDescriptionArraySize,
- UA_ApplicationDescription **applicationDescriptionArray)
-{
- UA_StatusCode retval = UA_Client_findServers (
- client, discoveryServerEndpoint, 0, NULL, 0, NULL,
- applicationDescriptionArraySize, applicationDescriptionArray);
-
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
- "Could not call FindServers service. "
- "Is the discovery server started? StatusCode %s",
- UA_StatusCode_name (retval));
- return retval;
- }
-
- for (size_t i = 0; i < *applicationDescriptionArraySize; i++)
- printApplicationDescription (&(*applicationDescriptionArray)[i], i);
-
- return UA_STATUSCODE_GOOD;
-}
-
-/**
- * Queries endpoints for each discovered server using the GetEndpoints service.
- *
- * For each server in the applicationDescriptionArray, this function extracts
- * the first discovery URL and calls GetEndpoints to retrieve all available
- * endpoints. Results are logged via UA_LOG_INFO.
- *
- * @param client The OPC UA client instance.
- * @param applicationDescriptionArray Array of server descriptions from
- * FindServers.
- * @param applicationDescriptionArraySize Number of servers in the array.
- */
-static void
-getServersEndpoints (UA_Client *client,
- UA_ApplicationDescription *applicationDescriptionArray,
- size_t applicationDescriptionArraySize)
-{
- for (size_t i = 0; i < applicationDescriptionArraySize; i++)
- {
- UA_ApplicationDescription *description = &applicationDescriptionArray[i];
- if (description->discoveryUrlsSize == 0)
- {
- UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
- "[GetEndpoints] Server %.*s did not provide any "
- "discovery urls. Skipping.",
- (int)description->applicationUri.length,
- description->applicationUri.data);
- continue;
- }
-
- /* UA_String is not null-terminated; build a C string for the API. */
- char *discoveryUrl = (char *)UA_malloc (
- sizeof (char) * description->discoveryUrls[0].length + 1);
- memcpy (discoveryUrl, description->discoveryUrls[0].data,
- description->discoveryUrls[0].length);
- discoveryUrl[description->discoveryUrls[0].length] = '\0';
-
- UA_EndpointDescription *endpointArray = NULL;
- size_t endpointArraySize = 0;
- UA_StatusCode retval = UA_Client_getEndpoints (
- client, discoveryUrl, &endpointArraySize, &endpointArray);
- UA_free (discoveryUrl);
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_Client_disconnect (client);
- break;
- }
-
- UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_APPLICATION,
- "Endpoints for Server[%lu]: %.*s", (unsigned long)i,
- (int)description->applicationUri.length,
- description->applicationUri.data);
- for (size_t j = 0; j < endpointArraySize; j++)
- printEndpoint (&endpointArray[j], j);
-
- UA_Array_delete (endpointArray, endpointArraySize,
- &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
- }
-}
-
-/* ========================================================================
- * Node Reading
- * ======================================================================== */
-
-/**
- * Connects to each non-discovery server and reads the current time node.
- *
- * For each server that is not a DiscoveryServer, this function establishes a
- * secure session, reads the Server_ServerStatus_CurrentTime variable, prints
- * the result, and disconnects.
- *
- * @param client The OPC UA client instance.
- * @param applicationDescriptionArray Array of server descriptions from
- * FindServers.
- * @param applicationDescriptionArraySize Number of servers in the array.
- * @param username Username for session auth, or NULL for anonymous.
- * @param password Password for session auth (ignored when username is NULL).
- */
-static void
-readServerTime (UA_Client *client,
- UA_ApplicationDescription *applicationDescriptionArray,
- size_t applicationDescriptionArraySize, const char *username,
- const char *password)
-{
- for (size_t i = 0; i < applicationDescriptionArraySize; i++)
- {
- UA_ApplicationDescription *desc = &applicationDescriptionArray[i];
-
- if (desc->applicationType == UA_APPLICATIONTYPE_DISCOVERYSERVER)
- continue;
-
- if (desc->discoveryUrlsSize == 0)
- {
- UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
- "[ReadTime] Server %.*s has no discovery URLs. "
- "Skipping.",
- (int)desc->applicationUri.length,
- desc->applicationUri.data);
- continue;
- }
-
- /* UA_String is not null-terminated; build a C string for the API. */
- char *url = (char *)UA_malloc (desc->discoveryUrls[0].length + 1);
- memcpy (url, desc->discoveryUrls[0].data, desc->discoveryUrls[0].length);
- url[desc->discoveryUrls[0].length] = '\0';
-
- UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
- "Connecting to %s to read current time...", url);
-
- UA_StatusCode retval;
- if (username)
- retval = UA_Client_connectUsername (client, url, username, password);
- else
- retval = UA_Client_connect (client, url);
- UA_free (url);
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
- "Could not connect: %s", UA_StatusCode_name (retval));
- continue;
- }
-
- UA_Variant value;
- UA_Variant_init (&value);
-
- const UA_NodeId nodeId = UA_NS0ID (SERVER_SERVERSTATUS_CURRENTTIME);
- retval = UA_Client_readValueAttribute (client, nodeId, &value);
-
- if (retval == UA_STATUSCODE_GOOD
- && UA_Variant_hasScalarType (&value, &UA_TYPES[UA_TYPES_DATETIME]))
- {
- UA_DateTime raw_date = *(UA_DateTime *)value.data;
- UA_DateTimeStruct dts = UA_DateTime_toStruct (raw_date);
- UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_APPLICATION,
- "date is: %u-%u-%u %u:%u:%u.%03u", dts.day, dts.month,
- dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
- }
- else
- {
- UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
- "Could not read current time: %s",
- UA_StatusCode_name (retval));
- }
-
- UA_Variant_clear (&value);
- UA_Client_disconnect (client);
- }
-}
-
-/* ========================================================================
- * Main
- * ======================================================================== */
-
-int
-main (int argc, char **argv)
-{
- if (argc < 2 || argc > 3)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Usage: %s <config-file> [log-level]", argv[0]);
- return EXIT_FAILURE;
- }
-
- const char *logLevelStr = (argc == 3) ? argv[2] : "info";
- int logLevel = parseLogLevel (logLevelStr);
- if (logLevel < 0)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown log level: %s "
- "(expected trace, debug, info, warning, error, fatal)",
- logLevelStr);
- return EXIT_FAILURE;
- }
-
- Config cfg;
- if (configLoad (argv[1], &cfg) != 0)
- return EXIT_FAILURE;
-
- /* ---- Shared keys ---- */
-
- const char *discoveryServerEndpoint
- = configRequire (&cfg, "discoveryEndpoint", "ClientFindServers");
- const char *applicationUri
- = configRequire (&cfg, "applicationUri", "ClientFindServers");
-
- if (!discoveryServerEndpoint || !applicationUri)
- {
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- /* ---- Discovery-side config (LDS connection) ---- */
-
- const char *discCertPath
- = configRequire (&cfg, "discoveryCertificate", "ClientFindServers");
- const char *discKeyPath
- = configRequire (&cfg, "discoveryPrivateKey", "ClientFindServers");
- const char *discSecModeStr
- = configRequire (&cfg, "discoverySecurityMode", "ClientFindServers");
- const char *discSecPolStr
- = configRequire (&cfg, "discoverySecurityPolicy", "ClientFindServers");
- const char *discAuthMode
- = configRequire (&cfg, "discoveryAuthMode", "ClientFindServers");
-
- if (!discCertPath || !discKeyPath || !discSecModeStr || !discSecPolStr
- || !discAuthMode)
- {
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- UA_MessageSecurityMode discSecMode = parseSecurityMode (discSecModeStr);
- if (discSecMode == UA_MESSAGESECURITYMODE_INVALID)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown discovery security mode: %s", discSecModeStr);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- const char *discSecPolUri = resolveSecurityPolicyUri (discSecPolStr);
- if (!discSecPolUri)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown discovery security policy: %s", discSecPolStr);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- /* ---- Server-side config (connections to discovered servers) ---- */
-
- const char *srvCertPath
- = configRequire (&cfg, "serverCertificate", "ClientFindServers");
- const char *srvKeyPath
- = configRequire (&cfg, "serverPrivateKey", "ClientFindServers");
- const char *srvSecModeStr
- = configRequire (&cfg, "serverSecurityMode", "ClientFindServers");
- const char *srvSecPolStr
- = configRequire (&cfg, "serverSecurityPolicy", "ClientFindServers");
- const char *srvAuthMode
- = configRequire (&cfg, "serverAuthMode", "ClientFindServers");
-
- if (!srvCertPath || !srvKeyPath || !srvSecModeStr || !srvSecPolStr
- || !srvAuthMode)
- {
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- UA_MessageSecurityMode srvSecMode = parseSecurityMode (srvSecModeStr);
- if (srvSecMode == UA_MESSAGESECURITYMODE_INVALID)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown server security mode: %s", srvSecModeStr);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- const char *srvSecPolUri = resolveSecurityPolicyUri (srvSecPolStr);
- if (!srvSecPolUri)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown server security policy: %s", srvSecPolStr);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- /* ---- Server-side auth ---- */
-
- const char *srvUsername = NULL, *srvPassword = NULL;
-
- if (strcmp (srvAuthMode, "anonymous") == 0)
- {
- /* No credentials needed */
- }
- else if (strcmp (srvAuthMode, "user") == 0)
- {
- srvUsername
- = configRequire (&cfg, "serverUsername", "ClientFindServers");
- srvPassword
- = configRequire (&cfg, "serverPassword", "ClientFindServers");
- if (!srvUsername || !srvPassword)
- {
- configFree (&cfg);
- return EXIT_FAILURE;
- }
- }
- else
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown server auth mode: %s "
- "(expected 'anonymous' or 'user')",
- srvAuthMode);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- /* ---- Trust lists ---- */
-
- char **discTrustPaths = NULL;
- size_t discTrustSize = 0;
- configGetAll (&cfg, "discoveryTrustList", &discTrustPaths, &discTrustSize);
-
- char **srvTrustPaths = NULL;
- size_t srvTrustSize = 0;
- configGetAll (&cfg, "serverTrustList", &srvTrustPaths, &srvTrustSize);
-
- /* ---- Create discovery client (LDS) ---- */
-
- UA_Client *discoveryClient = UA_Client_new ();
- UA_StatusCode retval = createSecureClientConfig (
- UA_Client_getConfig (discoveryClient), applicationUri, discCertPath,
- discKeyPath, discTrustPaths, discTrustSize, discSecMode, discSecPolUri);
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_Client_delete (discoveryClient);
- free (discTrustPaths);
- free (srvTrustPaths);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
- UA_Client_getConfig (discoveryClient)->logging->context
- = (void *)(uintptr_t)logLevel;
-
- /* ---- Create server client (discovered servers) ---- */
-
- UA_Client *serverClient = UA_Client_new ();
- retval = createSecureClientConfig (
- UA_Client_getConfig (serverClient), applicationUri, srvCertPath,
- srvKeyPath, srvTrustPaths, srvTrustSize, srvSecMode, srvSecPolUri);
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_Client_delete (discoveryClient);
- UA_Client_delete (serverClient);
- free (discTrustPaths);
- free (srvTrustPaths);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
- UA_Client_getConfig (serverClient)->logging->context
- = (void *)(uintptr_t)logLevel;
-
- /* ---- Discovery calls (use discoveryClient) ---- */
-
- UA_ApplicationDescription *applicationDescriptionArray = NULL;
- size_t applicationDescriptionArraySize = 0;
-
- retval = findServers (discoveryClient, discoveryServerEndpoint,
- &applicationDescriptionArraySize,
- &applicationDescriptionArray);
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_Client_delete (discoveryClient);
- UA_Client_delete (serverClient);
- free (discTrustPaths);
- free (srvTrustPaths);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- getServersEndpoints (discoveryClient, applicationDescriptionArray,
- applicationDescriptionArraySize);
-
- /* ---- Server calls (use serverClient) ---- */
-
- readServerTime (serverClient, applicationDescriptionArray,
- applicationDescriptionArraySize, srvUsername, srvPassword);
-
- /* ---- Cleanup ---- */
-
- UA_Client_delete (discoveryClient);
- UA_Client_delete (serverClient);
- UA_Array_delete (applicationDescriptionArray,
- applicationDescriptionArraySize,
- &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
- free (discTrustPaths);
- free (srvTrustPaths);
- configFree (&cfg);
-
- return EXIT_SUCCESS;
-}