Coverage Report

Created: 2026-10-15 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/pubsub/ua_pubsub_writergroup.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 * Copyright (c) 2017-2025 Fraunhofer IOSB (Author: Andreas Ebner)
6
 * Copyright (c) 2019 Fraunhofer IOSB (Author: Julius Pfrommer)
7
 * Copyright (c) 2019 Kalycito Infotech Private Limited
8
 * Copyright (c) 2020 Yannick Wallerer, Siemens AG
9
 * Copyright (c) 2020 Thomas Fischer, Siemens AG
10
 * Copyright (c) 2021 Fraunhofer IOSB (Author: Jan Hermes)
11
 * Copyright (c) 2022 Linutronix GmbH (Author: Muddasir Shakil)
12
 * Copyright 2025 (c) o6 Automation GmbH (Author: Andreas Ebner)
13
 */
14
15
#include "ua_pubsub_internal.h"
16
17
#ifdef UA_ENABLE_PUBSUB /* conditional compilation */
18
19
#include "ua_pubsub_networkmessage.h"
20
21
#ifdef UA_ENABLE_PUBSUB_SKS
22
#include "ua_pubsub_keystorage.h"
23
#endif
24
25
static UA_StatusCode
26
encryptAndSign(UA_WriterGroup *wg, const UA_NetworkMessage *nm,
27
               UA_Byte *signStart, UA_Byte *encryptStart,
28
               UA_Byte *msgEnd);
29
30
static UA_StatusCode
31
generateNetworkMessage(UA_PubSubConnection *connection, UA_WriterGroup *wg,
32
                       UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount,
33
                       UA_ExtensionObject *messageSettings,
34
                        UA_ExtensionObject *transportSettings,
35
                         UA_NetworkMessage *networkMessage);
36
37
static void
38
UA_WriterGroup_disconnect(UA_WriterGroup *wg);
39
40
static UA_StatusCode
41
UA_WriterGroup_connect(UA_PubSubManager *psm, UA_WriterGroup *wg,
42
                       UA_Boolean validate);
43
44
static UA_Boolean
45
0
UA_WriterGroup_canConnect(UA_WriterGroup *wg) {
46
    /* Already connected */
47
0
    if(wg->sendChannel != 0)
48
0
        return false;
49
50
    /* Is this a WriterGroup with custom TransportSettings beyond the
51
     * PubSubConnection? */
52
0
    if(wg->config.transportSettings.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY)
53
0
        return false;
54
55
0
    return true;
56
0
}
57
58
UA_StatusCode
59
0
UA_WriterGroup_addPublishCallback(UA_PubSubManager *psm, UA_WriterGroup *wg) {
60
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
61
62
    /* Already registered */
63
0
    if(wg->publishCallbackId != 0)
64
0
        return UA_STATUSCODE_GOOD;
65
66
    /* Use EventLoop for cyclic callbacks */
67
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
68
0
    return el->addTimer(el, (UA_Callback)UA_WriterGroup_publishCallback,
69
0
                        psm, wg, wg->config.publishingInterval,
70
0
                        NULL /* TODO: use basetime */,
71
0
                        UA_TIMERPOLICY_CURRENTTIME,
72
0
                        &wg->publishCallbackId);
73
0
}
74
75
void
76
0
UA_WriterGroup_removePublishCallback(UA_PubSubManager *psm, UA_WriterGroup *wg) {
77
0
    if(wg->publishCallbackId == 0)
78
0
        return;
79
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
80
0
    if(UA_LIKELY(el != NULL))
81
0
        el->removeTimer(el, wg->publishCallbackId);
82
0
    wg->publishCallbackId = 0;
83
0
}
84
85
#ifdef UA_ENABLE_PUBSUB_SKS
86
static UA_StatusCode
87
writerGroupAttachSKSKeystorage(UA_PubSubManager *psm, UA_WriterGroup *wg) {
88
    /* No SecurityGroup defined */
89
    if(UA_String_isEmpty(&wg->config.securityGroupId) || !wg->config.securityPolicy)
90
        return UA_STATUSCODE_GOOD;
91
92
    /* KeyStorage already connected */
93
    if(wg->keyStorage)
94
        return UA_STATUSCODE_GOOD;
95
96
    /* Does the key storage already exist? */
97
    wg->keyStorage = UA_PubSubKeyStorage_find(psm, wg->config.securityGroupId);
98
    if(wg->keyStorage) {
99
        wg->keyStorage->referenceCount++; /* Increase the ref count */
100
        return UA_STATUSCODE_GOOD;
101
    }
102
103
    /* Create a new key storage */
104
    wg->keyStorage = (UA_PubSubKeyStorage *)UA_calloc(1, sizeof(UA_PubSubKeyStorage));
105
    if(!wg->keyStorage)
106
        return UA_STATUSCODE_BADOUTOFMEMORY;
107
108
    /* Initialize the KeyStorage */
109
    UA_StatusCode res =
110
        UA_PubSubKeyStorage_init(psm, wg->keyStorage, &wg->config.securityGroupId,
111
                                 wg->config.securityPolicy, 0, 0);
112
    if(res != UA_STATUSCODE_GOOD) {
113
        UA_PubSubKeyStorage_delete(psm, wg->keyStorage);
114
        wg->keyStorage = NULL;
115
        return res;
116
    }
117
118
    wg->keyStorage->referenceCount++; /* Increase the ref count */
119
    return UA_STATUSCODE_GOOD;
120
}
121
#endif
122
123
static UA_StatusCode
124
validateWriterGroupConfig(UA_PubSubManager *psm, UA_PubSubComponentHead *logHead,
125
0
                          const UA_WriterGroupConfig *config) {
126
0
    const UA_ExtensionObject *ms = &config->messageSettings;
127
0
    if(ms->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY)
128
0
        return UA_STATUSCODE_GOOD;
129
130
0
    if(config->encodingMimeType == UA_PUBSUB_ENCODING_JSON) {
131
0
        if(!UA_ExtensionObject_hasDecodedType(ms,
132
0
                &UA_TYPES[UA_TYPES_JSONWRITERGROUPMESSAGEDATATYPE])) {
133
0
            UA_LOG_WARNING_PUBSUB(psm->logging, (UA_PubSubConnection*)logHead,
134
0
                                  "WriTerGroupConfig MessageSettings need to be "
135
0
                                  "of type JSONWriterGroupMessageDataType");
136
0
            return UA_STATUSCODE_BADTYPEMISMATCH;
137
0
        }
138
0
    } else if(config->encodingMimeType == UA_PUBSUB_ENCODING_UADP) {
139
0
        if(!UA_ExtensionObject_hasDecodedType(ms,
140
0
                &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])) {
141
0
            UA_LOG_WARNING_PUBSUB(psm->logging, (UA_PubSubConnection*)logHead,
142
0
                                  "WriTerGroupConfig MessageSettings need to be "
143
0
                                  "of type UADPWriterGroupMessageDataType");
144
0
            return UA_STATUSCODE_BADTYPEMISMATCH;
145
0
        }
146
0
    } else {
147
0
        UA_LOG_WARNING_PUBSUB(psm->logging, (UA_PubSubConnection*)logHead,
148
0
                              "Wrong encoding MIME-type");
149
0
        return UA_STATUSCODE_BADINTERNALERROR;
150
0
    }
151
152
0
    return UA_STATUSCODE_GOOD;
153
0
}
154
155
UA_StatusCode
156
UA_WriterGroup_create(UA_PubSubManager *psm, const UA_NodeId connection,
157
                      const UA_WriterGroupConfig *config,
158
0
                      UA_NodeId *writerGroupIdentifier) {
159
    /* Delete the reserved IDs if the related session no longer exists. */
160
0
    UA_PubSubManager_freeIds(psm);
161
0
    if(!config)
162
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
163
164
    /* Search the connection by the given connectionIdentifier */
165
0
    UA_PubSubConnection *c = UA_PubSubConnection_find(psm, connection);
166
0
    if(!c)
167
0
        return UA_STATUSCODE_BADNOTFOUND;
168
169
    /* Validate messageSettings type */
170
0
    UA_StatusCode res = validateWriterGroupConfig(psm, &c->head, config);
171
0
    if(res != UA_STATUSCODE_GOOD)
172
0
        return res;
173
174
    /* Allocate new WriterGroup */
175
0
    UA_WriterGroup *wg = (UA_WriterGroup*)UA_calloc(1, sizeof(UA_WriterGroup));
176
0
    if(!wg)
177
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
178
179
0
    wg->head.componentType = UA_PUBSUBCOMPONENT_WRITERGROUP;
180
0
    wg->linkedConnection = c;
181
182
    /* Deep copy of the config */
183
0
    res = UA_WriterGroupConfig_copy(config, &wg->config);
184
0
    if(res != UA_STATUSCODE_GOOD) {
185
0
        UA_free(wg);
186
0
        return res;
187
0
    }
188
189
    /* Attach to the connection */
190
0
    LIST_INSERT_HEAD(&c->writerGroups, wg, listEntry);
191
0
    c->writerGroupsSize++;
192
193
    /* Add representation / create unique identifier */
194
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
195
0
    res = addWriterGroupRepresentation(psm->sc.server, wg);
196
0
    if(res != UA_STATUSCODE_GOOD) {
197
0
        UA_PubSubComponent_freeWithoutLifecycleCallback(
198
0
            psm, wg, UA_PUBSUBCOMPONENT_WRITERGROUP);
199
0
        return res;
200
0
    }
201
#else
202
    UA_PubSubManager_generateUniqueNodeId(psm, &wg->head.identifier);
203
#endif
204
205
    /* Cache the log string */
206
0
    char tmpLogIdStr[128];
207
0
    mp_snprintf(tmpLogIdStr, 128, "%SWriterGroup %N\t| ",
208
0
                c->head.logIdString, wg->head.identifier);
209
0
    wg->head.logIdString = UA_STRING_ALLOC(tmpLogIdStr);
210
211
    /* Validate the connection settings */
212
0
    res = UA_WriterGroup_connect(psm, wg, true);
213
0
    if(res != UA_STATUSCODE_GOOD) {
214
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
215
0
                            "Could not validate the connection parameters");
216
0
        UA_PubSubComponent_freeWithoutLifecycleCallback(
217
0
            psm, wg, UA_PUBSUBCOMPONENT_WRITERGROUP);
218
0
        return res;
219
0
    }
220
221
#ifdef UA_ENABLE_PUBSUB_SKS
222
    if(config->securityMode == UA_MESSAGESECURITYMODE_SIGN ||
223
       config->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
224
        res = writerGroupAttachSKSKeystorage(psm, wg);
225
        if(res != UA_STATUSCODE_GOOD) {
226
            UA_LOG_ERROR_PUBSUB(psm->logging, wg, "Attaching the SKS KeyStorage failed");
227
            UA_PubSubComponent_freeWithoutLifecycleCallback(
228
                psm, wg, UA_PUBSUBCOMPONENT_WRITERGROUP);
229
            return res;
230
        }
231
    }
