Coverage Report

Created: 2026-08-30 06:27

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