aboutsummaryrefslogtreecommitdiffstats
path: root/src/client.c
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-02-19 00:14:25 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-02-19 00:14:25 +0100
commita9ebc3b434b7979163fdf83984b32f1e513dacb8 (patch)
treec801a98f44689669c905e833371fa539f4934a1f /src/client.c
parentbdc2305c2376c8b6697b6ecfecce104c956bdfcf (diff)
downloadBobinkCOpcUa-a9ebc3b434b7979163fdf83984b32f1e513dacb8.tar.gz
BobinkCOpcUa-a9ebc3b434b7979163fdf83984b32f1e513dacb8.zip
Rename client executable to bobink_opcua_client
Diffstat (limited to 'src/client.c')
-rw-r--r--src/client.c393
1 files changed, 0 insertions, 393 deletions
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 <open62541/client_config_default.h>
-#include <open62541/client_highlevel.h>
-#include <open62541/plugin/log_stdout.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* ========================================================================
- * 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 <config-file> <operation> <endpoint-url> "
- "[log-level]\n"
- " %s <config-file> download-cert <endpoint-url> "
- "<output-file> [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 <config-file> download-cert "
- "<endpoint-url> <output-file> [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 <config-file> <operation> <endpoint-url> "
- "[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;
-}