232
#endif
233
234
    /* Notify the application that a new WriterGroup was created.
235
     * This may internally adjust the config */
236
0
    UA_Server *server = psm->sc.server;
237
0
    if(server->config.pubSubConfig.componentLifecycleCallback) {
238
0
        res = server->config.pubSubConfig.
239
0
            componentLifecycleCallback(server, wg->head.identifier,
240
0
                                       UA_PUBSUBCOMPONENT_WRITERGROUP, false);
241
0
        if(res != UA_STATUSCODE_GOOD) {
242
            /* The app refused the component; free without re-asking the
243
             * lifecycle callback (it would re-reject and leak the group). */
244
0
            UA_PubSubComponent_freeWithoutLifecycleCallback(
245
0
                psm, wg, UA_PUBSUBCOMPONENT_WRITERGROUP);
246
0
            return res;
247
0
        }
248
0
    }
249
250
0
    UA_LOG_INFO_PUBSUB(psm->logging, wg, "WriterGroup created (State: %s)",
251
0
                       UA_PubSubState_name(wg->head.state));
252
253
    /* Trigger the connection state machine. It might open a socket only when
254
     * the first WriterGroup is attached. */
255
0
    if(config->enabled)
256
0
        UA_PubSubConnection_setPubSubState(psm, c, c->head.state);
257
258
    /* Copying a numeric NodeId always succeeds */
259
0
    if(writerGroupIdentifier)
260
0
        UA_NodeId_copy(&wg->head.identifier, writerGroupIdentifier);
261
262
    /* Enable the WriterGroup immediately if the enabled flag is set */
263
0
    if(config->enabled)
264
0
        UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_OPERATIONAL);
265
266
0
    return UA_STATUSCODE_GOOD;
267
0
}
268
269
UA_StatusCode
270
0
UA_WriterGroup_remove(UA_PubSubManager *psm, UA_WriterGroup *wg) {
271
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
272
273
    /* Check with the application if we can remove */
274
0
    UA_Server *server = psm->sc.server;
275
0
    if(server->config.pubSubConfig.componentLifecycleCallback) {
276
0
        UA_StatusCode res = server->config.pubSubConfig.
277
0
            componentLifecycleCallback(server, wg->head.identifier,
278
0
                                       UA_PUBSUBCOMPONENT_WRITERGROUP, true);
279
0
        if(res != UA_STATUSCODE_GOOD)
280
0
            return res;
281
0
    }
282
283
0
    UA_PubSubConnection *connection = wg->linkedConnection;
284
0
    UA_assert(connection);
285
286
    /* Disable (and disconnect) and set the deleteFlag. This prevents a
287
     * reconnect and triggers the deletion when the last open socket is
288
     * closed. */
289
0
    wg->deleteFlag = true;
290
0
    UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_DISABLED);
291
292
0
    UA_DataSetWriter *dsw, *dsw_tmp;
293
0
    LIST_FOREACH_SAFE(dsw, &wg->writers, listEntry, dsw_tmp) {
294
0
        UA_DataSetWriter_remove(psm, dsw);
295
0
    }
296
297
0
    if(wg->config.securityPolicy && wg->securityPolicyContext) {
298
0
        UA_PubSubSecurityPolicy *sp = wg->config.securityPolicy;
299
0
        sp->deleteGroupContext(sp, wg->securityPolicyContext);
300
0
        wg->securityPolicyContext = NULL;
301
0
    }
302
303
#ifdef UA_ENABLE_PUBSUB_SKS
304
    if(wg->keyStorage) {
305
        UA_PubSubKeyStorage_detachKeyStorage(psm, wg->keyStorage);
306
        wg->keyStorage = NULL;
307
    }
308
#endif
309
310
0
    if(wg->sendChannel == 0) {
311
        /* Unlink from the connection */
312
0
        LIST_REMOVE(wg, listEntry);
313
0
        connection->writerGroupsSize--;
314
0
        wg->linkedConnection = NULL;
315
316
        /* Actually remove the WriterGroup */
317
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
318
0
        deleteNode(psm->sc.server, wg->head.identifier, true);
319
0
#endif
320
321
0
        UA_LOG_INFO_PUBSUB(psm->logging, wg, "WriterGroup deleted");
322
323
0
        UA_WriterGroupConfig_clear(&wg->config);
324
0
        UA_PubSubComponentHead_clear(&wg->head);
325
0
        UA_free(wg);
326
0
    }
327
328
    /* Update the connection state */
329
0
    UA_PubSubConnection_setPubSubState(psm, connection, connection->head.state);
330
331
0
    return UA_STATUSCODE_GOOD;
