From 7648a256d97abda40edbdc0d7bf59edd0a09fb95 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Tue, 17 Feb 2026 23:52:06 +0100 Subject: Extract createServer and parseAuthConfig, simplify programs Rename createSecureServer to createServer and add an unsecure path (UA_ServerConfig_setMinimal) when certPath is NULL, eliminating the if/else server creation blocks in server_lds.c and server_register.c. Add parseAuthConfig() to common.c to replace four near-identical authMode parsing blocks across the three programs. Restructure server_register.c error handling with goto cleanup, removing ~20 duplicated cleanup sequences. Rename the CMake library target from DiscoveryCommon to common. --- src/client.c | 35 +------- src/common.c | 85 +++++++++++++++----- src/common.h | 46 ++++++++--- src/server_lds.c | 70 ++++------------ src/server_register.c | 217 +++++++++++--------------------------------------- 5 files changed, 163 insertions(+), 290 deletions(-) (limited to 'src') diff --git a/src/client.c b/src/client.c index 8234963..f03a337 100644 --- a/src/client.c +++ b/src/client.c @@ -233,38 +233,11 @@ main (int argc, char **argv) const char *username = NULL, *password = NULL; - if (op == OP_READ_TIME) + if (op == OP_READ_TIME + && parseAuthConfig (&cfg, "Client", NULL, &username, &password) != 0) { - const char *authMode = configRequire (&cfg, "authMode", "Client"); - if (!authMode) - { - configFree (&cfg); - return EXIT_FAILURE; - } - - if (strcmp (authMode, "anonymous") == 0) - { - /* No credentials needed. */ - } - else if (strcmp (authMode, "user") == 0) - { - username = configRequire (&cfg, "username", "Client"); - password = configRequire (&cfg, "password", "Client"); - if (!username || !password) - { - configFree (&cfg); - return EXIT_FAILURE; - } - } - else - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Unknown auth mode: %s " - "(expected 'anonymous' or 'user')", - authMode); - configFree (&cfg); - return EXIT_FAILURE; - } + configFree (&cfg); + return EXIT_FAILURE; } /* ---- Trust store ---- */ diff --git a/src/common.c b/src/common.c index 568e4d0..8d7d651 100644 --- a/src/common.c +++ b/src/common.c @@ -27,7 +27,7 @@ loadFile (const char *const path) FILE *fp = fopen (path, "rb"); if (!fp) { - /* fopen sets errno on failure. Callers like createSecureServer use + /* fopen sets errno on failure. Callers like createServer use loadFile for optional trustlist entries where a missing file is not an error. Clear errno so open62541's logging does not pick up a stale value and emit misleading error messages. */ @@ -153,7 +153,7 @@ freeTrustStore (char **paths, size_t size) } /* ======================================================================== - * Security Helpers + * Parsing Helpers * ======================================================================== */ int @@ -176,6 +176,42 @@ parseLogLevel (const char *name) return -1; } +int +parseAuthConfig (const Config *cfg, const char *program, + UA_Boolean *allowAnonymous, const char **username, + const char **password) +{ + const char *authMode = configRequire (cfg, "authMode", program); + if (!authMode) + return -1; + + *username = NULL; + *password = NULL; + + if (strcmp (authMode, "anonymous") == 0) + { + if (allowAnonymous) + *allowAnonymous = true; + return 0; + } + + if (strcmp (authMode, "user") == 0) + { + if (allowAnonymous) + *allowAnonymous = false; + *username = configRequire (cfg, "username", program); + *password = configRequire (cfg, "password", program); + if (!*username || !*password) + return -1; + return 0; + } + + UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "%s: unknown auth mode '%s' (expected 'anonymous' or 'user')", + program, authMode); + return -1; +} + UA_MessageSecurityMode parseSecurityMode (const char *name) { @@ -308,29 +344,36 @@ printEndpoint (const UA_EndpointDescription *endpoint, size_t index) * ======================================================================== */ UA_Server * -createSecureServer (UA_UInt16 port, const char *applicationUri, - const char *certPath, const char *keyPath, - char **trustPaths, size_t trustSize, UA_StatusCode *retval) +createServer (UA_UInt16 port, const char *applicationUri, const char *certPath, + const char *keyPath, char **trustPaths, size_t trustSize, + UA_StatusCode *retval) { - UA_ByteString certificate = loadFile (certPath); - UA_ByteString privateKey = loadFile (keyPath); - - /* +1: UA_STACKARRAY requires a strictly positive size for VLA. */ - UA_STACKARRAY (UA_ByteString, trustList, trustSize + 1); - for (size_t i = 0; i < trustSize; i++) - trustList[i] = loadFile (trustPaths[i]); - UA_Server *server = UA_Server_new (); UA_ServerConfig *config = UA_Server_getConfig (server); - *retval = UA_ServerConfig_setDefaultWithSecurityPolicies ( - config, port, &certificate, &privateKey, trustList, trustSize, NULL, 0, - NULL, 0); - - UA_ByteString_clear (&certificate); - UA_ByteString_clear (&privateKey); - for (size_t i = 0; i < trustSize; i++) - UA_ByteString_clear (&trustList[i]); + if (certPath) + { + UA_ByteString certificate = loadFile (certPath); + UA_ByteString privateKey = loadFile (keyPath); + + /* +1: UA_STACKARRAY requires a strictly positive size for VLA. */ + UA_STACKARRAY (UA_ByteString, trustList, trustSize + 1); + for (size_t i = 0; i < trustSize; i++) + trustList[i] = loadFile (trustPaths[i]); + + *retval = UA_ServerConfig_setDefaultWithSecurityPolicies ( + config, port, &certificate, &privateKey, trustList, trustSize, NULL, + 0, NULL, 0); + + UA_ByteString_clear (&certificate); + UA_ByteString_clear (&privateKey); + for (size_t i = 0; i < trustSize; i++) + UA_ByteString_clear (&trustList[i]); + } + else + { + *retval = UA_ServerConfig_setMinimal (config, port, NULL); + } if (*retval != UA_STATUSCODE_GOOD) { diff --git a/src/common.h b/src/common.h index 7290181..b4bd323 100644 --- a/src/common.h +++ b/src/common.h @@ -15,6 +15,8 @@ #include +#include "config.h" + /** * @brief Loads a DER-encoded certificate or key file into a UA_ByteString. * @@ -47,26 +49,26 @@ int loadTrustStore (const char *dirPath, char ***outPaths, size_t *outSize); void freeTrustStore (char **paths, size_t size); /** - * @brief Creates a UA_Server configured with security policies and encryption. + * @brief Creates a UA_Server, optionally configured with security policies. * - * The server is initialized with the specified port, certificate, private key, - * and trustlist. The applicationUri is set in the server's application - * description. + * When @p certPath is non-NULL the server is initialized with encryption + * (certificate, private key, trustlist). When @p certPath is NULL the server + * runs with SecurityPolicy#None only (keyPath and trustPaths are ignored). + * The applicationUri is set in both cases. * * @param port Server port number. * @param applicationUri OPC UA application URI. - * @param certPath Path to server certificate (.der). - * @param keyPath Path to private key (.der). - * @param trustPaths Array of trustlist file paths (may be NULL if trustSize is - * 0). + * @param certPath Path to server certificate (.der), or NULL for unsecure. + * @param keyPath Path to private key (.der), or NULL when certPath is NULL. + * @param trustPaths Array of trustlist file paths (may be NULL). * @param trustSize Number of entries in trustPaths. * @param retval Output parameter set to the status code on failure. * @return A configured UA_Server, or NULL on error. */ -UA_Server *createSecureServer (UA_UInt16 port, const char *applicationUri, - const char *certPath, const char *keyPath, - char **trustPaths, size_t trustSize, - UA_StatusCode *retval); +UA_Server *createServer (UA_UInt16 port, const char *applicationUri, + const char *certPath, const char *keyPath, + char **trustPaths, size_t trustSize, + UA_StatusCode *retval); /** * @brief Parses a log-level name into the corresponding UA_LogLevel value. @@ -79,6 +81,26 @@ UA_Server *createSecureServer (UA_UInt16 port, const char *applicationUri, */ int parseLogLevel (const char *name); +/** + * @brief Parses the authMode key from a configuration file. + * + * When authMode is "anonymous", sets *allowAnonymous to true and + * *username / *password to NULL. When authMode is "user", sets + * *allowAnonymous to false and loads the username/password keys. + * Logs errors internally. + * + * @param cfg Parsed configuration. + * @param program Program name (for error messages). + * @param allowAnonymous Output: true for anonymous, false for user. + * May be NULL (ignored — useful for client callers). + * @param username Output: username string (owned by cfg), or NULL. + * @param password Output: password string (owned by cfg), or NULL. + * @return 0 on success, -1 on error. + */ +int parseAuthConfig (const Config *cfg, const char *program, + UA_Boolean *allowAnonymous, const char **username, + const char **password); + /** * @brief Parses a security mode name into the corresponding enum value. * diff --git a/src/server_lds.c b/src/server_lds.c index e3407d5..0a1bf89 100644 --- a/src/server_lds.c +++ b/src/server_lds.c @@ -16,11 +16,9 @@ #include #include #include -#include #include #include -#include UA_Boolean running = true; @@ -63,9 +61,8 @@ main (int argc, char *argv[]) const char *applicationUri = configRequire (&cfg, "applicationUri", "ServerLDS"); int cleanupTimeout = configRequireInt (&cfg, "cleanupTimeout", "ServerLDS"); - const char *authMode = configRequire (&cfg, "authMode", "ServerLDS"); - if (!applicationUri || !authMode || port < 0 || cleanupTimeout < 0) + if (!applicationUri || port < 0 || cleanupTimeout < 0) { configFree (&cfg); return EXIT_FAILURE; @@ -102,67 +99,30 @@ main (int argc, char *argv[]) UA_Boolean allowAnonymous; const char *username = NULL, *password = NULL; - - if (strcmp (authMode, "anonymous") == 0) - { - allowAnonymous = true; - } - else if (strcmp (authMode, "user") == 0) - { - allowAnonymous = false; - username = configRequire (&cfg, "username", "ServerLDS"); - password = configRequire (&cfg, "password", "ServerLDS"); - if (!username || !password) - { - configFree (&cfg); - return EXIT_FAILURE; - } - } - else + if (parseAuthConfig (&cfg, "ServerLDS", &allowAnonymous, &username, + &password) + != 0) { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Unknown auth mode: %s " - "(expected 'anonymous' or 'user')", - authMode); configFree (&cfg); return EXIT_FAILURE; } char **trustPaths = NULL; size_t trustSize = 0; - UA_StatusCode retval; - UA_Server *server; - - if (secure) + if (secure && loadTrustStore (trustStore, &trustPaths, &trustSize) != 0) { - if (loadTrustStore (trustStore, &trustPaths, &trustSize) != 0) - { - configFree (&cfg); - return EXIT_FAILURE; - } - server = createSecureServer ((UA_UInt16)port, applicationUri, certPath, - keyPath, trustPaths, trustSize, &retval); - if (!server) - { - freeTrustStore (trustPaths, trustSize); - configFree (&cfg); - return EXIT_FAILURE; - } + configFree (&cfg); + return EXIT_FAILURE; } - else + + UA_StatusCode retval; + UA_Server *server = createServer ((UA_UInt16)port, applicationUri, certPath, + keyPath, trustPaths, trustSize, &retval); + if (!server) { - server = UA_Server_new (); - UA_ServerConfig *config = UA_Server_getConfig (server); - retval = UA_ServerConfig_setMinimal (config, (UA_UInt16)port, NULL); - if (retval != UA_STATUSCODE_GOOD) - { - UA_Server_delete (server); - configFree (&cfg); - return EXIT_FAILURE; - } - UA_String_clear (&config->applicationDescription.applicationUri); - config->applicationDescription.applicationUri - = UA_String_fromChars (applicationUri); + freeTrustStore (trustPaths, trustSize); + configFree (&cfg); + return EXIT_FAILURE; } UA_ServerConfig *serverConfig = UA_Server_getConfig (server); diff --git a/src/server_register.c b/src/server_register.c index cea7124..80bed20 100644 --- a/src/server_register.c +++ b/src/server_register.c @@ -2,10 +2,10 @@ * @file server_register.c * @brief OPC UA Server that registers with a Local Discovery Server. * - * This program runs an OPC UA server configured with security and periodically - * registers itself with a remote LDS using the RegisterServer2 service. It - * uses separate certificate pairs for the server and for the client connection - * to the LDS. On shutdown, it deregisters from the LDS. + * This program runs an OPC UA server that periodically registers itself with + * a remote LDS using the RegisterServer2 service. Encryption is optional for + * the server; the client connection to the LDS uses a separate certificate + * pair. On shutdown, it deregisters from the LDS. */ #include "common.h" @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -66,23 +65,26 @@ main (int argc, char **argv) /* ── Load server config ─────────────────────────────────────── */ - Config serverCfg; + int rc = EXIT_FAILURE; + Config serverCfg = { 0 }; + Config clientCfg = { 0 }; + char **serverTrustPaths = NULL; + size_t serverTrustSize = 0; + char **clientTrustPaths = NULL; + size_t clientTrustSize = 0; + UA_Server *server = NULL; + if (configLoad (argv[1], &serverCfg) != 0) - return EXIT_FAILURE; + goto cleanup; int port = configRequireInt (&serverCfg, "port", "ServerRegister"); const char *applicationUri = configRequire (&serverCfg, "applicationUri", "ServerRegister"); int registerInterval = configRequireInt (&serverCfg, "registerInterval", "ServerRegister"); - const char *serverAuthMode - = configRequire (&serverCfg, "authMode", "ServerRegister"); - if (!applicationUri || !serverAuthMode || port < 0 || registerInterval < 0) - { - configFree (&serverCfg); - return EXIT_FAILURE; - } + if (!applicationUri || port < 0 || registerInterval < 0) + goto cleanup; /* Security configuration (optional). When certificate, privateKey, and trustStore are all omitted the server runs with SecurityPolicy#None @@ -99,62 +101,25 @@ main (int argc, char **argv) "Incomplete server security config: certificate, " "privateKey, and trustStore must all be set, or all " "omitted"); - configFree (&serverCfg); - return EXIT_FAILURE; + goto cleanup; } - /* Parse server-side auth mode (what clients connecting to this server - need). "anonymous" allows unauthenticated sessions; "user" requires - a username/password pair. */ UA_Boolean serverAllowAnonymous; const char *serverUsername = NULL, *serverPassword = NULL; + if (parseAuthConfig (&serverCfg, "ServerRegister", &serverAllowAnonymous, + &serverUsername, &serverPassword) + != 0) + goto cleanup; - if (strcmp (serverAuthMode, "anonymous") == 0) - { - serverAllowAnonymous = true; - } - else if (strcmp (serverAuthMode, "user") == 0) - { - serverAllowAnonymous = false; - serverUsername - = configRequire (&serverCfg, "username", "ServerRegister"); - serverPassword - = configRequire (&serverCfg, "password", "ServerRegister"); - if (!serverUsername || !serverPassword) - { - configFree (&serverCfg); - return EXIT_FAILURE; - } - } - else - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Unknown server auth mode: %s " - "(expected 'anonymous' or 'user')", - serverAuthMode); - configFree (&serverCfg); - return EXIT_FAILURE; - } - - char **serverTrustPaths = NULL; - size_t serverTrustSize = 0; if (serverSecure && loadTrustStore (serverTrustStore, &serverTrustPaths, &serverTrustSize) != 0) - { - configFree (&serverCfg); - return EXIT_FAILURE; - } + goto cleanup; /* ── Load client config ─────────────────────────────────────── */ - Config clientCfg; if (configLoad (argv[2], &clientCfg) != 0) - { - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&serverCfg); - return EXIT_FAILURE; - } + goto cleanup; const char *clientAppUri = configRequire (&clientCfg, "applicationUri", "ServerRegister"); @@ -166,27 +131,17 @@ main (int argc, char **argv) = configRequire (&clientCfg, "securityMode", "ServerRegister"); const char *securityPolicyStr = configRequire (&clientCfg, "securityPolicy", "ServerRegister"); - const char *clientAuthMode - = configRequire (&clientCfg, "authMode", "ServerRegister"); if (!clientAppUri || !clientCertPath || !clientKeyPath || !securityModeStr - || !securityPolicyStr || !clientAuthMode) - { - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } + || !securityPolicyStr) + goto cleanup; UA_MessageSecurityMode securityMode = parseSecurityMode (securityModeStr); if (securityMode == UA_MESSAGESECURITYMODE_INVALID) { UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown security mode: %s", securityModeStr); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; + goto cleanup; } const char *securityPolicyUri = resolveSecurityPolicyUri (securityPolicyStr); @@ -194,104 +149,32 @@ main (int argc, char **argv) { UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unknown security policy: %s", securityPolicyStr); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; + goto cleanup; } - /* Parse client-side auth mode (how this server authenticates to the - LDS when registering). */ const char *clientUsername = NULL, *clientPassword = NULL; - - if (strcmp (clientAuthMode, "anonymous") == 0) - { - /* No credentials needed. */ - } - else if (strcmp (clientAuthMode, "user") == 0) - { - clientUsername - = configRequire (&clientCfg, "username", "ServerRegister"); - clientPassword - = configRequire (&clientCfg, "password", "ServerRegister"); - if (!clientUsername || !clientPassword) - { - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } - } - else - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Unknown client auth mode: %s " - "(expected 'anonymous' or 'user')", - clientAuthMode); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } + if (parseAuthConfig (&clientCfg, "ServerRegister", NULL, &clientUsername, + &clientPassword) + != 0) + goto cleanup; const char *clientTrustStore = configRequire (&clientCfg, "trustStore", "ServerRegister"); if (!clientTrustStore) - { - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } + goto cleanup; - char **clientTrustPaths = NULL; - size_t clientTrustSize = 0; if (loadTrustStore (clientTrustStore, &clientTrustPaths, &clientTrustSize) != 0) - { - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } + goto cleanup; /* ── Create and configure server ────────────────────────────── */ UA_StatusCode retval; - UA_Server *server; - - if (serverSecure) - { - server = createSecureServer ((UA_UInt16)port, applicationUri, - serverCertPath, serverKeyPath, - serverTrustPaths, serverTrustSize, &retval); - if (!server) - { - freeTrustStore (clientTrustPaths, clientTrustSize); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } - } - else - { - server = UA_Server_new (); - UA_ServerConfig *config = UA_Server_getConfig (server); - retval = UA_ServerConfig_setMinimal (config, (UA_UInt16)port, NULL); - if (retval != UA_STATUSCODE_GOOD) - { - UA_Server_delete (server); - freeTrustStore (clientTrustPaths, clientTrustSize); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } - UA_String_clear (&config->applicationDescription.applicationUri); - config->applicationDescription.applicationUri - = UA_String_fromChars (applicationUri); - } + server = createServer ((UA_UInt16)port, applicationUri, serverCertPath, + serverKeyPath, serverTrustPaths, serverTrustSize, + &retval); + if (!server) + goto cleanup; UA_ServerConfig *serverConfig = UA_Server_getConfig (server); serverConfig->logging->context = (void *)(uintptr_t)logLevel; @@ -307,14 +190,7 @@ main (int argc, char **argv) logins[0].password = UA_STRING ((char *)serverPassword); retval = UA_AccessControl_default (serverConfig, false, NULL, 1, logins); if (retval != UA_STATUSCODE_GOOD) - { - UA_Server_delete (server); - freeTrustStore (clientTrustPaths, clientTrustSize); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; - } + goto cleanup; } serverConfig->applicationDescription.applicationType @@ -334,12 +210,7 @@ main (int argc, char **argv) if (retval != UA_STATUSCODE_GOOD) { UA_Server_run_shutdown (server); - UA_Server_delete (server); - freeTrustStore (clientTrustPaths, clientTrustSize); - freeTrustStore (serverTrustPaths, serverTrustSize); - configFree (&clientCfg); - configFree (&serverCfg); - return EXIT_FAILURE; + goto cleanup; } clientConfig.logging->context = (void *)(uintptr_t)logLevel; if (clientUsername) @@ -412,10 +283,14 @@ main (int argc, char **argv) } UA_Server_run_shutdown (server); - UA_Server_delete (server); + rc = EXIT_SUCCESS; + +cleanup: + if (server) + UA_Server_delete (server); freeTrustStore (clientTrustPaths, clientTrustSize); freeTrustStore (serverTrustPaths, serverTrustSize); configFree (&clientCfg); configFree (&serverCfg); - return EXIT_SUCCESS; + return rc; } -- cgit v1.2.3