aboutsummaryrefslogtreecommitdiffstats
path: root/src/server_lds.c
blob: a9a68bcc7ebec5a86996f046b4b2bf26ec7a93c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
 * @file  server_lds.c
 * @brief Local Discovery Server implementation.
 *
 * This program runs an OPC UA Local Discovery Server (LDS) configured with
 * encryption and a configurable cleanup timeout. Other OPC UA servers register
 * with this LDS using the RegisterServer2 service. Clients can query this LDS
 * using the FindServers service to discover registered servers.
 */

#include "common.h"
#include "config.h"

#include <open62541/plugin/accesscontrol_default.h>
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>

#include <signal.h>
#include <stdlib.h>
#include <string.h>

UA_Boolean running = true;

static void
stopHandler (int sig)
{
  UA_LOG_INFO (UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
  running = false;
}

int
main (int argc, char *argv[])
{
  signal (SIGINT, stopHandler);
  signal (SIGTERM, stopHandler);

  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;

  int port = configRequireInt (&cfg, "port", "ServerLDS");
  const char *applicationUri
      = configRequire (&cfg, "applicationUri", "ServerLDS");
  const char *certPath = configRequire (&cfg, "certificate", "ServerLDS");
  const char *keyPath = configRequire (&cfg, "privateKey", "ServerLDS");
  int cleanupTimeout = configRequireInt (&cfg, "cleanupTimeout", "ServerLDS");
  const char *authMode = configRequire (&cfg, "authMode", "ServerLDS");

  if (!applicationUri || !certPath || !keyPath || !authMode || port < 0
      || cleanupTimeout < 0)
    {
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  /* The OPC UA specification requires the cleanup timeout to exceed the
     register-server interval.  open62541 enforces a floor of 10 seconds. */
  if (cleanupTimeout <= 10)
    {
      UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Cleanup timeout must be > 10 seconds (got %d)",
                    cleanupTimeout);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  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
    {
      UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                    "Unknown auth mode: %s "
                    "(expected 'anonymous' or 'user')",
                    authMode);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  const char *trustStore = configRequire (&cfg, "trustStore", "ServerLDS");
  if (!trustStore)
    {
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  char **trustPaths = NULL;
  size_t trustSize = 0;
  if (loadTrustStore (trustStore, &trustPaths, &trustSize) != 0)
    {
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  UA_StatusCode retval;
  UA_Server *server
      = createSecureServer ((UA_UInt16)port, applicationUri, certPath, keyPath,
                            trustPaths, trustSize, &retval);
  if (!server)
    {
      freeTrustStore (trustPaths, trustSize);
      configFree (&cfg);
      return EXIT_FAILURE;
    }

  UA_ServerConfig *serverConfig = UA_Server_getConfig (server);
  serverConfig->logging->context = (void *)(uintptr_t)logLevel;

  /* Some OPC UA stacks omit the timestamp in the request header.  The
     default behaviour rejects these requests with BadInvalidTimestamp.
     Downgrade to a warning so third-party servers can still register. */
  serverConfig->verifyRequestTimestamp = UA_RULEHANDLING_WARN;

  /* Configure access control after server creation because
     UA_ServerConfig_setDefaultWithSecurityPolicies (called by
     createSecureServer) resets the access control plugin.  The credential
     list is deep-copied by UA_AccessControl_default. */
  if (!allowAnonymous)
    {
      UA_UsernamePasswordLogin logins[1];
      logins[0].username = UA_STRING ((char *)username);
      logins[0].password = UA_STRING ((char *)password);
      retval = UA_AccessControl_default (serverConfig, false, NULL, 1, logins);
      if (retval != UA_STATUSCODE_GOOD)
        {
          UA_Server_delete (server);
          freeTrustStore (trustPaths, trustSize);
          configFree (&cfg);
          return EXIT_FAILURE;
        }
    }

  /* Mark this server as a Discovery Server so clients can identify it. */
  serverConfig->applicationDescription.applicationType
      = UA_APPLICATIONTYPE_DISCOVERYSERVER;

  /* Time (seconds) after which stale registrations are removed.  Must
     exceed the registering server's re-register interval. */
  serverConfig->discoveryCleanupTimeout = cleanupTimeout;

  retval = UA_Server_run (server, &running);

  UA_Server_delete (server);
  freeTrustStore (trustPaths, trustSize);
  configFree (&cfg);
  return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}