332
0
}
333
334
UA_StatusCode
335
UA_WriterGroupConfig_copy(const UA_WriterGroupConfig *src,
336
0
                          UA_WriterGroupConfig *dst) {
337
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
338
0
    memcpy(dst, src, sizeof(UA_WriterGroupConfig));
339
0
    res |= UA_String_copy(&src->name, &dst->name);
340
0
    res |= UA_ExtensionObject_copy(&src->transportSettings, &dst->transportSettings);
341
0
    res |= UA_ExtensionObject_copy(&src->messageSettings, &dst->messageSettings);
342
0
    res |= UA_KeyValueMap_copy(&src->groupProperties, &dst->groupProperties);
343
0
    res |= UA_String_copy(&src->securityGroupId, &dst->securityGroupId);
344
0
    if(res != UA_STATUSCODE_GOOD)
345
0
        UA_WriterGroupConfig_clear(dst);
346
0
    return res;
347
0
}
348
349
UA_WriterGroup *
350
0
UA_WriterGroup_find(UA_PubSubManager *psm, const UA_NodeId id) {
351
0
    if(!psm)
352
0
        return NULL;
353
0
    UA_PubSubConnection *c;
354
0
    TAILQ_FOREACH(c, &psm->connections, listEntry) {
355
0
        UA_WriterGroup *wg;
356
0
        LIST_FOREACH(wg, &c->writerGroups, listEntry) {
357
0
            if(UA_NodeId_equal(&id, &wg->head.identifier))
358
0
                return wg;
359
0
        }
360
0
    }
361
0
    return NULL;
362
0
}
363
364
UA_StatusCode
365
UA_WriterGroup_setEncryptionKeys(UA_PubSubManager *psm, UA_WriterGroup *wg,
366
                                 UA_UInt32 securityTokenId,
367
                                 const UA_ByteString signingKey,
368
                                 const UA_ByteString encryptingKey,
369
0
                                 const UA_ByteString keyNonce) {
370
0
    if(wg->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON) {
371
0
        UA_LOG_WARNING_PUBSUB(psm->logging, wg,
372
0
                              "JSON encoding is enabled. The message security "
373
0
                              "iis only defined for the UADP message mapping.");
374
0
        return UA_STATUSCODE_BADINTERNALERROR;
375
0
    }
376
377
0
    if(!wg->config.securityPolicy) {
378
0
        UA_LOG_WARNING_PUBSUB(psm->logging, wg,
379
0
                              "No SecurityPolicy configured for the WriterGroup");
380
0
        return UA_STATUSCODE_BADINTERNALERROR;
381
0
    }
382
383
0
    if(securityTokenId != wg->securityTokenId) {
384
0
        wg->securityTokenId = securityTokenId;
385
0
        wg->nonceSequenceNumber = 1;
386
0
    }
387
388
0
    UA_PubSubSecurityPolicy *sp = wg->config.securityPolicy;
389
0
    UA_StatusCode res = UA_STATUSCODE_BAD;
390
0
    if(!wg->securityPolicyContext) {
391
        /* Create a new context */
392
0
        res = sp->newGroupContext(sp, &signingKey, &encryptingKey, &keyNonce,
393
0
                                  &wg->securityPolicyContext);
394
0
    } else {
395
        /* Update the context */
396
0
        res = sp->setSecurityKeys(sp, wg->securityPolicyContext, &signingKey,
397
0
                                  &encryptingKey, &keyNonce);
398
0
    }
399
400
0
    return (res == UA_STATUSCODE_GOOD) ?
401
0
        UA_WriterGroup_setPubSubState(psm, wg, wg->head.state) : res;
402
0
}
403
404
void
405
0
UA_WriterGroupConfig_clear(UA_WriterGroupConfig *writerGroupConfig) {
406
0
    UA_String_clear(&writerGroupConfig->name);
407
0
    UA_ExtensionObject_clear(&writerGroupConfig->transportSettings);
408
0
    UA_ExtensionObject_clear(&writerGroupConfig->messageSettings);
409
0
    UA_KeyValueMap_clear(&writerGroupConfig->groupProperties);
410
0
    UA_String_clear(&writerGroupConfig->securityGroupId);
411
0
    memset(writerGroupConfig, 0, sizeof(UA_WriterGroupConfig));
412
0
}
413
414
UA_StatusCode
415
UA_WriterGroup_setPubSubState(UA_PubSubManager *psm, UA_WriterGroup *wg,
416
0
                              UA_PubSubState targetState) {
417
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
418
419
0
    if(wg->deleteFlag && targetState != UA_PUBSUBSTATE_DISABLED) {
420
0
        UA_LOG_WARNING_PUBSUB(psm->logging, wg,
421
0
                              "The WriterGroup is being deleted. Can only be disabled.");
422
0
        return UA_STATUSCODE_BADINTERNALERROR;
423
0
    }
424
425
    /* Callback to modify the WriterGroup config and change the targetState
426
     * before the state machine executes */
427
0
    UA_Server *server = psm->sc.server;
428
0
    if(server->config.pubSubConfig.beforeStateChangeCallback) {
429
0
        server->config.pubSubConfig.
430
0
            beforeStateChangeCallback(server, wg->head.identifier, &targetState);
431
0
    }
432
433
    /* Are we doing a top-level state update or recursively? */
434
0
    UA_Boolean isTransient = wg->head.transientState;
435
0
    wg->head.transientState = true;
436
437
0
    UA_StatusCode ret = UA_STATUSCODE_GOOD;
438
0
    UA_PubSubState oldState = wg->head.state;
439
0
    UA_PubSubConnection *connection = wg->linkedConnection;
440
441
    /* Custom state machine */
442
0
    if(wg->config.customStateMachine) {
443
0
        ret = wg->config.customStateMachine(server, wg->head.identifier, wg->config.context,
444
0
                                            &wg->head.state, targetState);
445
0
        goto finalize_state_machine;
446
0
    }
447
448
    /* Internal state machine */
449
0
    switch(targetState) {
450
        /* Disabled or Error */
451
0
    case UA_PUBSUBSTATE_DISABLED:
452
0
    case UA_PUBSUBSTATE_ERROR:
453
0
        wg->head.state = targetState;
454
0
        UA_WriterGroup_disconnect(wg);
455
0
        UA_WriterGroup_removePublishCallback(psm, wg);
456
0
        break;
457
458
        /* Enabled */
459
0
    case UA_PUBSUBSTATE_PAUSED:
460
0
    case UA_PUBSUBSTATE_PREOPERATIONAL:
461
0
    case UA_PUBSUBSTATE_OPERATIONAL:
462
        /* PAUSED has no open connections and periodic callbacks */
463
0
        if(psm->sc.state != UA_LIFECYCLESTATE_STARTED) {
464
            /* Avoid repeat warnings */
465
0
            if(oldState != UA_PUBSUBSTATE_PAUSED) {
466
0
                UA_LOG_WARNING_PUBSUB(psm->logging, wg,
467
0
                                      "Cannot enable the WriterGroup while the "
468
0
                                      "server is not running -> Paused State");
469
0
            }
470
0
            wg->head.state = UA_PUBSUBSTATE_PAUSED;
471
0
            UA_WriterGroup_disconnect(wg);
472
0
            UA_WriterGroup_removePublishCallback(psm, wg);
473
0
            break;
474
0
        }
475
476
0
        if(connection->head.state != UA_PUBSUBSTATE_OPERATIONAL) {
477
0
            wg->head.state = UA_PUBSUBSTATE_PAUSED;
478
0
            UA_WriterGroup_disconnect(wg);
479
0
            UA_WriterGroup_removePublishCallback(psm, wg);
480
0
            break;
481
0
        }
482
483
0
        wg->head.state = UA_PUBSUBSTATE_OPERATIONAL;
484
485
        /* Not fully connected -> connect */
486
0
        if(UA_WriterGroup_canConnect(wg)) {
487
0
            ret = UA_WriterGroup_connect(psm, wg, false);
488
0
            if(ret != UA_STATUSCODE_GOOD)
489
0
                break;
490
0
        }
491
492
        /* Security Mode not set-> PreOperational */
493
0
        if(wg->config.securityMode > UA_MESSAGESECURITYMODE_NONE &&
494
0
           wg->securityTokenId == 0)
495
0
            wg->head.state = UA_PUBSUBSTATE_PREOPERATIONAL;
496
497
        /* Enable publish callback if operational */
498
0
        if(wg->head.state == UA_PUBSUBSTATE_OPERATIONAL)
499
0
            ret = UA_WriterGroup_addPublishCallback(psm, wg);
500
0
        break;
501
502
        /* Unknown case */
503
0
    default:
504
0
        ret = UA_STATUSCODE_BADINTERNALERROR;
505
0
        break;
506
0
    }
507
508
    /* Failure */
509
0
    if(ret != UA_STATUSCODE_GOOD) {
510
0
        wg->head.state = UA_PUBSUBSTATE_ERROR;
511
0
        UA_WriterGroup_disconnect(wg);
512
0
        UA_WriterGroup_removePublishCallback(psm, wg);
513
0
    }
514
515
0
 finalize_state_machine:
516
517
    /* Only the top-level state update (if recursive calls are happening)
518
     * notifies the application and updates Reader and WriterGroups */
519
0
    wg->head.transientState = isTransient;
520
0
    if(wg->head.transientState)
521
0
        return ret;
522
523
    /* No state change has happened */
524
0
    if(wg->head.state == oldState)
525
0
        return ret;
526
527
0
    UA_LOG_INFO_PUBSUB(psm->logging, wg, "%s -> %s",
528
0
                       UA_PubSubState_name(oldState),
529
0
                       UA_PubSubState_name(wg->head.state));
530
531
    /* Inform the application about state change */
532
0
    if(server->config.pubSubConfig.stateChangeCallback)
533
0
        server->config.pubSubConfig.
534
0
            stateChangeCallback(server, wg->head.identifier, wg->head.state, ret);
535
536
    /* Children evaluate their state machine after the state change of the parent.
537
     * Keep the current child state as the target state for the child. */
538
0
    UA_DataSetWriter *writer;
539
0
    LIST_FOREACH(writer, &wg->writers, listEntry) {
540
0
        if(psm->pubSubInitialSetupMode && writer->config.enabled) {
541
0
            UA_DataSetWriter_setPubSubState(psm, writer, UA_PUBSUBSTATE_OPERATIONAL);
542
0
        } else {
543
0
            UA_DataSetWriter_setPubSubState(psm, writer, writer->head.state);
544
0
        }
545
0
    }
546
547
    /* Update the PubSubManager state. It will go from STOPPING to STOPPED when
548
     * the last socket has closed. */
549
0
    UA_PubSubManager_setState(psm, psm->sc.state);
550
551
0
    return ret;
552
0
}
553
554
static UA_StatusCode
555
encryptAndSign(UA_WriterGroup *wg, const UA_NetworkMessage *nm,
556
               UA_Byte *signStart, UA_Byte *encryptStart,
557
0
               UA_Byte *msgEnd) {
558
0
    UA_StatusCode rv;
559
0
    void *channelContext = wg->securityPolicyContext;
560
561
0
    UA_PubSubSecurityPolicy *sp = wg->config.securityPolicy;
562
563
0
    if(nm->securityHeader.networkMessageEncrypted) {
564
        /* Set the temporary MessageNonce in the SecurityPolicy */
565
0
        const UA_ByteString nonce = {
566
0
            (size_t)nm->securityHeader.messageNonceSize,
567
0
            (UA_Byte*)(uintptr_t)nm->securityHeader.messageNonce
568
0
        };
569
0
        rv = sp->setMessageNonce(sp, channelContext, &nonce);
570
0
        UA_CHECK_STATUS(rv, return rv);
571
572
        /* The encryption is done in-place, no need to encode again */
573
0
        UA_ByteString toBeEncrypted =
574
0
            {(uintptr_t)msgEnd - (uintptr_t)encryptStart, encryptStart};
575
0
        rv = sp->encrypt(sp, channelContext, &toBeEncrypted);
576
0
        UA_CHECK_STATUS(rv, return rv);
577
0
    }
578
579
0
    if(nm->securityHeader.networkMessageSigned) {
580
0
        UA_ByteString toBeSigned =
581
0
            {(uintptr_t)msgEnd - (uintptr_t)signStart, signStart};
582
583
0
        size_t sigSize = sp->getSignatureSize(sp, channelContext);
584
0
        UA_ByteString signature = {sigSize, msgEnd};
585
586
0
        rv = sp->sign(sp, channelContext, &toBeSigned, &signature);
587
0
        UA_CHECK_STATUS(rv, return rv);
588
0
    }
589
0
    return UA_STATUSCODE_GOOD;
590
0
}
591
592
static UA_StatusCode
593
encodeNetworkMessage(UA_WriterGroup *wg, PubSubEncodeCtx *ctx,
594
0
                     UA_NetworkMessage *nm, UA_ByteString *buf) {
595
0
    UA_Byte *networkMessageStart = buf->data;
596
0
    UA_StatusCode rv = UA_NetworkMessage_encodeHeaders(ctx, nm);
597
0
    UA_CHECK_STATUS(rv, return rv);
598
599
0
    UA_Byte *payloadStart = ctx->ctx.pos;
600
0
    rv = UA_NetworkMessage_encodePayload(ctx, nm);
601
0
    UA_CHECK_STATUS(rv, return rv);
602
603
0
    rv = UA_NetworkMessage_encodeFooters(ctx, nm);
604
0
    UA_CHECK_STATUS(rv, return rv);
605
606
    /* Encrypt and Sign the message */
607
0
    UA_Byte *footerEnd = ctx->ctx.pos;
608
0
    return encryptAndSign(wg, nm, networkMessageStart, payloadStart, footerEnd);
609
0
}
610
611
static void
612
sendNetworkMessageBuffer(UA_PubSubManager *psm, UA_WriterGroup *wg, 
613
                         UA_PubSubConnection *connection, uintptr_t connectionId,
614
0
                         UA_ByteString *buffer) {
615
0
    UA_StatusCode res = connection->cm->
616
0
        sendWithConnection(connection->cm, connectionId,
617
0
                           &UA_KEYVALUEMAP_NULL, buffer);
618
619
    /* Failure, set the WriterGroup into an error mode */
620
0
    if(res != UA_STATUSCODE_GOOD) {
621
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
622
0
                            "Sending NetworkMessage failed");
623
0
        UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_ERROR);
624
0
        UA_PubSubConnection_setPubSubState(psm, connection, UA_PUBSUBSTATE_ERROR);
625
0
        return;
626
0
    }
627
628
    /* Sending successful - increase the sequence number */
629
0
    wg->sequenceNumber++;
630
0
}
631
632
#ifdef UA_ENABLE_JSON_ENCODING
633
static UA_StatusCode
634
sendNetworkMessageJson(UA_PubSubManager *psm, UA_PubSubConnection *connection, UA_WriterGroup *wg,
635
0
                       UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount) {
636
    /* Prepare the NetworkMessage */
637
0
    UA_NetworkMessage nm;
638
0
    memset(&nm, 0, sizeof(UA_NetworkMessage));
639
0
    nm.version = 1;
640
0
    nm.networkMessageType = UA_NETWORKMESSAGE_DATASET;
641
0
    nm.payloadHeaderEnabled = true;
642
0
    nm.payload.dataSetMessages = dsm;
643
0
    nm.messageCount = dsmCount;
644
0
    nm.publisherIdEnabled = true;
645
0
    nm.publisherId = connection->config.publisherId;
646
647
0
    for(size_t i = 0; i < dsmCount; i++)
648
0
        nm.dataSetWriterIds[i] = writerIds[i];
649
650
0
    PubSubEncodeJsonCtx ctx;
651
0
    memset(&ctx, 0, sizeof(PubSubEncodeJsonCtx));
652
653
    /* Prepare the metadata to encode the DataSetMessages */
654
0
    size_t i = 0;
655
0
    UA_STACKARRAY(UA_DataSetMessage_EncodingMetaData, emd, wg->writersCount);
656
0
    memset(emd, 0, sizeof(UA_DataSetMessage_EncodingMetaData) * wg->writersCount);
657
0
    ctx.eo.metaData = emd;
658
0
    ctx.eo.metaDataSize = wg->writersCount;
659
0
    UA_DataSetWriter *dsw;
660
0
    LIST_FOREACH(dsw, &wg->writers, listEntry) {
661
0
        emd[i].dataSetWriterId = dsw->config.dataSetWriterId;
662
0
        UA_PublishedDataSet *pds = dsw->connectedDataSet;
663
0
        if(pds) {
664
0
            emd[i].fields = pds->dataSetMetaData.fields;
665
0
            emd[i].fieldsSize = pds->dataSetMetaData.fieldsSize;
666
0
        }
667
0
        i++;
668
0
    }
669
670
    /* Compute the message length */
671
0
    size_t msgSize = UA_NetworkMessage_calcSizeJson(&nm, &ctx.eo, NULL);
672
673
0
    UA_ConnectionManager *cm = connection->cm;
674
0
    if(!cm)
675
0
        return UA_STATUSCODE_BADINTERNALERROR;
676
677
    /* Select the wg sendchannel if configured */
678
0
    uintptr_t sendChannel = connection->sendChannel;
679
0
    if(wg->sendChannel != 0)
680
0
        sendChannel = wg->sendChannel;
681
0
    if(sendChannel == 0) {
682
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg, "Cannot send, no open connection");
683
0
        return UA_STATUSCODE_BADINTERNALERROR;
684
0
    }
