From a9ebc3b434b7979163fdf83984b32f1e513dacb8 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Thu, 19 Feb 2026 00:14:25 +0100 Subject: Rename client executable to bobink_opcua_client --- CMakeLists.txt | 9 +- readme.md | 10 +- src/bobink_opcua_client.c | 393 +++++++++++++++++++++++++++++++++++++++ src/client.c | 393 --------------------------------------- tests/run_cert_bootstrap_test.sh | 10 +- tests/run_download_cert_test.sh | 2 +- tests/run_test.sh | 6 +- 7 files changed, 412 insertions(+), 411 deletions(-) create mode 100644 src/bobink_opcua_client.c delete mode 100644 src/client.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a0870c..1d9f7a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,9 @@ # # Builds three programs that demonstrate OPC UA discovery. # bobink_opcua_discovery_server runs a Local Discovery Server. -# bobink_opcua_server periodically registers itself with the LDS. client -# queries servers for discovery info, endpoints, or current time. +# bobink_opcua_server periodically registers itself with the LDS. +# bobink_opcua_client queries servers for discovery info, endpoints, or current +# time. # # All programs link against common (shared helpers and config parser) which in # turn depends on open62541. @@ -28,8 +29,8 @@ add_library(common STATIC src/common.c src/config.c) target_link_libraries(common open62541::open62541) # Unified client: find-servers, get-endpoints, read-time. -add_executable(client src/client.c) -target_link_libraries(client common) +add_executable(bobink_opcua_client src/bobink_opcua_client.c) +target_link_libraries(bobink_opcua_client common) # Runs the Local Discovery Server that other servers register with. add_executable(bobink_opcua_discovery_server src/server_lds.c) diff --git a/readme.md b/readme.md index 07554a8..e0bf631 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ A small C project that demonstrates OPC UA server discovery using the - **bobink_opcua_discovery_server** — Local Discovery Server that other servers register with - **bobink_opcua_server** — a server that periodically registers itself with the LDS -- **client** — queries the LDS for servers, lists endpoints, reads the current time, or downloads a server's certificate +- **bobink_opcua_client** — queries the LDS for servers, lists endpoints, reads the current time, or downloads a server's certificate ## Prerequisites @@ -62,16 +62,16 @@ build/bobink_opcua_server tests/secure_user/server_register.conf \ tests/secure_user/server_register_client.conf opc.tcp://localhost:14840 # 3. Find registered servers via the LDS -build/client tests/secure_user/client.conf find-servers opc.tcp://localhost:14840 +build/bobink_opcua_client tests/secure_user/client.conf find-servers opc.tcp://localhost:14840 # 4. List endpoints on the registered server -build/client tests/secure_user/client.conf get-endpoints opc.tcp://localhost:14841 +build/bobink_opcua_client tests/secure_user/client.conf get-endpoints opc.tcp://localhost:14841 # 5. Read the current time from the registered server -build/client tests/secure_user/client.conf read-time opc.tcp://localhost:14841 +build/bobink_opcua_client tests/secure_user/client.conf read-time opc.tcp://localhost:14841 # 6. Download the server's certificate to a local file -build/client tests/secure_user/client.conf download-cert opc.tcp://localhost:14841 server.der +build/bobink_opcua_client tests/secure_user/client.conf download-cert opc.tcp://localhost:14841 server.der ``` All three programs accept an optional log level as the last argument diff --git a/src/bobink_opcua_client.c b/src/bobink_opcua_client.c new file mode 100644 index 0000000..35a3e6f --- /dev/null +++ b/src/bobink_opcua_client.c @@ -0,0 +1,393 @@ +/** + * @file client.c + * @brief Unified OPC UA client for discovery and server interaction. + * + * Supports four operations selected via CLI: + * find-servers — queries a server's FindServers service + * get-endpoints — queries a server's GetEndpoints service + * read-time — connects to a server and reads the current time + * download-cert — downloads a server's certificate to a local file + * + * Encryption is optional: when certificate, privateKey, and trustStore are + * provided, the client uses the configured security policy; otherwise it + * connects without encryption. + */ + +#include "common.h" +#include "config.h" + +#include +#include +#include + +#include +#include +#include + +/* ======================================================================== + * Operation Dispatch + * ======================================================================== */ + +typedef enum +{ + OP_FIND_SERVERS, + OP_GET_ENDPOINTS, + OP_READ_TIME, + OP_DOWNLOAD_CERT, + OP_INVALID +} operation; + +static operation +_s_parse_operation (const char *name) +{ + if (strcmp (name, "find-servers") == 0) + return OP_FIND_SERVERS; + if (strcmp (name, "get-endpoints") == 0) + return OP_GET_ENDPOINTS; + if (strcmp (name, "read-time") == 0) + return OP_READ_TIME; + if (strcmp (name, "download-cert") == 0) + return OP_DOWNLOAD_CERT; + return OP_INVALID; +} + +/* ======================================================================== + * Operations + * ======================================================================== */ + +/** + * Calls the FindServers service and prints all discovered servers. + * + * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int +_s_op_find_servers (UA_Client *client, const char *url) +{ + size_t array_size = 0; + UA_ApplicationDescription *array = NULL; + + UA_StatusCode retval = UA_Client_findServers (client, url, 0, NULL, 0, NULL, + &array_size, &array); + if (retval != UA_STATUSCODE_GOOD) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "FindServers failed: %s", UA_StatusCode_name (retval)); + return EXIT_FAILURE; + } + + for (size_t i = 0; i < array_size; i++) + print_application_description (&array[i], i); + + UA_Array_delete (array, array_size, + &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); + return EXIT_SUCCESS; +} + +/** + * Calls the GetEndpoints service and prints all endpoints. + * + * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int +_s_op_get_endpoints (UA_Client *client, const char *url) +{ + size_t array_size = 0; + UA_EndpointDescription *array = NULL; + + UA_StatusCode retval + = UA_Client_getEndpoints (client, url, &array_size, &array); + if (retval != UA_STATUSCODE_GOOD) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "GetEndpoints failed: %s", UA_StatusCode_name (retval)); + return EXIT_FAILURE; + } + + for (size_t i = 0; i < array_size; i++) + print_endpoint (&array[i], i); + + UA_Array_delete (array, array_size, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); + return EXIT_SUCCESS; +} + +/** + * Connects to a server and reads the current time node. + * + * Authentication (anonymous, username/password, or X509 certificate) is + * configured in the client config before this function is called. + * + * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int +_s_op_read_time (UA_Client *client, const char *url) +{ + UA_StatusCode retval = UA_Client_connect (client, url); + + if (retval != UA_STATUSCODE_GOOD) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "Could not connect: %s", UA_StatusCode_name (retval)); + return EXIT_FAILURE; + } + + UA_Variant value; + UA_Variant_init (&value); + + const UA_NodeId nodeId = UA_NS0ID (SERVER_SERVERSTATUS_CURRENTTIME); + retval = UA_Client_readValueAttribute (client, nodeId, &value); + + int rc = EXIT_SUCCESS; + 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)); + rc = EXIT_FAILURE; + } + + UA_Variant_clear (&value); + UA_Client_disconnect (client); + return rc; +} + +/** + * Downloads the server's certificate via GetEndpoints and writes it to a file. + * + * Picks the first endpoint that carries a non-empty serverCertificate. + * + * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +static int +_s_op_download_cert (UA_Client *client, const char *url, + const char *output_path) +{ + size_t array_size = 0; + UA_EndpointDescription *array = NULL; + + UA_StatusCode retval + = UA_Client_getEndpoints (client, url, &array_size, &array); + if (retval != UA_STATUSCODE_GOOD) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "GetEndpoints failed: %s", UA_StatusCode_name (retval)); + return EXIT_FAILURE; + } + + UA_ByteString *cert = NULL; + for (size_t i = 0; i < array_size; i++) + { + if (array[i].serverCertificate.length > 0) + { + cert = &array[i].serverCertificate; + break; + } + } + + if (!cert) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "No endpoint returned a server certificate"); + UA_Array_delete (array, array_size, + &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); + return EXIT_FAILURE; + } + + FILE *fp = fopen (output_path, "wb"); + if (!fp) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "Could not open output file: %s", output_path); + UA_Array_delete (array, array_size, + &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); + return EXIT_FAILURE; + } + + size_t written = fwrite (cert->data, 1, cert->length, fp); + fclose (fp); + + int rc; + if (written != cert->length) + { + UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "Failed to write certificate (%zu of %zu bytes)", written, + cert->length); + rc = EXIT_FAILURE; + } + else + { + UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, + "Certificate saved to %s (%zu bytes)", output_path, + cert->length); + rc = EXIT_SUCCESS; + } + + UA_Array_delete (array, array_size, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); + return rc; +} + +/* ======================================================================== + * Main + * ======================================================================== */ + +int +main (int argc, char **argv) +{ + if (argc < 4) + { + UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Usage: %s " + "[log-level]\n" + " %s download-cert " + " [log-level]\n" + "Operations: find-servers, get-endpoints, read-time, " + "download-cert", + argv[0], argv[0]); + return EXIT_FAILURE; + } + + operation op = _s_parse_operation (argv[2]); + if (op == OP_INVALID) + { + UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Unknown operation: %s (expected find-servers, " + "get-endpoints, read-time, download-cert)", + argv[2]); + return EXIT_FAILURE; + } + + const char *endpoint_url = argv[3]; + const char *output_path = NULL; + const char *log_level_str = "info"; + + if (op == OP_DOWNLOAD_CERT) + { + if (argc < 5 || argc > 6) + { + UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Usage: %s download-cert " + " [log-level]", + argv[0]); + return EXIT_FAILURE; + } + output_path = argv[4]; + log_level_str = (argc == 6) ? argv[5] : "info"; + } + else + { + if (argc > 5) + { + UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Usage: %s " + "[log-level]", + argv[0]); + return EXIT_FAILURE; + } + log_level_str = (argc == 5) ? argv[4] : "info"; + } + int log_level = parse_log_level (log_level_str); + if (log_level < 0) + { + UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, + "Unknown log level: %s " + "(expected trace, debug, info, warning, error, fatal)", + log_level_str); + return EXIT_FAILURE; + } + + config cfg; + if (config_load (argv[1], &cfg) != 0) + return EXIT_FAILURE; + + /* ---- Common config keys ---- */ + + const char *application_uri + = config_require (&cfg, "applicationUri", "Client"); + if (!application_uri) + { + config_free (&cfg); + return EXIT_FAILURE; + } + + security_config sec; + if (parse_security_config (&cfg, "Client", true, &sec) != 0) + { + config_free (&cfg); + return EXIT_FAILURE; + } + + /* ---- Auth config (read-time only) ---- */ + + auth_config auth = { .mode = AUTH_ANONYMOUS }; + + if (op == OP_READ_TIME && parse_auth_config (&cfg, "Client", &auth) != 0) + { + free_trust_store (sec.trust_paths, sec.trust_size); + config_free (&cfg); + return EXIT_FAILURE; + } + + /* ---- Create client ---- */ + + UA_Client *client = UA_Client_new (); + + UA_StatusCode retval; + if (op == OP_DOWNLOAD_CERT) + retval = create_unsecure_client_config (UA_Client_getConfig (client), + application_uri, NULL); + else if (sec.cert_path) + retval = create_secure_client_config (UA_Client_getConfig (client), + application_uri, &sec, &auth); + else + retval = create_unsecure_client_config (UA_Client_getConfig (client), + application_uri, &auth); + + if (retval != UA_STATUSCODE_GOOD) + { + UA_Client_delete (client); + free_trust_store (sec.trust_paths, sec.trust_size); + config_free (&cfg); + return EXIT_FAILURE; + } + + UA_Client_getConfig (client)->logging->context + = (void *)(uintptr_t)log_level; + + /* ---- Dispatch operation ---- */ + + int rc; + switch (op) + { + case OP_FIND_SERVERS: + rc = _s_op_find_servers (client, endpoint_url); + break; + case OP_GET_ENDPOINTS: + rc = _s_op_get_endpoints (client, endpoint_url); + break; + case OP_READ_TIME: + rc = _s_op_read_time (client, endpoint_url); + break; + case OP_DOWNLOAD_CERT: + rc = _s_op_download_cert (client, endpoint_url, output_path); + break; + default: + rc = EXIT_FAILURE; + break; + } + + /* ---- Cleanup ---- */ + + UA_Client_delete (client); + free_trust_store (sec.trust_paths, sec.trust_size); + config_free (&cfg); + + return rc; +} diff --git a/src/client.c b/src/client.c deleted file mode 100644 index 35a3e6f..0000000 --- a/src/client.c +++ /dev/null @@ -1,393 +0,0 @@ -/** - * @file client.c - * @brief Unified OPC UA client for discovery and server interaction. - * - * Supports four operations selected via CLI: - * find-servers — queries a server's FindServers service - * get-endpoints — queries a server's GetEndpoints service - * read-time — connects to a server and reads the current time - * download-cert — downloads a server's certificate to a local file - * - * Encryption is optional: when certificate, privateKey, and trustStore are - * provided, the client uses the configured security policy; otherwise it - * connects without encryption. - */ - -#include "common.h" -#include "config.h" - -#include -#include -#include - -#include -#include -#include - -/* ======================================================================== - * Operation Dispatch - * ======================================================================== */ - -typedef enum -{ - OP_FIND_SERVERS, - OP_GET_ENDPOINTS, - OP_READ_TIME, - OP_DOWNLOAD_CERT, - OP_INVALID -} operation; - -static operation -_s_parse_operation (const char *name) -{ - if (strcmp (name, "find-servers") == 0) - return OP_FIND_SERVERS; - if (strcmp (name, "get-endpoints") == 0) - return OP_GET_ENDPOINTS; - if (strcmp (name, "read-time") == 0) - return OP_READ_TIME; - if (strcmp (name, "download-cert") == 0) - return OP_DOWNLOAD_CERT; - return OP_INVALID; -} - -/* ======================================================================== - * Operations - * ======================================================================== */ - -/** - * Calls the FindServers service and prints all discovered servers. - * - * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. - */ -static int -_s_op_find_servers (UA_Client *client, const char *url) -{ - size_t array_size = 0; - UA_ApplicationDescription *array = NULL; - - UA_StatusCode retval = UA_Client_findServers (client, url, 0, NULL, 0, NULL, - &array_size, &array); - if (retval != UA_STATUSCODE_GOOD) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "FindServers failed: %s", UA_StatusCode_name (retval)); - return EXIT_FAILURE; - } - - for (size_t i = 0; i < array_size; i++) - print_application_description (&array[i], i); - - UA_Array_delete (array, array_size, - &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); - return EXIT_SUCCESS; -} - -/** - * Calls the GetEndpoints service and prints all endpoints. - * - * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. - */ -static int -_s_op_get_endpoints (UA_Client *client, const char *url) -{ - size_t array_size = 0; - UA_EndpointDescription *array = NULL; - - UA_StatusCode retval - = UA_Client_getEndpoints (client, url, &array_size, &array); - if (retval != UA_STATUSCODE_GOOD) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "GetEndpoints failed: %s", UA_StatusCode_name (retval)); - return EXIT_FAILURE; - } - - for (size_t i = 0; i < array_size; i++) - print_endpoint (&array[i], i); - - UA_Array_delete (array, array_size, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); - return EXIT_SUCCESS; -} - -/** - * Connects to a server and reads the current time node. - * - * Authentication (anonymous, username/password, or X509 certificate) is - * configured in the client config before this function is called. - * - * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. - */ -static int -_s_op_read_time (UA_Client *client, const char *url) -{ - UA_StatusCode retval = UA_Client_connect (client, url); - - if (retval != UA_STATUSCODE_GOOD) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "Could not connect: %s", UA_StatusCode_name (retval)); - return EXIT_FAILURE; - } - - UA_Variant value; - UA_Variant_init (&value); - - const UA_NodeId nodeId = UA_NS0ID (SERVER_SERVERSTATUS_CURRENTTIME); - retval = UA_Client_readValueAttribute (client, nodeId, &value); - - int rc = EXIT_SUCCESS; - 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)); - rc = EXIT_FAILURE; - } - - UA_Variant_clear (&value); - UA_Client_disconnect (client); - return rc; -} - -/** - * Downloads the server's certificate via GetEndpoints and writes it to a file. - * - * Picks the first endpoint that carries a non-empty serverCertificate. - * - * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. - */ -static int -_s_op_download_cert (UA_Client *client, const char *url, - const char *output_path) -{ - size_t array_size = 0; - UA_EndpointDescription *array = NULL; - - UA_StatusCode retval - = UA_Client_getEndpoints (client, url, &array_size, &array); - if (retval != UA_STATUSCODE_GOOD) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "GetEndpoints failed: %s", UA_StatusCode_name (retval)); - return EXIT_FAILURE; - } - - UA_ByteString *cert = NULL; - for (size_t i = 0; i < array_size; i++) - { - if (array[i].serverCertificate.length > 0) - { - cert = &array[i].serverCertificate; - break; - } - } - - if (!cert) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "No endpoint returned a server certificate"); - UA_Array_delete (array, array_size, - &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); - return EXIT_FAILURE; - } - - FILE *fp = fopen (output_path, "wb"); - if (!fp) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "Could not open output file: %s", output_path); - UA_Array_delete (array, array_size, - &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); - return EXIT_FAILURE; - } - - size_t written = fwrite (cert->data, 1, cert->length, fp); - fclose (fp); - - int rc; - if (written != cert->length) - { - UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "Failed to write certificate (%zu of %zu bytes)", written, - cert->length); - rc = EXIT_FAILURE; - } - else - { - UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, - "Certificate saved to %s (%zu bytes)", output_path, - cert->length); - rc = EXIT_SUCCESS; - } - - UA_Array_delete (array, array_size, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); - return rc; -} - -/* ======================================================================== - * Main - * ======================================================================== */ - -int -main (int argc, char **argv) -{ - if (argc < 4) - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Usage: %s " - "[log-level]\n" - " %s download-cert " - " [log-level]\n" - "Operations: find-servers, get-endpoints, read-time, " - "download-cert", - argv[0], argv[0]); - return EXIT_FAILURE; - } - - operation op = _s_parse_operation (argv[2]); - if (op == OP_INVALID) - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Unknown operation: %s (expected find-servers, " - "get-endpoints, read-time, download-cert)", - argv[2]); - return EXIT_FAILURE; - } - - const char *endpoint_url = argv[3]; - const char *output_path = NULL; - const char *log_level_str = "info"; - - if (op == OP_DOWNLOAD_CERT) - { - if (argc < 5 || argc > 6) - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Usage: %s download-cert " - " [log-level]", - argv[0]); - return EXIT_FAILURE; - } - output_path = argv[4]; - log_level_str = (argc == 6) ? argv[5] : "info"; - } - else - { - if (argc > 5) - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Usage: %s " - "[log-level]", - argv[0]); - return EXIT_FAILURE; - } - log_level_str = (argc == 5) ? argv[4] : "info"; - } - int log_level = parse_log_level (log_level_str); - if (log_level < 0) - { - UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, - "Unknown log level: %s " - "(expected trace, debug, info, warning, error, fatal)", - log_level_str); - return EXIT_FAILURE; - } - - config cfg; - if (config_load (argv[1], &cfg) != 0) - return EXIT_FAILURE; - - /* ---- Common config keys ---- */ - - const char *application_uri - = config_require (&cfg, "applicationUri", "Client"); - if (!application_uri) - { - config_free (&cfg); - return EXIT_FAILURE; - } - - security_config sec; - if (parse_security_config (&cfg, "Client", true, &sec) != 0) - { - config_free (&cfg); - return EXIT_FAILURE; - } - - /* ---- Auth config (read-time only) ---- */ - - auth_config auth = { .mode = AUTH_ANONYMOUS }; - - if (op == OP_READ_TIME && parse_auth_config (&cfg, "Client", &auth) != 0) - { - free_trust_store (sec.trust_paths, sec.trust_size); - config_free (&cfg); - return EXIT_FAILURE; - } - - /* ---- Create client ---- */ - - UA_Client *client = UA_Client_new (); - - UA_StatusCode retval; - if (op == OP_DOWNLOAD_CERT) - retval = create_unsecure_client_config (UA_Client_getConfig (client), - application_uri, NULL); - else if (sec.cert_path) - retval = create_secure_client_config (UA_Client_getConfig (client), - application_uri, &sec, &auth); - else - retval = create_unsecure_client_config (UA_Client_getConfig (client), - application_uri, &auth); - - if (retval != UA_STATUSCODE_GOOD) - { - UA_Client_delete (client); - free_trust_store (sec.trust_paths, sec.trust_size); - config_free (&cfg); - return EXIT_FAILURE; - } - - UA_Client_getConfig (client)->logging->context - = (void *)(uintptr_t)log_level; - - /* ---- Dispatch operation ---- */ - - int rc; - switch (op) - { - case OP_FIND_SERVERS: - rc = _s_op_find_servers (client, endpoint_url); - break; - case OP_GET_ENDPOINTS: - rc = _s_op_get_endpoints (client, endpoint_url); - break; - case OP_READ_TIME: - rc = _s_op_read_time (client, endpoint_url); - break; - case OP_DOWNLOAD_CERT: - rc = _s_op_download_cert (client, endpoint_url, output_path); - break; - default: - rc = EXIT_FAILURE; - break; - } - - /* ---- Cleanup ---- */ - - UA_Client_delete (client); - free_trust_store (sec.trust_paths, sec.trust_size); - config_free (&cfg); - - return rc; -} diff --git a/tests/run_cert_bootstrap_test.sh b/tests/run_cert_bootstrap_test.sh index d0f4a3e..5db2b7b 100755 --- a/tests/run_cert_bootstrap_test.sh +++ b/tests/run_cert_bootstrap_test.sh @@ -54,7 +54,7 @@ wait_for_port "$SR_PORT" "$SR_PID" "bobink_opcua_server" TMPFILE=$(mktemp) # ── Step 1: FindServers on LDS (client trusts LDS) ──────────── -build/client "$CONFIG_DIR/client.conf" find-servers "opc.tcp://localhost:$LDS_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" find-servers "opc.tcp://localhost:$LDS_PORT" >"$TMPFILE" 2>&1 FS_RC=$? FS_OUTPUT=$(<"$TMPFILE") @@ -65,7 +65,7 @@ echo "$FS_OUTPUT" | grep -q "urn:localhost:bobink:ServerRegister" check "find-servers contains urn:localhost:bobink:ServerRegister" $? # ── Step 2: GetEndpoints on ServerRegister (should FAIL) ────── -build/client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 GE_FAIL_RC=$? GE_FAIL_OUTPUT=$(<"$TMPFILE") @@ -75,7 +75,7 @@ check "get-endpoints FAILS without ServerRegister cert (exit code $GE_FAIL_RC)" # ── Step 3: download-cert from ServerRegister (via None) ────── DOWNLOADED_CERT=$(mktemp --suffix=.der) -build/client "$CONFIG_DIR/client.conf" download-cert "opc.tcp://localhost:$SR_PORT" "$DOWNLOADED_CERT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" download-cert "opc.tcp://localhost:$SR_PORT" "$DOWNLOADED_CERT" >"$TMPFILE" 2>&1 DC_RC=$? DC_OUTPUT=$(<"$TMPFILE") @@ -93,7 +93,7 @@ check "downloaded certificate matches ServerRegister cert.der" $? cp "$DOWNLOADED_CERT" "$CONFIG_DIR/certs/trust_client/ServerRegister_cert.der" # ── Step 6: GetEndpoints on ServerRegister (should succeed) ─── -build/client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 GE_RC=$? GE_OUTPUT=$(<"$TMPFILE") @@ -104,7 +104,7 @@ echo "$GE_OUTPUT" | grep -q "Aes256_Sha256_RsaPss" check "get-endpoints contains Aes256_Sha256_RsaPss" $? # ── Step 7: ReadTime on ServerRegister (should succeed) ─────── -build/client "$CONFIG_DIR/client.conf" read-time "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" read-time "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 RT_RC=$? RT_OUTPUT=$(<"$TMPFILE") diff --git a/tests/run_download_cert_test.sh b/tests/run_download_cert_test.sh index 7dd7dfd..f60cd20 100755 --- a/tests/run_download_cert_test.sh +++ b/tests/run_download_cert_test.sh @@ -40,7 +40,7 @@ wait_for_port "$LDS_PORT" "$LDS_PID" "bobink_opcua_discovery_server" TMPFILE=$(mktemp) DOWNLOADED_CERT=$(mktemp --suffix=.der) -build/client "$CONFIG_DIR/client.conf" download-cert "opc.tcp://localhost:$LDS_PORT" "$DOWNLOADED_CERT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" download-cert "opc.tcp://localhost:$LDS_PORT" "$DOWNLOADED_CERT" >"$TMPFILE" 2>&1 DC_RC=$? DC_OUTPUT=$(<"$TMPFILE") diff --git a/tests/run_test.sh b/tests/run_test.sh index 8f6c21b..85eb291 100755 --- a/tests/run_test.sh +++ b/tests/run_test.sh @@ -51,7 +51,7 @@ wait_for_port "$SR_PORT" "$SR_PID" "bobink_opcua_server" # ── FindServers ─────────────────────────────────────────────── TMPFILE=$(mktemp) -build/client "$CONFIG_DIR/client.conf" find-servers "opc.tcp://localhost:$LDS_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" find-servers "opc.tcp://localhost:$LDS_PORT" >"$TMPFILE" 2>&1 FS_RC=$? FS_OUTPUT=$(<"$TMPFILE") @@ -62,7 +62,7 @@ echo "$FS_OUTPUT" | grep -q "urn:localhost:bobink:ServerRegister" check "find-servers contains urn:localhost:bobink:ServerRegister" $? # ── GetEndpoints ────────────────────────────────────────────── -build/client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" get-endpoints "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 GE_RC=$? GE_OUTPUT=$(<"$TMPFILE") @@ -73,7 +73,7 @@ echo "$GE_OUTPUT" | grep -q "$EXPECTED_POLICY" check "get-endpoints contains $EXPECTED_POLICY" $? # ── ReadTime ────────────────────────────────────────────────── -build/client "$CONFIG_DIR/client.conf" read-time "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 +build/bobink_opcua_client "$CONFIG_DIR/client.conf" read-time "opc.tcp://localhost:$SR_PORT" >"$TMPFILE" 2>&1 RT_RC=$? RT_OUTPUT=$(<"$TMPFILE") -- cgit v1.2.3