diff options
| author | Thomas Vanbesien <tvanbesi@proton.me> | 2026-02-17 19:06:22 +0100 |
|---|---|---|
| committer | Thomas Vanbesien <tvanbesi@proton.me> | 2026-02-17 19:06:22 +0100 |
| commit | 827e90e0daabe32e058e08dd2a253425898a7e7a (patch) | |
| tree | ecd3f31da63890ac029b7929eade88f38e078b3d /src/client_find_servers.c | |
| parent | e4ba24b3d24fdce36bc9dbd3c2c8f00b0ec23335 (diff) | |
| download | BobinkCOpcUa-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.c | 438 |
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; -} |