685
686
    /* Allocate the buffer */
687
0
    UA_ByteString buf;
688
0
    UA_StatusCode res = cm->allocNetworkBuffer(cm, sendChannel, &buf, msgSize);
689
0
    UA_CHECK_STATUS(res, return res);
690
691
    /* Encode the message */
692
0
    ctx.ctx.pos = buf.data;
693
0
    ctx.ctx.end = &buf.data[msgSize];
694
0
    res = UA_NetworkMessage_encodeJsonInternal(&ctx, &nm);
695
0
    if(res != UA_STATUSCODE_GOOD) {
696
0
        cm->freeNetworkBuffer(cm, sendChannel, &buf);
697
0
        return res;
698
0
    }
699
0
    UA_assert(ctx.ctx.pos == ctx.ctx.end);
700
701
    /* Send the prepared messages */
702
0
    sendNetworkMessageBuffer(psm, wg, connection, sendChannel, &buf);
703
0
    return UA_STATUSCODE_GOOD;
704
0
}
705
#endif
706
707
static UA_StatusCode
708
generateNetworkMessage(UA_PubSubConnection *connection, UA_WriterGroup *wg,
709
                       UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount,
710
                       UA_ExtensionObject *messageSettings,
711
                       UA_ExtensionObject *transportSettings,
712
0
                       UA_NetworkMessage *nm) {
713
    /* Defense-in-depth: the dataSetWriterIds array in UA_NetworkMessage is
714
     * fixed at UA_NETWORKMESSAGE_MAXMESSAGECOUNT (32) entries. Reject callers
715
     * that would overflow it. */
716
0
    if(dsmCount >= UA_NETWORKMESSAGE_MAXMESSAGECOUNT)
717
0
        return UA_STATUSCODE_BADINTERNALERROR;
718
0
    UA_UadpWriterGroupMessageDataType tmpWgm;
719
0
    UA_UadpWriterGroupMessageDataType *wgm;
720
0
    if(UA_ExtensionObject_hasDecodedType(messageSettings,
721
0
           &UA_TYPES[UA_TYPES_UADPWRITERGROUPMESSAGEDATATYPE])) {
722
0
        wgm = (UA_UadpWriterGroupMessageDataType*)messageSettings->content.decoded.data;
723
0
    } else {
724
        /* Use default settings */
725
0
        UA_UadpWriterGroupMessageDataType_init(&tmpWgm);
726
0
        wgm = &tmpWgm;
727
0
    }
728
729
0
    nm->publisherIdEnabled =
730
0
        ((u64)wgm->networkMessageContentMask &
731
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PUBLISHERID) != 0;
732
0
    nm->groupHeaderEnabled =
733
0
        ((u64)wgm->networkMessageContentMask &
734
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPHEADER) != 0;
735
0
    nm->groupHeader.writerGroupIdEnabled =
736
0
        ((u64)wgm->networkMessageContentMask &
737
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_WRITERGROUPID) != 0;
738
0
    nm->groupHeader.groupVersionEnabled =
739
0
        ((u64)wgm->networkMessageContentMask &
740
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_GROUPVERSION) != 0;
741
0
    nm->groupHeader.networkMessageNumberEnabled =
742
0
        ((u64)wgm->networkMessageContentMask &
743
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_NETWORKMESSAGENUMBER) != 0;
744
0
    nm->groupHeader.sequenceNumberEnabled =
745
0
        ((u64)wgm->networkMessageContentMask &
746
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_SEQUENCENUMBER) != 0;
747
0
    nm->payloadHeaderEnabled =
748
0
        ((u64)wgm->networkMessageContentMask &
749
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PAYLOADHEADER) != 0;
750
0
    nm->timestampEnabled =
751
0
        ((u64)wgm->networkMessageContentMask &
752
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_TIMESTAMP) != 0;
753
0
    nm->picosecondsEnabled =
754
0
        ((u64)wgm->networkMessageContentMask &
755
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PICOSECONDS) != 0;
756
0
    nm->dataSetClassIdEnabled =
757
0
        ((u64)wgm->networkMessageContentMask &
758
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_DATASETCLASSID) != 0;
759
0
    nm->promotedFieldsEnabled =
760
0
        ((u64)wgm->networkMessageContentMask &
761
0
         (u64)UA_UADPNETWORKMESSAGECONTENTMASK_PROMOTEDFIELDS) != 0;
762
763
    /* Set the SecurityHeader */
764
0
    if(wg->config.securityMode > UA_MESSAGESECURITYMODE_NONE) {
765
0
        nm->securityEnabled = true;
766
0
        nm->securityHeader.networkMessageSigned = true;
767
0
        if(wg->config.securityMode >= UA_MESSAGESECURITYMODE_SIGNANDENCRYPT)
768
0
            nm->securityHeader.networkMessageEncrypted = true;
769
0
        nm->securityHeader.securityTokenId = wg->securityTokenId;
770
771
        /* Generate the MessageNonce. Four random bytes followed by a four-byte
772
         * sequence number */
773
0
        UA_PubSubSecurityPolicy *sp = wg->config.securityPolicy;
774
0
        UA_ByteString nonce = {4, nm->securityHeader.messageNonce};
775
0
        UA_StatusCode rv = sp->generateNonce(sp, wg->securityPolicyContext, &nonce);
776
0
        if(rv != UA_STATUSCODE_GOOD)
777
0
            return rv;
778
0
        UA_Byte *pos = &nm->securityHeader.messageNonce[4];
779
0
        const UA_Byte *end = &nm->securityHeader.messageNonce[8];
780
0
        UA_UInt32_encodeBinary(&wg->nonceSequenceNumber, &pos, end);
781
0
        nm->securityHeader.messageNonceSize = 8;
782
        /* Part of the MessageNonce that makes it unique for a given key */
783
0
        wg->nonceSequenceNumber++;
784
0
    }
785
786
0
    nm->version = 1;
787
0
    nm->networkMessageType = UA_NETWORKMESSAGE_DATASET;
788
    /* shallow copy of the PublisherId from connection configuration
789
        -> the configuration needs to be stable during publishing process
790
        -> it must not be cleaned after network message has been sent */
791
0
    nm->publisherId = connection->config.publisherId;
792
793
0
    if(nm->groupHeader.sequenceNumberEnabled)
794
0
        nm->groupHeader.sequenceNumber = wg->sequenceNumber;
795
796
0
    if(nm->groupHeader.groupVersionEnabled)
797
0
        nm->groupHeader.groupVersion = wgm->groupVersion;
798
799
0
    nm->groupHeader.writerGroupId = wg->config.writerGroupId;
800
    /* number of the NetworkMessage inside a PublishingInterval */
801
0
    nm->groupHeader.networkMessageNumber = 1;
802
0
    nm->payload.dataSetMessages = dsm;
803
0
    nm->messageCount = dsmCount;
804
805
0
    for(size_t i = 0; i < dsmCount; i++)
806
0
        nm->dataSetWriterIds[i] = writerIds[i];
807
808
0
    return UA_STATUSCODE_GOOD;
809
0
}
810
811
static UA_StatusCode
812
sendNetworkMessageBinary(UA_PubSubManager *psm, UA_PubSubConnection *connection,
813
                         UA_WriterGroup *wg, UA_DataSetMessage *dsm, UA_UInt16 *writerIds,
814
0
                         UA_Byte dsmCount) {
815
0
    UA_NetworkMessage nm;
816
0
    memset(&nm, 0, sizeof(UA_NetworkMessage));
817
818
    /* Fill the message structure */
819
0
    UA_StatusCode rv =
820
0
        generateNetworkMessage(connection, wg, dsm, writerIds, dsmCount,
821
0
                               &wg->config.messageSettings,
822
0
                               &wg->config.transportSettings, &nm);
823
0
    UA_CHECK_STATUS(rv, return rv);
824
825
0
    PubSubEncodeCtx ctx;
826
0
    memset(&ctx, 0, sizeof(PubSubEncodeCtx));
827
828
    /* Prepare the metadata with information from the readers to decode the
829
     * DataSetMessages */
830
0
    size_t i = 0;
831
0
    UA_STACKARRAY(UA_DataSetMessage_EncodingMetaData, emd, wg->writersCount);
832
0
    memset(emd, 0, sizeof(UA_DataSetMessage_EncodingMetaData) * wg->writersCount);
833
0
    ctx.eo.metaData = emd;
834
0
    ctx.eo.metaDataSize = wg->writersCount;
835
0
    UA_DataSetWriter *dsw;
836
0
    LIST_FOREACH(dsw, &wg->writers, listEntry) {
837
0
        emd[i].dataSetWriterId = dsw->config.dataSetWriterId;
838
0
        UA_PublishedDataSet *pds = dsw->connectedDataSet;
839
0
        if(pds) {
840
0
            emd[i].fields = pds->dataSetMetaData.fields;
841
0
            emd[i].fieldsSize = pds->dataSetMetaData.fieldsSize;
842
0
        }
843
0
        i++;
844
0
    }
845
846
    /* Compute the message size. Add the overhead for the security signature.
847
     * There is no padding and the encryption incurs no size overhead. */
848
0
    size_t msgSize = UA_NetworkMessage_calcSizeBinaryInternal(&ctx, &nm);
849
0
    if(msgSize == 0)
850
0
        return UA_STATUSCODE_BADINTERNALERROR;
851
852
    /* Add the overhead for the security signature.
853
     * There is no padding and the encryption incurs no size overhead. Use the
854
     * same WriterGroup context as the signing operation below. */
855
0
    if(wg->config.securityMode > UA_MESSAGESECURITYMODE_NONE) {
856
0
        UA_PubSubSecurityPolicy *sp = wg->config.securityPolicy;
857
0
        msgSize += sp->getSignatureSize(sp, wg->securityPolicyContext);
858
0
    }
859
860
0
    UA_ConnectionManager *cm = connection->cm;
861
0
    if(!cm)
862
0
        return UA_STATUSCODE_BADINTERNALERROR;
863
864
    /* Select the wg sendchannel if configured */
865
0
    uintptr_t sendChannel = connection->sendChannel;
866
0
    if(wg->sendChannel != 0)
867
0
        sendChannel = wg->sendChannel;
868
0
    if(sendChannel == 0) {
869
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg, "Cannot send, no open connection");
870
0
        return UA_STATUSCODE_BADINTERNALERROR;
871
0
    }
872
873
    /* Allocate the buffer. Allocate on the stack if the buffer is small. */
874
0
    UA_ByteString buf = UA_BYTESTRING_NULL;
875
0
    rv = cm->allocNetworkBuffer(cm, sendChannel, &buf, msgSize);
876
0
    UA_CHECK_STATUS(rv, return rv);
877
878
    /* Encode and encrypt the message */
879
0
    ctx.ctx.pos = buf.data;
880
0
    ctx.ctx.end = &buf.data[buf.length];
881
0
    rv = encodeNetworkMessage(wg, &ctx, &nm, &buf);
882
0
    if(rv != UA_STATUSCODE_GOOD) {
883
0
        cm->freeNetworkBuffer(cm, sendChannel, &buf);
884
0
        return rv;
885
0
    }
886
887
    /* Send out the message */
888
0
    sendNetworkMessageBuffer(psm, wg, connection, sendChannel, &buf);
889
0
    return UA_STATUSCODE_GOOD;
890
0
}
891
892
static void
893
sendNetworkMessage(UA_PubSubManager *psm, UA_WriterGroup *wg, UA_PubSubConnection *connection,
894
0
                   UA_DataSetMessage *dsm, UA_UInt16 *writerIds, UA_Byte dsmCount) {
895
0
    if(dsmCount >= UA_NETWORKMESSAGE_MAXMESSAGECOUNT) {
896
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
897
0
                            "More DataSetMessages than allowed in "
898
0
                            "UA_NETWORKMESSAGE_MAXMESSAGECOUNT");
899
0
        UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_ERROR);
900
0
        return;
901
0
    }
902
903
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
904
0
    switch(wg->config.encodingMimeType) {
905
0
    case UA_PUBSUB_ENCODING_UADP:
906
0
        res = sendNetworkMessageBinary(psm, connection, wg, dsm, writerIds, dsmCount);
907
0
        break;
908
0
#ifdef UA_ENABLE_JSON_ENCODING
909
0
    case UA_PUBSUB_ENCODING_JSON:
910
0
        res = sendNetworkMessageJson(psm, connection, wg, dsm, writerIds, dsmCount);
911
0
        break;
912
0
#endif
913
0
    default:
914
0
        res = UA_STATUSCODE_BADNOTSUPPORTED;
915
0
        break;
916
0
    }
917
918
    /* If sending failed, disable all writer of the writergroup */
919
0
    if(res != UA_STATUSCODE_GOOD) {
920
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
921
0
                            "PubSub Publish: Could not send a NetworkMessage "
922
0
                            "with status code %s", UA_StatusCode_name(res));
923
0
        UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_ERROR);
924
0
    }
925
0
}
926
927
/* This callback triggers the collection and publish of NetworkMessages and the
928
 * contained DataSetMessages. */
929
void
930
0
UA_WriterGroup_publishCallback(UA_PubSubManager *psm, UA_WriterGroup *wg) {
931
0
    UA_assert(wg != NULL);
932
0
    UA_assert(psm != NULL);
933
934
0
    UA_LOG_DEBUG_PUBSUB(psm->logging, wg, "Publish Callback");
935
936
0
    lockServer(psm->sc.server);
937
938
    /* Find the connection associated with the writer */
939
0
    UA_PubSubConnection *connection = wg->linkedConnection;
940
0
    if(!connection) {
941
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
942
0
                            "Publish failed. PubSubConnection invalid");
943
0
        UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_ERROR);
944
0
        unlockServer(psm->sc.server);
945
0
        return;
946
0
    }
947
948
    /* How many DSM can be sent in one NM? */
949
0
    UA_Byte maxDSM = (UA_Byte)wg->config.maxEncapsulatedDataSetMessageCount;
950
0
    if(wg->config.maxEncapsulatedDataSetMessageCount > UA_BYTE_MAX)
951
0
        maxDSM = UA_BYTE_MAX;
952
0
    if(maxDSM == 0)
953
0
        maxDSM = 1; /* Send at least one dsm */
954
955
    /* It is possible to put several DataSetMessages into one NetworkMessage.
956
     * But only if they do not contain promoted fields. NM with promoted fields
957
     * are sent out right away. The others are kept in a buffer for
958
     * "batching". */
959
0
    size_t dsmCount = 0;
960
0
    UA_STACKARRAY(UA_UInt16, dsWriterIds, wg->writersCount);
961
0
    UA_STACKARRAY(UA_DataSetMessage, dsmStore, wg->writersCount);
962
963
0
    size_t enabledWriters = 0;
964
965
0
    UA_DataSetWriter *dsw;
966
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
967
0
    LIST_FOREACH(dsw, &wg->writers, listEntry) {
968
0
        if(dsw->head.state != UA_PUBSUBSTATE_OPERATIONAL)
969
0
            continue;
970
971
0
        enabledWriters++;
972
973
        /* PDS can be NULL -> Heartbeat */
974
0
        UA_PublishedDataSet *pds = dsw->connectedDataSet;
975
976
        /* Generate the DSM */
977
0
        dsWriterIds[dsmCount] = dsw->config.dataSetWriterId;
978
0
        UA_StatusCode res =
979
0
            UA_DataSetWriter_generateDataSetMessage(psm, dsw, &dsmStore[dsmCount]);
980
0
        if(res != UA_STATUSCODE_GOOD) {
981
0
            UA_LOG_ERROR_PUBSUB(psm->logging, dsw,
982
0
                                "PubSub Publish: DataSetMessage creation failed");
983
0
            UA_DataSetWriter_setPubSubState(psm, dsw, UA_PUBSUBSTATE_ERROR);
984
0
            continue;
985
0
        }
986
987
        /* There is no promoted field -> send right away */
988
0
        if(pds && pds->promotedFieldsCount > 0) {
989
0
            wg->lastPublishTimeStamp = el->dateTime_nowMonotonic(el);
990
0
            sendNetworkMessage(psm, wg, connection, &dsmStore[dsmCount],
991
0
                               &dsWriterIds[dsmCount], 1);
992
993
0
            UA_DataSetMessage_clear(&dsmStore[dsmCount]);
994
0
            continue; /* Don't increase the dsmCount, reuse the slot */
995
0
        }
996
997
0
        dsmCount++;
998
0
    }
999
1000
    /* No enabled Writers */
1001
0
    if(enabledWriters == 0) {
1002
0
        UA_LOG_WARNING_PUBSUB(psm->logging, wg,
1003
0
                              "Cannot publish -- No Writers are enabled");
1004
0
        unlockServer(psm->sc.server);
1005
0
        return;
1006
0
    }
1007
1008
    /* Send the NetworkMessages with batched DataSetMessages */
1009
0
    UA_Byte nmDsmCount = 0;
1010
0
    for(size_t i = 0; i < dsmCount; i += nmDsmCount) {
1011
        /* How many dsm are batched in this iteration? */
1012
0
        nmDsmCount = (i + maxDSM > dsmCount) ? (UA_Byte)(dsmCount - i) : maxDSM;
1013
0
        wg->lastPublishTimeStamp = el->dateTime_nowMonotonic(el);
1014
        /* Send the batched messages */
1015
0
        sendNetworkMessage(psm, wg, connection, &dsmStore[i],
1016
0
                           &dsWriterIds[i], nmDsmCount);
1017
0
    }
1018
1019
    /* Clean up DSM */
1020
0
    for(size_t i = 0; i < dsmCount; i++) {
1021
0
        UA_DataSetMessage_clear(&dsmStore[i]);
1022
0
    }
1023
1024
0
    unlockServer(psm->sc.server);
1025
0
}
1026
1027
/***********************/
1028
/* Connection Handling */
1029
/***********************/
1030
1031
static UA_StatusCode
1032
UA_WriterGroup_connectMQTT(UA_PubSubManager *psm, UA_WriterGroup *wg,
1033
                           UA_Boolean validate);
1034
1035
static UA_StatusCode
1036
UA_WriterGroup_connectUDPUnicast(UA_PubSubManager *psm, UA_WriterGroup *wg,
1037
                                 UA_Boolean validate);
1038
1039
typedef struct  {
1040
    UA_String profileURI;
1041
    UA_String protocol;
1042
    UA_Boolean json;
1043
    UA_StatusCode (*connectWriterGroup)(UA_PubSubManager *psm, UA_WriterGroup *wg,
1044
                                        UA_Boolean validate);
1045
} WriterGroupProfileMapping;
1046
1047
static WriterGroupProfileMapping writerGroupProfiles[UA_PUBSUB_PROFILES_SIZE] = {
1048
    {UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"),
1049
     UA_STRING_STATIC("udp"), false, UA_WriterGroup_connectUDPUnicast},
1050
    {UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-mqtt-uadp"),
1051
     UA_STRING_STATIC("mqtt"), false, UA_WriterGroup_connectMQTT},
1052
    {UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-mqtt-json"),
1053
     UA_STRING_STATIC("mqtt"), true, UA_WriterGroup_connectMQTT},
1054
    {UA_STRING_STATIC("http://opcfoundation.org/UA-Profile/Transport/pubsub-eth-uadp"),
1055
     UA_STRING_STATIC("eth"), false, NULL}
1056
};
1057
1058
static void
1059
WriterGroupChannelCallback(UA_ConnectionManager *cm, uintptr_t connectionId,
1060
                          void *application, void **connectionContext,
1061
                          UA_ConnectionState state, const UA_KeyValueMap *params,
1062
0
                          UA_ByteString msg) {
1063
0
    if(!connectionContext)
1064
0
        return;
1065
1066
    /* Get the context pointers */
1067
0
    UA_WriterGroup *wg = (UA_WriterGroup*)*connectionContext;
1068
0
    UA_PubSubManager *psm = (UA_PubSubManager*)application;
1069
0
    UA_Server *server = psm->sc.server;
1070
1071
0
    lockServer(server);
1072
1073
    /* The connection is closing in the EventLoop. This is the last callback
1074
     * from that connection. Clean up the SecureChannel in the client. */
1075
0
    if(state == UA_CONNECTIONSTATE_CLOSING) {
1076
0
        if(wg->sendChannel == connectionId) {
1077
            /* Reset the connection channel */
1078
0
            wg->sendChannel = 0;
1079
1080
            /* PSC marked for deletion and the last EventLoop connection has closed */
1081
0
            if(wg->deleteFlag) {
1082
0
                UA_WriterGroup_remove(psm, wg);
1083
0
                unlockServer(server);
1084
0
                return;
1085
0
            }
1086
0
        }
1087
1088
        /* Reconnect automatically if the connection was operational. This sets
1089
         * the connection state if connecting fails. Attention! If there are
1090
         * several send or recv channels, then the connection is only reopened if
1091
         * all of them close - which is usually the case. */
1092
0
        if(wg->head.state == UA_PUBSUBSTATE_OPERATIONAL)
1093
0
            UA_WriterGroup_connect(psm, wg, false);
1094
1095
        /* Switch the psm state from stopping to stopped once the last
1096
         * connection has closed */
1097
0
        UA_PubSubManager_setState(psm, psm->sc.state);
1098
1099
0
        unlockServer(server);
1100
0
        return;
1101
0
    }
1102
1103
    /* Store the connectionId (if a new connection) */
1104
0
    if(wg->sendChannel && wg->sendChannel != connectionId) {
1105
0
        UA_LOG_WARNING_PUBSUB(psm->logging, wg,
1106
0
                              "WriterGroup is already bound to a different channel");
1107
0
        unlockServer(server);
1108
0
        return;
1109
0
    }
1110
0
    wg->sendChannel = connectionId;
1111
1112
    /* Connection open, set to operational if not already done */
1113
0
    UA_WriterGroup_setPubSubState(psm, wg, wg->head.state);
1114
    
1115
    /* Send-channels don't receive messages */
1116
0
    unlockServer(server);
1117
0
}
1118
1119
static UA_StatusCode
1120
UA_WriterGroup_connectUDPUnicast(UA_PubSubManager *psm, UA_WriterGroup *wg,
1121
0
                                 UA_Boolean validate) {
1122
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
1123
1124
    /* Already connected? */
1125
0
    if(wg->sendChannel != 0 && !validate)
1126
0
        return UA_STATUSCODE_GOOD;
1127
1128
    /* Check if address is available in TransportSettings */
1129
0
    if(((wg->config.transportSettings.encoding == UA_EXTENSIONOBJECT_DECODED ||
1130
0
         wg->config.transportSettings.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) &&
1131
0
        wg->config.transportSettings.content.decoded.type ==
1132
0
        &UA_TYPES[UA_TYPES_DATAGRAMWRITERGROUPTRANSPORTDATATYPE]))
1133
0
        return UA_STATUSCODE_GOOD;
1134
1135
    /* Unpack the TransportSettings */
1136
0
    if((wg->config.transportSettings.encoding != UA_EXTENSIONOBJECT_DECODED &&
1137
0
        wg->config.transportSettings.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
1138
0
       wg->config.transportSettings.content.decoded.type !=
1139
0
       &UA_TYPES[UA_TYPES_DATAGRAMWRITERGROUPTRANSPORT2DATATYPE]) {
1140
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1141
0
                            "Invalid TransportSettings for a UDP Connection");
1142
0
        return UA_STATUSCODE_BADINTERNALERROR;
1143
0
    }
1144
0
    UA_DatagramWriterGroupTransport2DataType *ts =
1145
0
        (UA_DatagramWriterGroupTransport2DataType*)
1146
0
        wg->config.transportSettings.content.decoded.data;
1147
1148
    /* Unpack the address */
1149
0
    if((ts->address.encoding != UA_EXTENSIONOBJECT_DECODED &&
1150
0
        ts->address.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
1151
0
       ts->address.content.decoded.type != &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]) {
1152
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1153
0
                            "Invalid TransportSettings Address for a UDP Connection");
1154
0
        return UA_STATUSCODE_BADINTERNALERROR;
1155
0
    }
1156
0
    UA_NetworkAddressUrlDataType *addressUrl = (UA_NetworkAddressUrlDataType *)
1157
0
        ts->address.content.decoded.data;
1158
1159
    /* Extract hostname and port */
1160
0
    UA_String address;
1161
0
    UA_UInt16 port;
1162
0
    UA_StatusCode res = UA_parseEndpointUrl(&addressUrl->url, &address, &port, NULL);
1163
0
    if(res != UA_STATUSCODE_GOOD) {
1164
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1165
0
                            "Could not parse the UDP network URL");
1166
0
        return res;
1167
0
    }
1168
1169
    /* Set up the connection parameters */
1170
0
    UA_Boolean listen = false;
1171
0
    UA_KeyValuePair kvp[5];
1172
0
    UA_KeyValueMap kvm = {4, kvp};
1173
0
    kvp[0].key = UA_QUALIFIEDNAME(0, "address");
1174
0
    UA_Variant_setScalar(&kvp[0].value, &address, &UA_TYPES[UA_TYPES_STRING]);
1175
0
    kvp[1].key = UA_QUALIFIEDNAME(0, "port");
1176
0
    UA_Variant_setScalar(&kvp[1].value, &port, &UA_TYPES[UA_TYPES_UINT16]);
1177
0
    kvp[2].key = UA_QUALIFIEDNAME(0, "listen");
1178
0
    UA_Variant_setScalar(&kvp[2].value, &listen, &UA_TYPES[UA_TYPES_BOOLEAN]);
1179
0
    kvp[3].key = UA_QUALIFIEDNAME(0, "validate");
1180
0
    UA_Variant_setScalar(&kvp[3].value, &validate, &UA_TYPES[UA_TYPES_BOOLEAN]);
1181
0
    if(!UA_String_isEmpty(&addressUrl->networkInterface)) {
1182
0
        kvp[4].key = UA_QUALIFIEDNAME(0, "interface");
1183
0
        UA_Variant_setScalar(&kvp[4].value, &addressUrl->networkInterface,
1184
0
                             &UA_TYPES[UA_TYPES_STRING]);
1185
0
        kvm.mapSize++;
1186
0
    }
1187
1188
    /* Connect */
1189
0
    UA_ConnectionManager *cm = wg->linkedConnection->cm;
1190
0
    res = cm->openConnection(cm, &kvm, psm, wg, WriterGroupChannelCallback);
1191
0
    if(res != UA_STATUSCODE_GOOD) {
1192
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg, "Could not open a UDP send channel");
1193
0
    }
1194
0
    return res;
1195
0
}
1196
1197
static UA_StatusCode
1198
UA_WriterGroup_connectMQTT(UA_PubSubManager *psm, UA_WriterGroup *wg,
1199
0
                           UA_Boolean validate) {
1200
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
1201
1202
0
    UA_PubSubConnection *c = wg->linkedConnection;
1203
0
    UA_NetworkAddressUrlDataType *addressUrl = (UA_NetworkAddressUrlDataType*)
1204
0
        c->config.address.data;
1205
1206
    /* Get the TransportSettings */
1207
0
    UA_ExtensionObject *ts = &wg->config.transportSettings;
1208
0
    if((ts->encoding != UA_EXTENSIONOBJECT_DECODED &&
1209
0
        ts->encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) ||
1210
0
       ts->content.decoded.type !=
1211
0
       &UA_TYPES[UA_TYPES_BROKERWRITERGROUPTRANSPORTDATATYPE]) {
1212
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg, "Wrong TransportSettings type for MQTT");
1213
0
        return UA_STATUSCODE_BADINTERNALERROR;
1214
0
    }
1215
0
    UA_BrokerWriterGroupTransportDataType *transportSettings =
1216
0
        (UA_BrokerWriterGroupTransportDataType*)ts->content.decoded.data;
1217
1218
    /* Extract hostname and port */
1219
0
    UA_String address;
1220
0
    UA_UInt16 port = 1883; /* Default */
1221
0
    UA_StatusCode res = UA_parseEndpointUrl(&addressUrl->url, &address, &port, NULL);
1222
0
    if(res != UA_STATUSCODE_GOOD) {
1223
0
        UA_LOG_ERROR_PUBSUB(psm->logging, c, "Could not parse the MQTT network URL");
1224
0
        return res;
1225
0
    }
1226
1227
    /* Set up the connection parameters.
1228
     * TODO: Complete the MQTT parameters. */
1229
0
    UA_Boolean listen = false;
1230
0
    UA_KeyValuePair kvp[5];
1231
0
    UA_KeyValueMap kvm = {5, kvp};
1232
0
    kvp[0].key = UA_QUALIFIEDNAME(0, "address");
1233
0
    UA_Variant_setScalar(&kvp[0].value, &address, &UA_TYPES[UA_TYPES_STRING]);
1234
0
    kvp[1].key = UA_QUALIFIEDNAME(0, "subscribe");
1235
0
    UA_Variant_setScalar(&kvp[1].value, &listen, &UA_TYPES[UA_TYPES_BOOLEAN]);
1236
0
    kvp[2].key = UA_QUALIFIEDNAME(0, "port");
1237
0
    UA_Variant_setScalar(&kvp[2].value, &port, &UA_TYPES[UA_TYPES_UINT16]);
1238
0
    kvp[3].key = UA_QUALIFIEDNAME(0, "topic");
1239
0
    UA_Variant_setScalar(&kvp[3].value, &transportSettings->queueName,
1240
0
                         &UA_TYPES[UA_TYPES_STRING]);
1241
0
    kvp[4].key = UA_QUALIFIEDNAME(0, "validate");
1242
0
    UA_Variant_setScalar(&kvp[4].value, &validate, &UA_TYPES[UA_TYPES_BOOLEAN]);
1243
1244
    /* Connect */
1245
0
    res = c->cm->openConnection(c->cm, &kvm, psm, wg, WriterGroupChannelCallback);
1246
0
    if(res != UA_STATUSCODE_GOOD) {
1247
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg, "Could not open the MQTT connection");
1248
0
    }
1249
0
    return res;
1250
0
}
1251
1252
static void
1253
0
UA_WriterGroup_disconnect(UA_WriterGroup *wg) {
1254
0
    if(wg->sendChannel == 0)
1255
0
        return;
1256
0
    UA_PubSubConnection *c = wg->linkedConnection;
1257
0
    if(!c || !c->cm)
1258
0
        return;
1259
0
    c->cm->closeConnection(c->cm, wg->sendChannel);
1260
0
}
1261
1262
static UA_StatusCode
1263
UA_WriterGroup_connect(UA_PubSubManager *psm, UA_WriterGroup *wg,
1264
0
                       UA_Boolean validate) {
1265
0
    UA_LOCK_ASSERT(&psm->sc.server->serviceMutex);
1266
1267
    /* Check if already connected or no WG TransportSettings */
1268
0
    if(!UA_WriterGroup_canConnect(wg) && !validate)
1269
0
        return UA_STATUSCODE_GOOD;
1270
1271
    /* Is this a WriterGroup with custom TransportSettings beyond the
1272
     * PubSubConnection? */
1273
0
    if(wg->config.transportSettings.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY)
1274
0
        return UA_STATUSCODE_GOOD;
1275
1276
0
    UA_EventLoop *el = psm->sc.server->config.eventLoop;
1277
0
    if(!el) {
1278
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg, "No EventLoop configured");
1279
0
        UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_ERROR);
1280
0
        return UA_STATUSCODE_BADINTERNALERROR;
1281
0
    }
1282
1283
0
    UA_PubSubConnection *c = wg->linkedConnection;
1284
0
    if(!c)
1285
0
        return UA_STATUSCODE_BADINTERNALERROR;
1286
1287
    /* Look up the connection manager for the connection */
1288
0
    WriterGroupProfileMapping *profile = NULL;
1289
0
    for(size_t i = 0; i < UA_PUBSUB_PROFILES_SIZE; i++) {
1290
0
        if(!UA_String_equal(&c->config.transportProfileUri,
1291
0
                            &writerGroupProfiles[i].profileURI))
1292
0
            continue;
1293
0
        profile = &writerGroupProfiles[i];
1294
0
        break;
1295
0
    }
1296
1297
0
    UA_ConnectionManager *cm = (profile) ? getCM(el, profile->protocol) : NULL;
1298
0
    if(!cm || (c->cm && cm != c->cm)) {
1299
0
        UA_LOG_ERROR_PUBSUB(psm->logging, c,
1300
0
                            "The requested profile \"%S\"is not supported",
1301
0
                            c->config.transportProfileUri);
1302
0
        return UA_STATUSCODE_BADINTERNALERROR;
1303
0
    }
1304
1305
0
    c->cm = cm;
1306
0
    c->json = profile->json;
1307
1308
    /* Connect */
1309
0
    return (profile->connectWriterGroup) ?
1310
0
        profile->connectWriterGroup(psm, wg, validate) : UA_STATUSCODE_GOOD;
1311
0
}
1312
1313
/**************/
1314
/* Server API */
1315
/**************/
1316
1317
UA_StatusCode
1318
UA_Server_addWriterGroup(UA_Server *server, const UA_NodeId connection,
1319
                         const UA_WriterGroupConfig *writerGroupConfig,
1320
0
                         UA_NodeId *writerGroupIdentifier) {
1321
0
    if(!server || !writerGroupConfig)
1322
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1323
0
    lockServer(server);
1324
0
    UA_PubSubManager *psm = getPSM(server);
1325
0
    if(!psm) {
1326
0
        unlockServer(server);
1327
0
        return UA_STATUSCODE_BADINTERNALERROR;
1328
0
    }
1329
0
    UA_StatusCode res = UA_WriterGroup_create(psm, connection, writerGroupConfig,
1330
0
                                              writerGroupIdentifier);
1331
0
    unlockServer(server);
1332
0
    return res;
1333
0
}
1334
1335
UA_StatusCode
1336
0
UA_Server_removeWriterGroup(UA_Server *server, const UA_NodeId writerGroup) {
1337
0
    if(!server)
1338
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1339
0
    lockServer(server);
1340
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1341
0
    UA_PubSubManager *psm = getPSM(server);
1342
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroup);
1343
0
    if(wg)
1344
0
        UA_WriterGroup_remove(psm, wg);
1345
0
    else
1346
0
        res = UA_STATUSCODE_BADNOTFOUND;
1347
0
    unlockServer(server);
1348
0
    return res;
1349
0
}
1350
1351
UA_StatusCode
1352
0
UA_Server_enableWriterGroup(UA_Server *server, const UA_NodeId writerGroup)  {
1353
0
    if(!server)
1354
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1355
0
    lockServer(server);
1356
0
    UA_PubSubManager *psm = getPSM(server);
1357
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroup);
1358
0
    UA_StatusCode res =
1359
0
        (wg) ? UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_OPERATIONAL)
1360
0
             :  UA_STATUSCODE_BADINTERNALERROR;
1361
0
    unlockServer(server);
1362
0
    return res;
1363
0
}
1364
1365
UA_StatusCode
1366
UA_Server_disableWriterGroup(UA_Server *server,
1367
0
                             const UA_NodeId writerGroup) {
1368
0
    if(!server)
1369
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1370
0
    lockServer(server);
1371
0
    UA_PubSubManager *psm = getPSM(server);
1372
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroup);
1373
0
    UA_StatusCode res =
1374
0
        (wg) ? UA_WriterGroup_setPubSubState(psm, wg, UA_PUBSUBSTATE_DISABLED)
1375
0
             :  UA_STATUSCODE_BADINTERNALERROR;
1376
0
    unlockServer(server);
1377
0
    return res;
1378
0
}
1379
1380
#ifdef UA_ENABLE_PUBSUB_SKS
1381
UA_StatusCode
1382
UA_Server_setWriterGroupActivateKey(UA_Server *server,
1383
                                    const UA_NodeId writerGroup) {
1384
    if(!server)
1385
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1386
    lockServer(server);
1387
    UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND;
1388
    UA_PubSubManager *psm = getPSM(server);
1389
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroup);
1390
    if(wg) {
1391
        if(wg->keyStorage && wg->keyStorage->currentItem) {
1392
            res = UA_PubSubKeyStorage_activateKeyToChannelContext(
1393
                psm, wg->head.identifier, wg->config.securityGroupId);
1394
        } else {
1395
            res = UA_STATUSCODE_BADINTERNALERROR;
1396
        }
1397
    }
1398
    unlockServer(server);
1399
    return res;
1400
}
1401
#endif
1402
1403
UA_StatusCode
1404
UA_Server_getWriterGroupConfig(UA_Server *server, const UA_NodeId writerGroup,
1405
0
                               UA_WriterGroupConfig *config) {
1406
0
    if(!server || !config)
1407
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1408
0
    lockServer(server);
1409
0
    UA_PubSubManager *psm = getPSM(server);
1410
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroup);
1411
0
    UA_StatusCode res = (wg) ?
1412
0
        UA_WriterGroupConfig_copy(&wg->config, config) : UA_STATUSCODE_BADNOTFOUND;
1413
0
    unlockServer(server);
1414
0
    return res;
1415
0
}
1416
1417
UA_StatusCode
1418
UA_Server_getWriterGroupState(UA_Server *server, const UA_NodeId wgId,
1419
0
                              UA_PubSubState *state) {
1420
0
    if(!server || !state)
1421
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1422
0
    lockServer(server);
1423
0
    UA_WriterGroup *wg = UA_WriterGroup_find(getPSM(server), wgId);
1424
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1425
0
    if(wg)
1426
0
        *state = wg->head.state;
1427
0
    else
1428
0
        res = UA_STATUSCODE_BADNOTFOUND;
1429
0
    unlockServer(server);
1430
0
    return res;
1431
0
}
1432
1433
UA_StatusCode
1434
UA_Server_triggerWriterGroupPublish(UA_Server *server,
1435
0
                                    const UA_NodeId wgId) {
1436
0
    if(!server)
1437
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1438
0
    lockServer(server);
1439
0
    UA_PubSubManager *psm = getPSM(server);
1440
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, wgId);
1441
0
    if(!wg) {
1442
0
        unlockServer(server);
1443
0
        return UA_STATUSCODE_BADNOTFOUND;
1444
0
    }
1445
    /* Keep the WriterGroup alive until publishing has completed */
1446
0
    UA_WriterGroup_publishCallback(psm, wg);
1447
0
    unlockServer(server);
1448
0
    return UA_STATUSCODE_GOOD;
1449
0
}
1450
1451
UA_StatusCode
1452
UA_Server_getWriterGroupLastPublishTimestamp(UA_Server *server,
1453
                                             const UA_NodeId wgId,
1454
0
                                             UA_DateTime *timestamp) {
1455
0
    if(!server)
1456
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1457
0
    lockServer(server);
1458
0
    UA_WriterGroup *wg = UA_WriterGroup_find(getPSM(server), wgId);
1459
0
    if(!wg) {
1460
0
        unlockServer(server);
1461
0
        return UA_STATUSCODE_BADNOTFOUND;
1462
0
    }
1463
0
    *timestamp = wg->lastPublishTimeStamp;
1464
0
    unlockServer(server);
1465
0
    return UA_STATUSCODE_GOOD;
1466
0
}
1467
1468
UA_StatusCode
1469
UA_Server_setWriterGroupEncryptionKeys(UA_Server *server, const UA_NodeId writerGroup,
1470
                                       UA_UInt32 securityTokenId,
1471
                                       const UA_ByteString signingKey,
1472
                                       const UA_ByteString encryptingKey,
1473
0
                                       const UA_ByteString keyNonce) {
1474
0
    if(!server)
1475
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1476
0
    lockServer(server);
1477
0
    UA_PubSubManager *psm = getPSM(server);
1478
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, writerGroup);
1479
0
    UA_StatusCode res =
1480
0
        (wg) ? UA_WriterGroup_setEncryptionKeys(psm, wg, securityTokenId,
1481
0
                                                 signingKey, encryptingKey, keyNonce)
1482
0
              : UA_STATUSCODE_BADNOTFOUND;
1483
0
    unlockServer(server);
1484
0
    return res;
1485
0
}
1486
1487
UA_StatusCode
1488
UA_Server_updateWriterGroupConfig(UA_Server *server, const UA_NodeId wgId,
1489
0
                                  const UA_WriterGroupConfig *config) {
1490
0
    if(!server || !config)
1491
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1492
1493
0
    lockServer(server);
1494
0
    UA_PubSubManager *psm = getPSM(server);
1495
0
    UA_WriterGroup *wg = UA_WriterGroup_find(psm, wgId);
1496
0
    if(!wg) {
1497
0
        unlockServer(server);
1498
0
        return UA_STATUSCODE_BADNOTFOUND;
1499
0
    }
1500
1501
0
    if(UA_PubSubState_isEnabled(wg->head.state)) {
1502
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1503
0
                            "The WriterGroup must be disabled to update the config");
1504
0
        unlockServer(server);
1505
0
        return UA_STATUSCODE_BADINTERNALERROR;
1506
0
    }
1507
1508
    /* Validate the new configuration */
1509
0
    UA_StatusCode res = validateWriterGroupConfig(psm, &wg->head, config);
1510
0
    if(res != UA_STATUSCODE_GOOD) {
1511
0
        unlockServer(server);
1512
0
        return res;
1513
0
    }
1514
1515
    /* Store the old configuration */
1516
0
    UA_WriterGroupConfig oldConfig = wg->config;
1517
0
    memset(&wg->config, 0, sizeof(UA_WriterGroupConfig));
1518
1519
    /* Deep copy the new config */
1520
0
    res = UA_WriterGroupConfig_copy(config, &wg->config);
1521
0
    if(res != UA_STATUSCODE_GOOD)
1522
0
        goto errout;
1523
1524
    /* Validate the connection settings */
1525
0
    res = UA_WriterGroup_connect(psm, wg, true);
1526
0
    if(res != UA_STATUSCODE_GOOD) {
1527
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1528
0
                            "Could not validate the connection parameters");
1529
0
        goto errout;
1530
0
    }
1531
1532
#ifdef UA_ENABLE_PUBSUB_SKS
1533
    if(!UA_String_equal(&wg->config.securityGroupId, &oldConfig.securityGroupId) ||
1534
       wg->config.securityMode != oldConfig.securityMode) {
1535
        /* Detach keystorage and reattach if needed */
1536
        if(wg->keyStorage) {
1537
            UA_PubSubKeyStorage_detachKeyStorage(psm, wg->keyStorage);
1538
            wg->keyStorage = NULL;
1539
        }
1540
        if(wg->config.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
1541
           wg->config.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
1542
            res = writerGroupAttachSKSKeystorage(psm, wg);
1543
            if(res != UA_STATUSCODE_GOOD) {
1544
                UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1545
                                    "Attaching the SKS KeyStorage failed");
1546
                goto errout;
1547
            }
1548
        }
1549
    }
1550
#endif
1551
1552
    /* Clean up and return */
1553
0
    UA_WriterGroupConfig_clear(&oldConfig);
1554
0
    unlockServer(server);
1555
0
    return UA_STATUSCODE_GOOD;
1556
1557
0
 errout:
1558
0
    UA_WriterGroupConfig_clear(&wg->config);
1559
0
    wg->config = oldConfig; /* Restore the old config */
1560
0
    unlockServer(server);
1561
0
    return res;
1562
0
}
1563
1564
UA_StatusCode
1565
UA_Server_computeWriterGroupOffsetTable(UA_Server *server,
1566
                                        const UA_NodeId writerGroupId,
1567
0
                                        UA_PubSubOffsetTable *ot) {
1568
0
    if(!server || !ot)
1569
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1570
1571
0
    lockServer(server);
1572
1573
    /* Get the Writer Group */
1574
0
    UA_PubSubManager *psm = getPSM(server);
1575
0
    UA_WriterGroup *wg = (psm) ? UA_WriterGroup_find(psm, writerGroupId) : NULL;
1576
0
    if(!wg) {
1577
0
        unlockServer(server);
1578
0
        return UA_STATUSCODE_BADNOTFOUND;
1579
0
    }
1580
1581
    /* Initialize variables so we can goto cleanup below */
1582
0
    UA_DataSetField *field = NULL;
1583
0
    UA_NetworkMessage networkMessage;
1584
0
    memset(&networkMessage, 0, sizeof(networkMessage));
1585
0
    memset(ot, 0, sizeof(UA_PubSubOffsetTable));
1586
1587
    /* Prepare the metadata encode the DataSetMessages */
1588
0
    PubSubEncodeCtx ctx;
1589
0
    memset(&ctx, 0, sizeof(PubSubEncodeCtx));
1590
0
    ctx.ot = ot;
1591
1592
0
    size_t i = 0;
1593
0
    UA_STACKARRAY(UA_DataSetMessage_EncodingMetaData, emd, wg->writersCount);
1594
0
    memset(emd, 0, sizeof(UA_DataSetMessage_EncodingMetaData) * wg->writersCount);
1595
0
    ctx.eo.metaData = emd;
1596
0
    ctx.eo.metaDataSize = wg->writersCount;
1597
0
    UA_DataSetWriter *dsw;
1598
0
    LIST_FOREACH(dsw, &wg->writers, listEntry) {
1599
0
        emd[i].dataSetWriterId = dsw->config.dataSetWriterId;
1600
0
        UA_PublishedDataSet *pds = dsw->connectedDataSet;
1601
0
        if(pds) {
1602
0
            emd[i].fields = pds->dataSetMetaData.fields;
1603
0
            emd[i].fieldsSize = pds->dataSetMetaData.fieldsSize;
1604
0
        }
1605
0
        i++;
1606
0
    }
1607
1608
    /* Validate the DataSetWriters and generate their DataSetMessage */
1609
0
    size_t msgSize;
1610
0
    size_t dsmCount = 0;
1611
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1612
0
    UA_STACKARRAY(UA_UInt16, dsWriterIds, wg->writersCount);
1613
0
    UA_STACKARRAY(UA_DataSetMessage, dsmStore, wg->writersCount);
1614
0
    memset(dsmStore, 0, sizeof(UA_DataSetMessage) * wg->writersCount);
1615
0
    LIST_FOREACH(dsw, &wg->writers, listEntry) {
1616
0
        dsWriterIds[dsmCount] = dsw->config.dataSetWriterId;
1617
0
        res = UA_DataSetWriter_generateDataSetMessage(psm, dsw, &dsmStore[dsmCount]);
1618
0
        dsmCount++;
1619
0
        if(res != UA_STATUSCODE_GOOD)
1620
0
            goto cleanup;
1621
0
    }
1622
1623
    /* Generate the NetworkMessage */
1624
    /* Guard against exceeding the fixed-size dataSetWriterIds array (32 entries).
1625
     * The sendNetworkMessage path already checks this (line 886), but the offset-
1626
     * table computation calls generateNetworkMessage directly. Without this guard,
1627
     * a WriterGroup with more than 32 writers writes past the on-stack
1628
     * networkMessage.dataSetWriterIds[UA_NETWORKMESSAGE_MAXMESSAGECOUNT] array. */
1629
0
    if(dsmCount >= UA_NETWORKMESSAGE_MAXMESSAGECOUNT) {
1630
0
        UA_LOG_ERROR_PUBSUB(psm->logging, wg,
1631
0
                            "More DataSetMessages than allowed in "
1632
0
                            "UA_NETWORKMESSAGE_MAXMESSAGECOUNT");
1633
0
        res = UA_STATUSCODE_BADINTERNALERROR;
1634
0
        goto cleanup;
1635
0
    }
1636
0
    res = generateNetworkMessage(wg->linkedConnection, wg, dsmStore, dsWriterIds,
1637
0
                                 (UA_Byte) dsmCount, &wg->config.messageSettings,
1638
0
                                 &wg->config.transportSettings, &networkMessage);
1639
0
    if(res != UA_STATUSCODE_GOOD)
1640
0
        goto cleanup;
1641
1642
    /* Compute the message length and generate the old format offset-table (done
1643
     * inside calcSizeBinary) */
1644
0
    msgSize = UA_NetworkMessage_calcSizeBinaryInternal(&ctx, &networkMessage);
1645
0
    if(msgSize == 0) {
1646
0
        res = UA_STATUSCODE_BADINTERNALERROR;
1647
0
        goto cleanup;
1648
0
    }
1649
1650
    /* Create the encoded network message */
1651
0
    res = UA_NetworkMessage_encodeBinary(&networkMessage, &ot->networkMessage, &ctx.eo);
1652
0
    if(res != UA_STATUSCODE_GOOD)
1653
0
        goto cleanup;
1654
1655
    /* Pick up the component NodeIds */
1656
0
    dsw = NULL;
1657
0
    for(i = 0; i < ot->offsetsSize; i++) {
1658
0
        UA_PubSubOffset *o = &ot->offsets[i];
1659
0
        switch(o->offsetType) {
1660
0
        case UA_PUBSUBOFFSETTYPE_NETWORKMESSAGE_SEQUENCENUMBER:
1661
0
        case UA_PUBSUBOFFSETTYPE_NETWORKMESSAGE_TIMESTAMP:
1662
0
        case UA_PUBSUBOFFSETTYPE_NETWORKMESSAGE_PICOSECONDS:
1663
0
        case UA_PUBSUBOFFSETTYPE_NETWORKMESSAGE_GROUPVERSION:
1664
0
            res |= UA_NodeId_copy(&wg->head.identifier, &o->component);
1665
0
            break;
1666
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE:
1667
0
            dsw = (dsw == NULL) ? LIST_FIRST(&wg->writers) : LIST_NEXT(dsw, listEntry);
1668
0
            field = NULL;
1669
            /* fall through */
1670
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_SEQUENCENUMBER:
1671
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_STATUS:
1672
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_TIMESTAMP:
1673
0
        case UA_PUBSUBOFFSETTYPE_DATASETMESSAGE_PICOSECONDS:
1674
0
            UA_assert(dsw);
1675
0
            res |= UA_NodeId_copy(&dsw->head.identifier, &o->component);
1676
0
            break;
1677
0
        case UA_PUBSUBOFFSETTYPE_DATASETFIELD_DATAVALUE:
1678
0
            UA_assert(dsw && dsw->connectedDataSet);
1679
0
            field = (field == NULL) ?
1680
0
                TAILQ_FIRST(&dsw->connectedDataSet->fields) : TAILQ_NEXT(field, listEntry);
1681
0
            res |= UA_NodeId_copy(&field->identifier, &o->component);
1682
0
            break;
1683
0
        case UA_PUBSUBOFFSETTYPE_DATASETFIELD_VARIANT:
1684
0
            UA_assert(dsw && dsw->connectedDataSet);
1685
0
            field = (field == NULL) ?
1686
0
                TAILQ_FIRST(&dsw->connectedDataSet->fields) : TAILQ_NEXT(field, listEntry);
1687
0
            res |= UA_NodeId_copy(&field->identifier, &o->component);
1688
0
            break;
1689
0
        case UA_PUBSUBOFFSETTYPE_DATASETFIELD_RAW:
1690
0
            UA_assert(dsw && dsw->connectedDataSet);
1691
0
            field = (field == NULL) ?
1692
0
                TAILQ_FIRST(&dsw->connectedDataSet->fields) : TAILQ_NEXT(field, listEntry);
1693
0
            res |= UA_NodeId_copy(&field->identifier, &o->component);
1694
0
            break;
1695
0
        default:
1696
0
            break;
1697
0
        }
1698
0
    }
1699
1700
    /* Clean up */
1701
0
 cleanup:
1702
0
    if(res != UA_STATUSCODE_GOOD)
1703
0
        UA_PubSubOffsetTable_clear(ot);
1704
1705
0
    for(i = 0; i < dsmCount; i++) {
1706
0
        UA_DataSetMessage_clear(&dsmStore[i]);
1707
0
    }
1708
1709
0
    unlockServer(server);
1710
1711
0
    return res;
1712
0
}
1713
1714
void
1715
0
UA_PubSubOffsetTable_clear(UA_PubSubOffsetTable *ot) {
1716
0
    for(size_t i = 0; i < ot->offsetsSize; i++) {
1717
0
        UA_NodeId_clear(&ot->offsets[i].component);
1718
0
    }
1719
0
    UA_ByteString_clear(&ot->networkMessage);
1720
0
    UA_free(ot->offsets);
1721
0
    memset(ot, 0, sizeof(UA_PubSubOffsetTable));
1722
0
}
1723
1724
#endif /* UA_ENABLE_PUBSUB */