Coverage Report

Created: 2026-11-05 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/client/ua_client.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 2015-2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
6
 *    Copyright 2015-2016 (c) Sten GrĂ¼ner
7
 *    Copyright 2015-2016 (c) Chris Iatrou
8
 *    Copyright 2015 (c) hfaham
9
 *    Copyright 2015-2017 (c) Florian Palm
10
 *    Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
11
 *    Copyright 2015 (c) Holger Jeromin
12
 *    Copyright 2015 (c) Oleksiy Vasylyev
13
 *    Copyright 2016 (c) TorbenD
14
 *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
15
 *    Copyright 2016 (c) Lykurg
16
 *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
17
 *    Copyright 2018 (c) Kalycito Infotech Private Limited
18
 *    Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart
19
 *    Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes)
20
 *    Copyright 2022 (c) Linutronix GmbH (Author: Muddasir Shakil)
21
 */
22
23
#include <open62541/transport_generated.h>
24
25
#include "ua_client_internal.h"
26
#include "../ua_types_encoding_binary.h"
27
28
static void
29
clientHouseKeeping(UA_Client *client, void *_);
30
31
/********************/
32
/* Client Lifecycle */
33
/********************/
34
35
UA_StatusCode
36
0
UA_ClientConfig_copy(UA_ClientConfig const *src, UA_ClientConfig *dst){
37
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
38
39
0
    retval = UA_ApplicationDescription_copy(&src->clientDescription, &dst->clientDescription);
40
0
    if(retval != UA_STATUSCODE_GOOD)
41
0
        goto cleanup;
42
43
0
    retval = UA_String_copy(&src->endpointUrl, &dst->endpointUrl);
44
0
    if(retval != UA_STATUSCODE_GOOD)
45
0
        goto cleanup;
46
47
0
    retval = UA_ExtensionObject_copy(&src->userIdentityToken, &dst->userIdentityToken);
48
0
    if(retval != UA_STATUSCODE_GOOD)
49
0
        goto cleanup;
50
51
0
    retval = UA_String_copy(&src->securityPolicyUri, &dst->securityPolicyUri);
52
0
    if(retval != UA_STATUSCODE_GOOD)
53
0
        goto cleanup;
54
55
0
    retval = UA_EndpointDescription_copy(&src->endpoint, &dst->endpoint);
56
0
    if(retval != UA_STATUSCODE_GOOD)
57
0
        goto cleanup;
58
59
0
    retval = UA_UserTokenPolicy_copy(&src->userTokenPolicy, &dst->userTokenPolicy);
60
0
    if(retval != UA_STATUSCODE_GOOD)
61
0
        goto cleanup;
62
63
0
    retval = UA_String_copy(&src->applicationUri, &dst->applicationUri);
64
0
    if(retval != UA_STATUSCODE_GOOD)
65
0
        goto cleanup;
66
67
0
    retval = UA_String_copy(&src->authSecurityPolicyUri,
68
0
                            &dst->authSecurityPolicyUri);
69
0
    if(retval != UA_STATUSCODE_GOOD)
70
0
        goto cleanup;
71
72
0
    retval = UA_String_copy(&src->sessionName, &dst->sessionName);
73
0
    if(retval != UA_STATUSCODE_GOOD)
74
0
        goto cleanup;
75
76
0
    retval = UA_Array_copy(src->sessionLocaleIds, src->sessionLocaleIdsSize,
77
0
                           (void **)&dst->sessionLocaleIds, &UA_TYPES[UA_TYPES_LOCALEID]);
78
0
    if(retval != UA_STATUSCODE_GOOD)
79
0
        goto cleanup;
80
81
0
    dst->sessionLocaleIdsSize = src->sessionLocaleIdsSize;
82
0
    retval = UA_Array_copy(src->namespaces, src->namespacesSize,
83
0
                           (void**)&dst->namespaces, &UA_TYPES[UA_TYPES_STRING]);
84
0
    if(retval != UA_STATUSCODE_GOOD)
85
0
        goto cleanup;
86
0
    dst->namespacesSize = src->namespacesSize;
87
0
    dst->tcpReuseAddr = src->tcpReuseAddr;
88
0
    dst->allowNonePolicyPassword = src->allowNonePolicyPassword;
89
0
    dst->globalNotificationCallback = src->globalNotificationCallback;
90
0
    dst->lifecycleNotificationCallback = src->lifecycleNotificationCallback;
91
0
    dst->serviceNotificationCallback = src->serviceNotificationCallback;
92
0
#ifdef UA_ENABLE_ENCRYPTION
93
0
    dst->maxTrustListSize = src->maxTrustListSize;
94
0
    dst->maxRejectedListSize = src->maxRejectedListSize;
95
0
#endif
96
0
    dst->connectivityCheckInterval = src->connectivityCheckInterval;
97
0
    dst->certificateVerification = src->certificateVerification;
98
0
    dst->clientContext = src->clientContext;
99
0
    dst->customDataTypes = src->customDataTypes;
100
0
    dst->eventLoop = src->eventLoop;
101
0
    dst->externalEventLoop = src->externalEventLoop;
102
0
    dst->inactivityCallback = src->inactivityCallback;
103
0
    dst->localConnectionConfig = src->localConnectionConfig;
104
0
    dst->logging = src->logging;
105
0
    dst->noSession = src->noSession;
106
0
    dst->noReconnect = src->noReconnect;
107
0
    dst->noNewSession = src->noNewSession;
108
0
    if(src->certificateVerification.logging == NULL)
109
0
        dst->certificateVerification.logging = dst->logging;
110
0
#ifdef UA_ENABLE_SUBSCRIPTIONS
111
0
    dst->outStandingPublishRequests = src->outStandingPublishRequests;
112
0
#endif
113
0
    dst->requestedSessionTimeout = src->requestedSessionTimeout;
114
0
    dst->secureChannelLifeTime = src->secureChannelLifeTime;
115
0
    dst->securityMode = src->securityMode;
116
0
    dst->stateCallback = src->stateCallback;
117
0
#ifdef UA_ENABLE_SUBSCRIPTIONS
118
0
    dst->subscriptionInactivityCallback = src->subscriptionInactivityCallback;
119
0
#endif
120
0
    dst->timeout = src->timeout;
121
0
    dst->securityPolicies = src->securityPolicies;
122
0
    dst->securityPoliciesSize = src->securityPoliciesSize;
123
0
    dst->securityPolicyHistory = src->securityPolicyHistory;
124
0
    dst->authSecurityPolicies = src->authSecurityPolicies;
125
0
    dst->authSecurityPoliciesSize = src->authSecurityPoliciesSize;
126
0
#ifdef UA_ENABLE_ENCRYPTION
127
0
    dst->privateKeyPasswordCallback = src->privateKeyPasswordCallback;
128
0
#endif
129
130
0
cleanup:
131
0
    if(retval != UA_STATUSCODE_GOOD) {
132
        /* _clear will free the plugins in dst that are a shallow copy from src. */
133
0
        dst->authSecurityPolicies = NULL;
134
0
        dst->certificateVerification.context = NULL;
135
0
        dst->eventLoop = NULL;
136
0
        dst->logging = NULL;
137
0
        dst->securityPolicies = NULL;
138
0
        dst->securityPolicyHistory = NULL;
139
0
        UA_ClientConfig_clear(dst);
140
0
    }
141
0
    return retval;
142
0
}
143
144
UA_Client *
145
40
UA_Client_newWithConfig(const UA_ClientConfig *config) {
146
40
    if(!config)
147
0
        return NULL;
148
40
    UA_Client *client = (UA_Client*)UA_malloc(sizeof(UA_Client));
149
40
    if(!client)
150
0
        return NULL;
151
40
    memset(client, 0, sizeof(UA_Client));
152
40
    client->config = *config;
153
154
40
    UA_SecureChannel_init(&client->channel);
155
40
    client->channel.config = client->config.localConnectionConfig;
156
40
    client->connectStatus = UA_STATUSCODE_GOOD;
157
158
40
#if UA_MULTITHREADING >= 100
159
40
    UA_LOCK_INIT(&client->clientMutex);
160
40
#endif
161
162
    /* Initialize the namespace mapping */
163
40
    UA_StatusCode res = UA_STATUSCODE_GOOD;
164
40
    size_t initialNs = 2 + config->namespacesSize;
165
40
    client->namespaces = (UA_String*)UA_calloc(initialNs, sizeof(UA_String));
166
40
    if(!client->namespaces)
167
0
        goto error;
168
169
40
    client->namespacesSize = initialNs;
170
40
    client->namespaces[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
171
40
    client->namespaces[1] = UA_STRING_NULL; /* Gets set when we connect to the server */
172
40
    for(size_t i = 0; i < config->namespacesSize; i++) {
173
0
        res |= UA_String_copy(&config->namespaces[i], &client->namespaces[i+2]);
174
0
    }
175
40
    if(res != UA_STATUSCODE_GOOD)
176
0
        goto error;
177
178
40
    return client;
179
180
0
error:
181
0
    memset(&client->config, 0, sizeof(UA_ClientConfig));
182
0
    UA_Client_delete(client);
183
0
    return NULL;
184
40
}
185
186
void
187
40
UA_ClientConfig_clear(UA_ClientConfig *config) {
188
40
    UA_ApplicationDescription_clear(&config->clientDescription);
189
40
    UA_String_clear(&config->endpointUrl);
190
40
    UA_ExtensionObject_clear(&config->userIdentityToken);
191
192
    /* Delete the SecurityPolicies for Authentication */
193
40
    if(config->authSecurityPolicies != 0) {
194
80
        for(size_t i = 0; i < config->authSecurityPoliciesSize; i++)
195
40
            config->authSecurityPolicies[i].clear(&config->authSecurityPolicies[i]);
196
40
        UA_free(config->authSecurityPolicies);
197
40
        config->authSecurityPolicies = 0;
198
40
    }
199
40
    UA_String_clear(&config->securityPolicyUri);
200
40
    UA_String_clear(&config->authSecurityPolicyUri);
201
202
40
    UA_EndpointDescription_clear(&config->endpoint);
203
40
    UA_UserTokenPolicy_clear(&config->userTokenPolicy);
204
205
40
    UA_String_clear(&config->applicationUri);
206
207
40
    if(config->certificateVerification.clear)
208
40
        config->certificateVerification.clear(&config->certificateVerification);
209
210
    /* Delete the SecurityPolicies */
211
40
    if(config->securityPolicies != 0) {
212
80
        for(size_t i = 0; i < config->securityPoliciesSize; i++)
213
40
            config->securityPolicies[i].clear(&config->securityPolicies[i]);
214
40
        UA_free(config->securityPolicies);
215
40
        config->securityPolicies = 0;
216
40
    }
217
218
40
    while(config->securityPolicyHistory) {
219
0
        UA_ClientConfig_PolicyHistory *previous = config->securityPolicyHistory;
220
0
        config->securityPolicyHistory = previous->next;
221
0
        for(size_t i = 0; i < previous->policiesSize; i++)
222
0
            previous->policies[i].clear(&previous->policies[i]);
223
0
        UA_free(previous->policies);
224
0
        UA_free(previous);
225
0
    }
226
227
    /* Stop and delete the EventLoop */
228
40
    UA_EventLoop *el = config->eventLoop;
229
40
    if(el && !config->externalEventLoop) {
230
40
        if(el->state != UA_EVENTLOOPSTATE_FRESH &&
231
0
           el->state != UA_EVENTLOOPSTATE_STOPPED) {
232
0
            el->stop(el);
233
0
            while(el->state != UA_EVENTLOOPSTATE_STOPPED) {
234
0
                el->run(el, 100);
235
0
            }
236
0
        }
237
40
        el->free(el);
238
40
        config->eventLoop = NULL;
239
40
    }
240
241
    /* Logging */
242
40
    if(config->logging != NULL && config->logging->clear != NULL)
243
40
        config->logging->clear(config->logging);
244
40
    config->logging = NULL;
245
246
40
    UA_String_clear(&config->sessionName);
247
40
    if(config->sessionLocaleIdsSize > 0 && config->sessionLocaleIds) {
248
0
        UA_Array_delete(config->sessionLocaleIds,
249
0
                        config->sessionLocaleIdsSize, &UA_TYPES[UA_TYPES_LOCALEID]);
250
0
    }
251
40
    config->sessionLocaleIds = NULL;
252
40
    config->sessionLocaleIdsSize = 0;
253
254
40
    UA_Array_delete(config->namespaces, config->namespacesSize, &UA_TYPES[UA_TYPES_STRING]);
255
40
    config->namespaces = NULL;
256
40
    config->namespacesSize = 0;
257
40
    config->securityPoliciesSize = 0;
258
40
    config->authSecurityPoliciesSize = 0;
259
260
    /* Custom Data Types */
261
40
    UA_cleanupDataTypeWithCustom(config->customDataTypes);
262
263
40
#ifdef UA_ENABLE_ENCRYPTION
264
40
    config->privateKeyPasswordCallback = NULL;
265
40
#endif
266
40
}
267
268
void
269
0
UA_ClientConfig_delete(UA_ClientConfig *config){
270
0
    UA_assert(config != NULL);
271
0
    UA_ClientConfig_clear(config);
272
0
    UA_free(config);
273
0
}
274
275
static void
276
40
UA_Client_clear(UA_Client *client) {
277
    /* Prevent new async service calls in UA_Client_AsyncService_removeAll */
278
40
    UA_SessionState oldState = client->sessionState;
279
40
    client->sessionState = UA_SESSIONSTATE_CLOSING;
280
281
    /* Delete the async service calls with BADHSUTDOWN */
282
40
    __Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN);
283
284
    /* Reset to the old state to properly close the session */
285
40
    client->sessionState = oldState;
286
287
40
    UA_Client_disconnect(client);
288
289
    /* Prevent reconnection attempts during the EventLoop teardown
290
     * in UA_ClientConfig_clear */
291
40
    client->connectStatus = UA_STATUSCODE_BADSHUTDOWN;
292
293
40
    UA_String_clear(&client->discoveryUrl);
294
40
    UA_EndpointDescription_clear(&client->endpoint);
295
296
40
    UA_ByteString_clear(&client->serverSessionNonce);
297
40
    UA_ByteString_clear(&client->clientSessionNonce);
298
299
    /* Delete the subscriptions */
300
40
#ifdef UA_ENABLE_SUBSCRIPTIONS
301
40
    __Client_Subscriptions_clear(client);
302
40
#endif
303
304
    /* Remove the internal regular callback */
305
40
    UA_Client_removeCallback(client, client->houseKeepingCallbackId);
306
40
    client->houseKeepingCallbackId = 0;
307
308
    /* Clean up the SecureChannel */
309
40
    UA_SecureChannel_clear(&client->channel);
310
311
    /* Free the namespace mapping */
312
40
    UA_Array_delete(client->namespaces, client->namespacesSize,
313
40
                    &UA_TYPES[UA_TYPES_STRING]);
314
40
    client->namespaces = NULL;
315
40
    client->namespacesSize = 0;
316
317
    /* Call the application notification callback */
318
40
    UA_ClientConfig *config = &client->config;
319
40
    if(config->lifecycleNotificationCallback)
320
0
        config->lifecycleNotificationCallback(client, UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STOPPED,
321
0
                                              UA_KEYVALUEMAP_NULL);
322
40
    if(config->globalNotificationCallback)
323
40
        config->globalNotificationCallback(client, UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STOPPED,
324
40
                                           UA_KEYVALUEMAP_NULL);
325
326
40
#if UA_MULTITHREADING >= 100
327
40
    UA_LOCK_DESTROY(&client->clientMutex);
328
40
#endif
329
40
}
330
331
void
332
40
UA_Client_delete(UA_Client* client) {
333
40
    UA_Client_disconnect(client);
334
40
    UA_Client_clear(client);
335
40
    UA_ClientConfig_clear(&client->config);
336
40
    UA_free(client);
337
40
}
338
339
void
340
UA_Client_getState(UA_Client *client, UA_SecureChannelState *channelState,
341
0
                   UA_SessionState *sessionState, UA_StatusCode *connectStatus) {
342
0
    lockClient(client);
343
0
    if(channelState)
344
0
        *channelState = client->channel.state;
345
0
    if(sessionState)
346
0
        *sessionState = client->sessionState;
347
0
    if(connectStatus)
348
0
        *connectStatus = client->connectStatus;
349
0
    unlockClient(client);
350
0
}
351
352
UA_ClientConfig *
353
80
UA_Client_getConfig(UA_Client *client) {
354
80
    if(!client)
355
0
        return NULL;
356
80
    return &client->config;
357
80
}
358
359
#if UA_LOGLEVEL <= 300
360
static const char *channelStateTexts[14] = {
361
    "Closed", "ReverseListening", "Connecting", "Connected", "ReverseConnected", "RHESent", "HELSent", "HELReceived", "ACKSent",
362
    "AckReceived", "OPNSent", "Open", "Closing"};
363
static const char *sessionStateTexts[6] =
364
    {"Closed", "CreateRequested", "Created",
365
     "ActivateRequested", "Activated", "Closing"};
366
#endif
367
368
void
369
0
setConnectStatus(UA_Client *client, UA_StatusCode status) {
370
0
    UA_LOCK_ASSERT(&client->clientMutex);
371
372
0
    client->connectStatus = status;
373
0
    if(status != UA_STATUSCODE_GOOD)
374
0
        closeSecureChannel(client);
375
376
0
    notifyClientState(client);
377
0
}
378
379
void
380
80
notifyClientState(UA_Client *client) {
381
80
    UA_LOCK_ASSERT(&client->clientMutex);
382
383
80
    if(client->connectStatus == client->oldConnectStatus &&
384
40
       client->channel.state == client->oldChannelState &&
385
40
       client->sessionState == client->oldSessionState)
386
40
        return;
387
388
#if UA_LOGLEVEL <= 300
389
    UA_Boolean info = (client->connectStatus != UA_STATUSCODE_GOOD);
390
    if(client->oldChannelState != client->channel.state)
391
        info |= (client->channel.state == UA_SECURECHANNELSTATE_OPEN ||
392
                 client->channel.state == UA_SECURECHANNELSTATE_CLOSED);
393
    if(client->oldSessionState != client->sessionState)
394
        info |= (client->sessionState == UA_SESSIONSTATE_CREATED ||
395
                 client->sessionState == UA_SESSIONSTATE_ACTIVATED ||
396
                 client->sessionState == UA_SESSIONSTATE_CLOSED);
397
398
    const char *channelStateText = channelStateTexts[client->channel.state];
399
    const char *sessionStateText = sessionStateTexts[client->sessionState];
400
    const char *connectStatusText = UA_StatusCode_name(client->connectStatus);
401
402
    if(info)
403
        UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
404
                    "Client Status: ChannelState: %s, SessionState: %s, ConnectStatus: %s",
405
                    channelStateText, sessionStateText, connectStatusText);
406
    else
407
        UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
408
                     "Client Status: ChannelState: %s, SessionState: %s, ConnectStatus: %s",
409
                     channelStateText, sessionStateText, connectStatusText);
410
#endif
411
412
40
    client->oldConnectStatus = client->connectStatus;
413
40
    client->oldChannelState = client->channel.state;
414
40
    client->oldSessionState = client->sessionState;
415
416
40
    if(client->config.stateCallback)
417
0
        client->config.stateCallback(client, client->channel.state,
418
0
                                     client->sessionState, client->connectStatus);
419
40
}
420
421
/****************/
422
/* Raw Services */
423
/****************/
424
425
/* For both synchronous and asynchronous service calls */
426
static UA_StatusCode
427
sendRequest(UA_Client *client, const void *request,
428
0
            const UA_DataType *requestType, UA_UInt32 *requestId) {
429
0
    UA_LOCK_ASSERT(&client->clientMutex);
430
431
    /* Renew SecureChannel if necessary */
432
0
    __Client_renewSecureChannel(client);
433
0
    if(client->connectStatus != UA_STATUSCODE_GOOD)
434
0
        return client->connectStatus;
435
436
0
    UA_EventLoop *el = client->config.eventLoop;
437
438
    /* Adjusting the request header. The const attribute is violated, but we
439
     * reset to the original state before returning. Use the AuthenticationToken
440
     * only once the session is active (or to activate / close it). */
441
0
    UA_RequestHeader *rr = (UA_RequestHeader*)(uintptr_t)request;
442
0
    UA_NodeId oldToken = rr->authenticationToken; /* Put back in place later */
443
444
0
    if(client->sessionState == UA_SESSIONSTATE_ACTIVATED ||
445
0
       requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST] ||
446
0
       requestType == &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST])
447
0
        rr->authenticationToken = client->authenticationToken;
448
0
    rr->timestamp = el->dateTime_now(el);
449
450
    /* Create a unique handle >100,000 if not manually defined. The handle is
451
     * not necessarily unique when manually defined and used to cancel async
452
     * service requests. */
453
0
    if(rr->requestHandle == 0) {
454
0
        if(UA_UNLIKELY(client->requestHandle < 100000))
455
0
            client->requestHandle = 100000;
456
0
        rr->requestHandle = ++client->requestHandle;
457
0
    }
458
459
    /* Set the timeout hint if not manually defined */
460
0
    if(rr->timeoutHint == 0)
461
0
        rr->timeoutHint = client->config.timeout;
462
463
    /* Generate the request id */
464
0
    UA_UInt32 rqId = ++client->requestId;
465
466
0
#ifdef UA_ENABLE_TYPEDESCRIPTION
467
0
    UA_LOG_DEBUG_CHANNEL(client->config.logging, &client->channel,
468
0
                         "Sending request with RequestId %u of type %s",
469
0
                         (unsigned)rqId, requestType->typeName);
470
#else
471
    UA_LOG_DEBUG_CHANNEL(client->config.logging, &client->channel,
472
                         "Sending request with RequestId %u of type %" PRIu32,
473
                         (unsigned)rqId, requestType->binaryEncodingId.identifier.numeric);
474
#endif
475
476
    /* Send the message */
477
0
    UA_StatusCode retval =
478
0
        UA_SecureChannel_sendMSG(&client->channel, rqId, rr, requestType);
479
480
0
    rr->authenticationToken = oldToken; /* Set back to the original token */
481
482
    /* Sending failed. The SecureChannel cannot recover from that. Call
483
     * closeSecureChannel to a) close from our end and b) set the session to
484
     * non-activated. */
485
0
    if(retval != UA_STATUSCODE_GOOD)
486
0
        closeSecureChannel(client);
487
488
    /* Return the request id */
489
0
    *requestId = rqId;
490
0
    return retval;
491
0
}
492
493
static const UA_NodeId
494
serviceFaultId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_SERVICEFAULT_ENCODING_DEFAULTBINARY}};
495
496
/* Look for the async callback in the linked list, execute and delete it */
497
static UA_StatusCode
498
processMSGResponse(UA_Client *client, UA_UInt32 requestId,
499
0
                   const UA_ByteString *msg) {
500
0
    UA_ClientConfig *config = &client->config;
501
502
    /* Find the callback */
503
0
    AsyncServiceCall *ac;
504
0
    LIST_FOREACH(ac, &client->asyncServiceCalls, pointers) {
505
0
        if(ac->requestId == requestId)
506
0
            break;
507
0
    }
508
509
    /* Part 6, 6.7.6: After the security validation is complete the receiver
510
     * shall verify the RequestId and the SequenceNumber. If these checks fail a
511
     * Bad_SecurityChecksFailed error is reported. The RequestId only needs to
512
     * be verified by the Client since only the Client knows if it is valid or
513
     * not.
514
     *
515
     * But! If an async request has timed out, the RequestId is no longer found.
516
     * In theory the server should send an error back before the timeout. But
517
     * not all do. In these cases we don't want to close the entire
518
     * SecureChannel and just log a warning. The service callback has already
519
     * been notified about the timeout before. */
520
0
    if(!ac) {
521
0
        UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_CLIENT,
522
0
                       "Request with unknown RequestId %u", requestId);
523
0
        return UA_STATUSCODE_GOOD;
524
0
    }
525
526
0
    UA_Response asyncResponse;
527
0
    UA_Response *response = (ac->syncResponse) ? ac->syncResponse : &asyncResponse;
528
0
    const UA_DataType *responseType = ac->responseType;
529
    /* Also initialize before decoding the type NodeId: errors there already
530
     * invoke the callback and clear the response. ServiceFault decoding only
531
     * initializes the header, so initialize the full expected response type. */
532
0
    UA_init(response, responseType);
533
534
    /* Dequeue ac. We might disconnect the client (remove all ac) in the callback. */
535
0
    LIST_REMOVE(ac, pointers);
536
537
    /* Decode the response type */
538
0
    size_t offset = 0;
539
0
    UA_NodeId responseTypeId;
540
0
    UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &responseTypeId);
541
0
    if(retval != UA_STATUSCODE_GOOD)
542
0
        goto process;
543
544
    /* Verify the type of the response */
545
0
    if(!UA_NodeId_equal(&responseTypeId, &ac->responseType->binaryEncodingId)) {
546
0
        if(UA_NodeId_equal(&responseTypeId, &serviceFaultId)) {
547
            /* Decode as a ServiceFault, i.e. only the response header */
548
0
            UA_LOG_DEBUG(config->logging, UA_LOGCATEGORY_CLIENT,
549
0
                         "Received a ServiceFault response");
550
0
            responseType = &UA_TYPES[UA_TYPES_SERVICEFAULT];
551
0
        } else {
552
0
            UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_CLIENT,
553
0
                         "Service response type does not match");
554
0
            retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
555
0
            goto process; /* Do not decode */
556
0
        }
557
0
    }
558
559
    /* Decode the response */
560
0
#ifdef UA_ENABLE_TYPEDESCRIPTION
561
0
    UA_LOG_DEBUG(config->logging, UA_LOGCATEGORY_CLIENT,
562
0
                 "Decode a message of type %s", responseType->typeName);
563
#else
564
    UA_LOG_DEBUG(config->logging, UA_LOGCATEGORY_CLIENT,
565
                 "Decode a message of type %" PRIu32,
566
                 responseTypeId.identifier.numeric);
567
#endif
568
569
0
    UA_DecodeBinaryOptions opt;
570
0
    memset(&opt, 0, sizeof(UA_DecodeBinaryOptions));
571
0
    opt.customTypes = config->customDataTypes;
572
0
    opt.namespaceMapping = client->channel.namespaceMapping;
573
0
    retval = UA_decodeBinaryInternal(msg, &offset, response, responseType, &opt);
574
575
0
 process:
576
    /* Process the received MSG response */
577
0
    if(retval != UA_STATUSCODE_GOOD) {
578
0
        UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_CLIENT,
579
0
                       "Could not decode the response with RequestId %u with status %s",
580
0
                       (unsigned)requestId, UA_StatusCode_name(retval));
581
0
        response->responseHeader.serviceResult = retval;
582
0
    }
583
584
    /* The Session closed. The current response is processed with the return code.
585
     * The next request first recreates a session. */
586
0
    if(responseType != &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE] &&
587
0
       (response->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONIDINVALID ||
588
0
        response->responseHeader.serviceResult == UA_STATUSCODE_BADSESSIONCLOSED)) {
589
        /* Clean up the session information and reset the state */
590
0
        cleanupSession(client);
591
592
0
        if(config->noNewSession) {
593
            /* Configuration option to not create a new Session. Disconnect the
594
             * client. */
595
0
            client->connectStatus = response->responseHeader.serviceResult;
596
0
            UA_LOG_ERROR(config->logging, UA_LOGCATEGORY_CLIENT,
597
0
                         "Session cannot be activated with StatusCode %s. "
598
0
                         "The client is configured not to create a new Session.",
599
0
                         UA_StatusCode_name(client->connectStatus));
600
0
            closeSecureChannel(client);
601
0
        } else {
602
0
            UA_LOG_WARNING(config->logging, UA_LOGCATEGORY_CLIENT,
603
0
                           "Session no longer valid. A new Session is created for the next "
604
0
                           "Service request but we do not re-send the current request.");
605
0
        }
606
0
    }
607
608
    /* Prepare the notification payload */
609
0
    UA_ApplicationNotificationType nt;
610
0
    static UA_THREAD_LOCAL UA_KeyValuePair notifyPayload[4] = {
611
0
        {{0, UA_STRING_STATIC("securechannel-id")}, {0}},
612
0
        {{0, UA_STRING_STATIC("session-id")}, {0}},
613
0
        {{0, UA_STRING_STATIC("request-id")}, {0}},
614
0
        {{0, UA_STRING_STATIC("service-type")}, {0}}
615
0
    };
616
0
    UA_KeyValueMap notifyPayloadMap = {4, notifyPayload};
617
0
    if(config->globalNotificationCallback || config->serviceNotificationCallback) {
618
0
        UA_Variant_setScalar(&notifyPayload[0].value,
619
0
                             &client->channel.securityToken.channelId,
620
0
                             &UA_TYPES[UA_TYPES_UINT32]);
621
0
        UA_Variant_setScalar(&notifyPayload[1].value, &client->sessionId,
622
0
                             &UA_TYPES[UA_TYPES_NODEID]);
623
0
        UA_Variant_setScalar(&notifyPayload[2].value, &requestId,
624
0
                             &UA_TYPES[UA_TYPES_UINT32]);
625
0
        UA_Variant_setScalar(&notifyPayload[3].value,
626
0
                             (void *)(uintptr_t)&ac->responseType->typeId,
627
0
                             &UA_TYPES[UA_TYPES_NODEID]);
628
0
    }
629
630
0
    if(ac->callback) {
631
        /* Notify with UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_BEGIN before the
632
         * service response is processed asynchronously */
633
0
        nt = UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_BEGIN;
634
0
        if(config->serviceNotificationCallback)
635
0
            config->serviceNotificationCallback(client, nt, notifyPayloadMap);
636
0
        if(config->globalNotificationCallback)
637
0
            config->globalNotificationCallback(client, nt, notifyPayloadMap);
638
639
        /* Call the async callback */
640
0
        ac->callback(client, ac->userdata, requestId, response);
641
0
    }
642
643
    /* Always notify with UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_END that the
644
     * service was processed. For the synchronous case this gets called before
645
     * the response is returned together with the main control flow. */
646
0
    nt = UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_END;
647
0
    if(config->serviceNotificationCallback)
648
0
        config->serviceNotificationCallback(client, nt, notifyPayloadMap);
649
0
    if(config->globalNotificationCallback)
650
0
        config->globalNotificationCallback(client, nt, notifyPayloadMap);
651
652
    /* Clean up */
653
0
    UA_NodeId_clear(&responseTypeId);
654
0
    if(!ac->syncResponse) {
655
0
        UA_clear(response, ac->responseType);
656
0
        UA_free(ac);
657
0
    } else {
658
        /* Return a special status code after processing a synchronous message.
659
         * This makes the client return control immediately. */
660
0
        ac->syncResponse = NULL; /* Indicate that response was received */
661
0
        if(retval == UA_STATUSCODE_GOOD)
662
0
            retval = UA_STATUSCODE_GOODCOMPLETESASYNCHRONOUSLY;
663
0
    }
664
0
    return retval;
665
0
}
666
667
UA_StatusCode
668
processServiceResponse(UA_Client *client, UA_SecureChannel *channel,
669
                       UA_MessageType messageType, UA_UInt32 requestId,
670
40
                       UA_ByteString *message) {
671
40
    if(!UA_SecureChannel_isConnected(channel)) {
672
0
        if(messageType == UA_MESSAGETYPE_MSG) {
673
0
            UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Discard MSG message "
674
0
                                 "with RequestId %u as the SecureChannel is not connected",
675
0
                                 requestId);
676
0
        } else {
677
0
            UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Discard message "
678
0
                                 "as the SecureChannel is not connected");
679
0
        }
680
0
        return UA_STATUSCODE_BADCONNECTIONCLOSED;
681
0
    }
682
683
40
    switch(messageType) {
684
0
    case UA_MESSAGETYPE_RHE:
685
0
        UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Process RHE message");
686
0
        processRHEMessage(client, message);
687
0
        return UA_STATUSCODE_GOOD;
688
0
    case UA_MESSAGETYPE_ACK:
689
0
        UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Process ACK message");
690
0
        processACKResponse(client, message);
691
0
        return UA_STATUSCODE_GOOD;
692
0
    case UA_MESSAGETYPE_OPN:
693
0
        UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Process OPN message");
694
0
        processOPNResponse(client, message);
695
0
        return UA_STATUSCODE_GOOD;
696
0
    case UA_MESSAGETYPE_ERR:
697
0
        UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Process ERR message");
698
0
        processERRResponse(client, message);
699
0
        return UA_STATUSCODE_GOOD;
700
0
    case UA_MESSAGETYPE_MSG:
701
0
        UA_LOG_DEBUG_CHANNEL(client->config.logging, channel, "Process MSG message "
702
0
                             "with RequestId %u", requestId);
703
0
        return processMSGResponse(client, requestId, message);
704
40
    default:
705
40
        UA_LOG_TRACE_CHANNEL(client->config.logging, channel,
706
40
                             "Invalid message type");
707
40
        channel->state = UA_SECURECHANNELSTATE_CLOSING;
708
40
        return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
709
40
    }
710
40
}
711
712
void
713
__Client_Service(UA_Client *client, const void *request,
714
                 const UA_DataType *requestType, void *response,
715
20
                 const UA_DataType *responseType) {
716
20
    UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
717
718
    /* Initialize. Response is valied in case of aborting. */
719
20
    UA_init(response, responseType);
720
721
    /* Verify that the EventLoop is running */
722
20
    UA_EventLoop *el = client->config.eventLoop;
723
20
    if(!el || el->state != UA_EVENTLOOPSTATE_STARTED) {
724
20
        respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR;
725
20
        return;
726
20
    }
727
728
    /* Check that the SecureChannel is open and also a Session active (if we
729
     * want a Session). Otherwise reopen. */
730
0
    if(!isFullyConnected(client)) {
731
0
        UA_LOG_INFO(client->config.logging, UA_LOGCATEGORY_CLIENT,
732
0
                    "Re-establish the connection for the synchronous service call");
733
0
        connectSync(client);
734
0
        if(client->connectStatus != UA_STATUSCODE_GOOD) {
735
0
            respHeader->serviceResult = client->connectStatus;
736
0
            return;
737
0
        }
738
0
    }
739
740
    /* Store the channelId to detect if the channel was changed by a
741
     * reconnection within the EventLoop run method. */
742
0
    UA_UInt32 channelId = client->channel.securityToken.channelId;
743
744
    /* Send the request */
745
0
    UA_UInt32 requestId = 0;
746
0
    UA_StatusCode retval = sendRequest(client, request, requestType, &requestId);
747
0
    if(retval != UA_STATUSCODE_GOOD) {
748
        /* If sending failed, the status is set to closing. The SecureChannel is
749
         * the actually closed in the next iteration of the EventLoop. */
750
0
        UA_assert(client->channel.state == UA_SECURECHANNELSTATE_CLOSING ||
751
0
                  client->channel.state == UA_SECURECHANNELSTATE_CLOSED);
752
0
        UA_LOG_WARNING(client->config.logging, UA_LOGCATEGORY_CLIENT,
753
0
                       "Sending the request failed with status %s",
754
0
                       UA_StatusCode_name(retval));
755
0
        notifyClientState(client);
756
0
        respHeader->serviceResult = retval;
757
0
        return;
758
0
    }
759
760
    /* Temporarily insert an AsyncServiceCall */
761
0
    const UA_RequestHeader *rh = (const UA_RequestHeader*)request;
762
0
    AsyncServiceCall ac;
763
0
    ac.callback = NULL;
764
0
    ac.userdata = NULL;
765
0
    ac.responseType = responseType;
766
0
    ac.syncResponse = (UA_Response*)response;
767
0
    ac.requestId = requestId;
768
0
    ac.start = el->dateTime_nowMonotonic(el); /* Start timeout after sending */
769
0
    ac.timeout = rh->timeoutHint;
770
0
    ac.requestHandle = rh->requestHandle;
771
0
    if(ac.timeout == 0)
772
0
        ac.timeout = UA_UINT32_MAX; /* 0 -> unlimited */
773
774
0
    LIST_INSERT_HEAD(&client->asyncServiceCalls, &ac, pointers);
775
776
    /* Time until which the request has to be answered */
777
0
    UA_DateTime maxDate = ac.start + ((UA_DateTime)ac.timeout * UA_DATETIME_MSEC);
778
779
    /* Run the EventLoop until the request was processed, the request has timed
780
     * out or the client connection fails */
781
0
    UA_UInt32 timeout_remaining = ac.timeout;
782
0
    while(true) {
783
        /* Unlock before dropping into the EventLoop. The client lock is
784
         * re-taken in the network callback if an event occurs. */
785
0
        retval = el->run(el, timeout_remaining);
786
787
        /* Was the response received? In that case we can directly return. The
788
         * ac was already removed from the internal linked list. */
789
0
        if(ac.syncResponse == NULL)
790
0
            return;
791
792
        /* Check the status. Do not try to resend if the connection breaks.
793
         * Leave this to the application-level user. For example, we do not want
794
         * to call a method twice is the connection broke after sending the
795
         * request. */
796
0
        if(retval != UA_STATUSCODE_GOOD)
797
0
            break;
798
799
        /* The connection was lost */
800
0
        retval = client->connectStatus;
801
0
        if(retval != UA_STATUSCODE_GOOD)
802
0
            break;
803
804
        /* The channel is no longer the same or was closed */
805
0
        if(channelId != client->channel.securityToken.channelId) {
806
0
            retval = UA_STATUSCODE_BADSECURECHANNELCLOSED;
807
0
            break;
808
0
        }
809
810
        /* Update the remaining timeout or break */
811
0
        UA_DateTime now = ac.start = el->dateTime_nowMonotonic(el);
812
0
        if(now > maxDate) {
813
0
            retval = UA_STATUSCODE_BADTIMEOUT;
814
0
            break;
815
0
        }
816
0
        timeout_remaining = (UA_UInt32)((maxDate - now) / UA_DATETIME_MSEC);
817
0
    }
818
819
    /* Detach from the internal async service list */
820
0
    LIST_REMOVE(&ac, pointers);
821
822
    /* Return the status code */
823
0
    respHeader->serviceResult = retval;
824
0
}
825
826
/***********************************/
827
/* Handling of Async Service Calls */
828
/***********************************/
829
830
static void
831
__Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
832
0
                             UA_StatusCode statusCode) {
833
    /* Set the status for the synchronous service call. Don't free the ac. */
834
0
    if(ac->syncResponse) {
835
0
        ac->syncResponse->responseHeader.serviceResult = statusCode;
836
0
        ac->syncResponse = NULL; /* Indicate the async service call was processed */
837
0
        return;
838
0
    }
839
840
0
    if(ac->callback) {
841
        /* Create an empty response with the statuscode and call the callback */
842
0
        UA_Response response;
843
0
        UA_init(&response, ac->responseType);
844
0
        response.responseHeader.serviceResult = statusCode;
845
0
        ac->callback(client, ac->userdata, ac->requestId, &response);
846
847
        /* Clean up the response. The user callback might move data into it. For
848
         * whatever reasons. */
849
0
        UA_clear(&response, ac->responseType);
850
0
    }
851
852
0
    UA_free(ac);
853
0
}
854
855
void
856
120
__Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCode) {
857
    /* Make this function reentrant. One of the async callbacks could indirectly
858
     * operate on the list. Moving all elements to a local list before iterating
859
     * that. */
860
120
    UA_AsyncServiceList asyncServiceCalls = client->asyncServiceCalls;
861
120
    LIST_INIT(&client->asyncServiceCalls);
862
120
    if(asyncServiceCalls.lh_first)
863
0
        asyncServiceCalls.lh_first->pointers.le_prev = &asyncServiceCalls.lh_first;
864
865
    /* Cancel and remove the elements from the local list */
866
120
    AsyncServiceCall *ac, *ac_tmp;
867
120
    LIST_FOREACH_SAFE(ac, &asyncServiceCalls, pointers, ac_tmp) {
868
0
        LIST_REMOVE(ac, pointers);
869
0
        __Client_AsyncService_cancel(client, ac, statusCode);
870
0
    }
871
120
}
872
873
UA_StatusCode
874
__Client_AsyncService(UA_Client *client, const void *request,
875
                      const UA_DataType *requestType,
876
                      UA_ClientAsyncServiceCallback callback,
877
                      const UA_DataType *responseType,
878
0
                      void *userdata, UA_UInt32 *requestId) {
879
0
    UA_LOCK_ASSERT(&client->clientMutex);
880
881
    /* Is the SecureChannel connected? */
882
0
    if(client->channel.state != UA_SECURECHANNELSTATE_OPEN) {
883
0
        UA_LOG_ERROR(client->config.logging, UA_LOGCATEGORY_CLIENT,
884
0
                     "SecureChannel must be connected to send request");
885
0
        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
886
0
    }
887
888
    /* Prepare the entry for the linked list */
889
0
    AsyncServiceCall *ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall));
890
0
    if(!ac)
891
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
892
893
    /* Call the service and set the requestId */
894
0
    UA_StatusCode retval = sendRequest(client, request, requestType, &ac->requestId);
895
0
    if(retval != UA_STATUSCODE_GOOD) {
896
        /* If sending failed, the status is set to closing. The SecureChannel is
897
         * the actually closed in the next iteration of the EventLoop. */
898
0
        UA_assert(client->channel.state == UA_SECURECHANNELSTATE_CLOSING ||
899
0
                  client->channel.state == UA_SECURECHANNELSTATE_CLOSED);
900
0
        UA_free(ac);
901
0
        notifyClientState(client);
902
0
        return retval;
903
0
    }
904
905
    /* Set up the AsyncServiceCall for processing the response */
906
0
    UA_EventLoop *el = client->config.eventLoop;
907
0
    const UA_RequestHeader *rh = (const UA_RequestHeader*)request;
908
0
    ac->callback = callback;
909
0
    ac->responseType = responseType;
910
0
    ac->userdata = userdata;
911
0
    ac->syncResponse = NULL;
912
0
    ac->start = el->dateTime_nowMonotonic(el);
913
0
    ac->timeout = rh->timeoutHint;
914
0
    ac->requestHandle = rh->requestHandle;
915
0
    if(ac->timeout == 0)
916
0
        ac->timeout = UA_UINT32_MAX; /* 0 -> unlimited */
917
918
0
    LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers);
919
920
    /* Return the generated request id */
921
0
    if(requestId)
922
0
        *requestId = ac->requestId;
923
924
    /* Notify the userland if a change happened */
925
0
    notifyClientState(client);
926
927
0
    return UA_STATUSCODE_GOOD;
928
0
}
929
930
static UA_StatusCode
931
0
cancelByRequestHandle(UA_Client *client, UA_UInt32 requestHandle, UA_UInt32 *cancelCount) {
932
0
    UA_CancelRequest creq;
933
0
    UA_CancelRequest_init(&creq);
934
0
    creq.requestHandle = requestHandle;
935
0
    UA_CancelResponse cresp;
936
0
    UA_CancelResponse_init(&cresp);
937
0
    __Client_Service(client, &creq, &UA_TYPES[UA_TYPES_CANCELREQUEST],
938
0
                     &cresp, &UA_TYPES[UA_TYPES_CANCELRESPONSE]);
939
0
    if(cancelCount)
940
0
        *cancelCount = cresp.cancelCount;
941
0
    UA_StatusCode res = cresp.responseHeader.serviceResult;
942
0
    UA_CancelResponse_clear(&cresp);
943
0
    return res;
944
0
}
945
946
UA_StatusCode
947
UA_Client_cancelByRequestHandle(UA_Client *client, UA_UInt32 requestHandle,
948
0
                                UA_UInt32 *cancelCount) {
949
0
    lockClient(client);
950
0
    UA_StatusCode res = cancelByRequestHandle(client, requestHandle, cancelCount);
951
0
    unlockClient(client);
952
0
    return res;
953
0
}
954
955
UA_StatusCode
956
UA_Client_cancelByRequestId(UA_Client *client, UA_UInt32 requestId,
957
0
                            UA_UInt32 *cancelCount) {
958
0
    lockClient(client);
959
0
    UA_StatusCode res = UA_STATUSCODE_BADNOTFOUND;
960
0
    AsyncServiceCall *ac;
961
0
    LIST_FOREACH(ac, &client->asyncServiceCalls, pointers) {
962
0
        if(ac->requestId != requestId)
963
0
            continue;
964
0
        res = cancelByRequestHandle(client, ac->requestHandle, cancelCount);
965
0
        break;
966
0
    }
967
0
    unlockClient(client);
968
0
    return res;
969
0
}
970
971
/*******************/
972
/* Timed Callbacks */
973
/*******************/
974
975
UA_StatusCode
976
UA_Client_addTimedCallback(UA_Client *client, UA_ClientCallback callback,
977
0
                           void *data, UA_DateTime date, UA_UInt64 *callbackId) {
978
0
    if(!client->config.eventLoop)
979
0
        return UA_STATUSCODE_BADINTERNALERROR;
980
0
    lockClient(client);
981
0
    UA_StatusCode res = client->config.eventLoop->
982
0
        addTimer(client->config.eventLoop, (UA_Callback)callback,
983
0
                 client, data, 0.0, &date, UA_TIMERPOLICY_ONCE, callbackId);
984
0
    unlockClient(client);
985
0
    return res;
986
0
}
987
988
UA_StatusCode
989
UA_Client_addRepeatedCallback(UA_Client *client, UA_ClientCallback callback,
990
0
                              void *data, UA_Double interval_ms, UA_UInt64 *callbackId) {
991
0
    if(!client->config.eventLoop)
992
0
        return UA_STATUSCODE_BADINTERNALERROR;
993
0
    lockClient(client);
994
0
    UA_StatusCode res = client->config.eventLoop->
995
0
        addTimer(client->config.eventLoop, (UA_Callback)callback, client, data,
996
0
                 interval_ms, NULL, UA_TIMERPOLICY_CURRENTTIME, callbackId);
997
0
    unlockClient(client);
998
0
    return res;
999
0
}
1000
1001
UA_StatusCode
1002
UA_Client_changeRepeatedCallbackInterval(UA_Client *client, UA_UInt64 callbackId,
1003
0
                                         UA_Double interval_ms) {
1004
0
    if(!client->config.eventLoop)
1005
0
        return UA_STATUSCODE_BADINTERNALERROR;
1006
0
    lockClient(client);
1007
0
    UA_StatusCode res = client->config.eventLoop->
1008
0
        modifyTimer(client->config.eventLoop, callbackId, interval_ms,
1009
0
                    NULL, UA_TIMERPOLICY_CURRENTTIME);
1010
0
    unlockClient(client);
1011
0
    return res;
1012
0
}
1013
1014
void
1015
40
UA_Client_removeCallback(UA_Client *client, UA_UInt64 callbackId) {
1016
40
    if(!client->config.eventLoop)
1017
0
        return;
1018
40
    lockClient(client);
1019
40
    client->config.eventLoop->removeTimer(client->config.eventLoop, callbackId);
1020
40
    unlockClient(client);
1021
40
}
1022
1023
/**********************/
1024
/* Housekeeping Tasks */
1025
/**********************/
1026
1027
static void
1028
0
asyncServiceTimeoutCheck(UA_Client *client) {
1029
    /* Make this function reentrant. One of the async callbacks could indirectly
1030
     * operate on the list. Moving all elements to a local list before iterating
1031
     * that. */
1032
0
    UA_EventLoop *el = client->config.eventLoop;
1033
0
    UA_DateTime now = el->dateTime_nowMonotonic(el);
1034
0
    UA_AsyncServiceList asyncServiceCalls;
1035
0
    AsyncServiceCall *ac, *ac_tmp;
1036
0
    LIST_INIT(&asyncServiceCalls);
1037
0
    LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) {
1038
0
        if(!ac->timeout)
1039
0
           continue;
1040
0
        if(ac->start + (UA_DateTime)(ac->timeout * UA_DATETIME_MSEC) <= now) {
1041
0
            LIST_REMOVE(ac, pointers);
1042
0
            LIST_INSERT_HEAD(&asyncServiceCalls, ac, pointers);
1043
0
        }
1044
0
    }
1045
1046
    /* Cancel and remove the elements from the local list */
1047
0
    LIST_FOREACH_SAFE(ac, &asyncServiceCalls, pointers, ac_tmp) {
1048
0
        LIST_REMOVE(ac, pointers);
1049
        /* Reset the pointers to pacify clang-analyzer */
1050
0
        ac->pointers.le_next = NULL;
1051
0
        ac->pointers.le_prev = NULL;
1052
0
        __Client_AsyncService_cancel(client, ac, UA_STATUSCODE_BADTIMEOUT);
1053
0
    }
1054
0
}
1055
1056
static void
1057
backgroundConnectivityCallback(UA_Client *client, void *userdata,
1058
0
                               UA_UInt32 requestId, const UA_ReadResponse *response) {
1059
0
    lockClient(client);
1060
0
    if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTIMEOUT) {
1061
0
        if(client->config.inactivityCallback)
1062
0
            client->config.inactivityCallback(client);
1063
0
    }
1064
0
    UA_EventLoop *el = client->config.eventLoop;
1065
0
    client->pendingConnectivityCheck = false;
1066
0
    client->lastConnectivityCheck = el->dateTime_nowMonotonic(el);
1067
0
    unlockClient(client);
1068
0
}
1069
1070
static void
1071
0
__Client_backgroundConnectivity(UA_Client *client) {
1072
0
    if(!client->config.connectivityCheckInterval)
1073
0
        return;
1074
1075
    /* Only probe the server once the Session is activated. A Read sent
1076
     * before the server returns ActivateSessionResponse triggers a
1077
     * ServiceFault that aborts a slow connect (issue #8195). */
1078
0
    if(client->sessionState != UA_SESSIONSTATE_ACTIVATED)
1079
0
        return;
1080
1081
0
    if(client->pendingConnectivityCheck)
1082
0
        return;
1083
1084
0
    UA_EventLoop *el = client->config.eventLoop;
1085
0
    UA_DateTime now = el->dateTime_nowMonotonic(el);
1086
0
    UA_DateTime nextDate = client->lastConnectivityCheck +
1087
0
        (UA_DateTime)(client->config.connectivityCheckInterval * UA_DATETIME_MSEC);
1088
0
    if(now <= nextDate)
1089
0
        return;
1090
1091
    /* Prepare the request */
1092
0
    UA_ReadValueId rvid;
1093
0
    UA_ReadValueId_init(&rvid);
1094
0
    rvid.attributeId = UA_ATTRIBUTEID_VALUE;
1095
0
    rvid.nodeId = UA_NS0ID(SERVER_SERVERSTATUS_STATE);
1096
0
    UA_ReadRequest request;
1097
0
    UA_ReadRequest_init(&request);
1098
0
    request.nodesToRead = &rvid;
1099
0
    request.nodesToReadSize = 1;
1100
0
    UA_StatusCode retval =
1101
0
        __Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
1102
0
                              (UA_ClientAsyncServiceCallback)backgroundConnectivityCallback,
1103
0
                              &UA_TYPES[UA_TYPES_READRESPONSE], NULL, NULL);
1104
0
    if(retval == UA_STATUSCODE_GOOD)
1105
0
        client->pendingConnectivityCheck = true;
1106
0
}
1107
1108
/* Regular housekeeping activities in the client -- called via a cyclic callback */
1109
static void
1110
0
clientHouseKeeping(UA_Client *client, void *_) {
1111
0
    lockClient(client);
1112
1113
0
    UA_LOG_DEBUG(client->config.logging, UA_LOGCATEGORY_CLIENT,
1114
0
                 "Internally check the the client state and "
1115
0
                 "required activities");
1116
1117
    /* Renew Secure Channel */
1118
0
    __Client_renewSecureChannel(client);
1119
1120
    /* Send read requests from time to time to test the connectivity */
1121
0
    __Client_backgroundConnectivity(client);
1122
1123
0
#ifdef UA_ENABLE_SUBSCRIPTIONS
1124
    /* Feed the server PublishRequests for the Subscriptions */
1125
0
    __Client_Subscriptions_backgroundPublish(client);
1126
1127
    /* Check for inactive Subscriptions */
1128
0
    __Client_Subscriptions_backgroundPublishInactivityCheck(client);
1129
0
#endif
1130
1131
    /* Did async services time out? Process callbacks with an error code */
1132
0
    asyncServiceTimeoutCheck(client);
1133
1134
    /* Log and notify user if the client state has changed */
1135
0
    notifyClientState(client);
1136
1137
0
    unlockClient(client);
1138
0
}
1139
1140
UA_StatusCode
1141
0
__UA_Client_startup(UA_Client *client) {
1142
0
    UA_LOCK_ASSERT(&client->clientMutex);
1143
1144
0
    UA_ClientConfig *config = &client->config;
1145
0
    UA_EventLoop *el = config->eventLoop;
1146
0
    UA_CHECK_ERROR(el != NULL,
1147
0
                   return UA_STATUSCODE_BADINTERNALERROR,
1148
0
                   config->logging, UA_LOGCATEGORY_CLIENT,
1149
0
                   "No EventLoop configured");
1150
1151
    /* Set up the repeated timer callback for checking the internal state. Like
1152
     * in the public API UA_Client_addRepeatedCallback, but without locking the
1153
     * mutex again */
1154
0
    UA_StatusCode rv = UA_STATUSCODE_GOOD;
1155
0
    if(!client->houseKeepingCallbackId) {
1156
0
        rv = el->addTimer(el, (UA_Callback)clientHouseKeeping,
1157
0
                          client, NULL, 1000.0, NULL,
1158
0
                          UA_TIMERPOLICY_CURRENTTIME,
1159
0
                          &client->houseKeepingCallbackId);
1160
0
        UA_CHECK_STATUS(rv, return rv);
1161
0
    }
1162
1163
    /* Start the EventLoop? */
1164
0
    if(el->state == UA_EVENTLOOPSTATE_FRESH) {
1165
0
        rv = el->start(el);
1166
0
        UA_CHECK_STATUS(rv, return rv);
1167
0
    }
1168
1169
    /* Call the application notification callback */
1170
0
    if(config->lifecycleNotificationCallback)
1171
0
        config->lifecycleNotificationCallback(client, UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STARTED,
1172
0
                                              UA_KEYVALUEMAP_NULL);
1173
0
    if(config->globalNotificationCallback)
1174
0
        config->globalNotificationCallback(client, UA_APPLICATIONNOTIFICATIONTYPE_LIFECYCLE_STARTED,
1175
0
                                           UA_KEYVALUEMAP_NULL);
1176
1177
0
    return UA_STATUSCODE_GOOD;
1178
0
}
1179
1180
UA_StatusCode
1181
0
UA_Client_run_iterate(UA_Client *client, UA_UInt32 timeout) {
1182
    /* Make sure the EventLoop has been started */
1183
0
    lockClient(client);
1184
0
    UA_StatusCode rv = __UA_Client_startup(client);
1185
0
    unlockClient(client);
1186
0
    UA_CHECK_STATUS(rv, return rv);
1187
1188
    /* All timers and network events are triggered in the EventLoop. Release the
1189
     * client lock before. The callbacks from the EventLoop take the lock
1190
     * again. */
1191
0
    UA_EventLoop *el = client->config.eventLoop;
1192
0
    rv = el->run(el, timeout);
1193
0
    UA_CHECK_STATUS(rv, return rv);
1194
0
    return client->connectStatus;
1195
0
}
1196
1197
const UA_DataType *
1198
0
UA_Client_findDataType(UA_Client *client, const UA_NodeId *typeId) {
1199
0
    return UA_findDataTypeWithCustom(typeId, client->config.customDataTypes);
1200
0
}
1201
1202
/*************************/
1203
/* Connection Attributes */
1204
/*************************/
1205
1206
#define UA_CONNECTIONATTRIBUTESSIZE 3
1207
static const UA_QualifiedName connectionAttributes[UA_CONNECTIONATTRIBUTESSIZE] = {
1208
    {0, UA_STRING_STATIC("serverDescription")},
1209
    {0, UA_STRING_STATIC("securityPolicyUri")},
1210
    {0, UA_STRING_STATIC("securityMode")}
1211
};
1212
1213
static UA_StatusCode
1214
getConnectionttribute(UA_Client *client, const UA_QualifiedName key,
1215
0
                      UA_Variant *outValue, UA_Boolean copy) {
1216
0
    if(!outValue)
1217
0
        return UA_STATUSCODE_BADINTERNALERROR;
1218
1219
0
    UA_Variant localAttr;
1220
1221
0
    if(UA_QualifiedName_equal(&key, &connectionAttributes[0])) {
1222
        /* ServerDescription */
1223
0
        UA_Variant_setScalar(&localAttr, &client->endpoint.server,
1224
0
                             &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
1225
0
    } else if(UA_QualifiedName_equal(&key, &connectionAttributes[1])) {
1226
        /* SecurityPolicyUri */
1227
0
        const UA_SecurityPolicy *sp = client->channel.securityPolicy;
1228
0
        if(!sp)
1229
0
            return UA_STATUSCODE_BADNOTCONNECTED;
1230
0
        UA_Variant_setScalar(&localAttr, (void*)(uintptr_t)&sp->policyUri,
1231
0
                             &UA_TYPES[UA_TYPES_STRING]);
1232
0
    } else if(UA_QualifiedName_equal(&key, &connectionAttributes[2])) {
1233
        /* SecurityMode */
1234
0
        UA_Variant_setScalar(&localAttr, &client->channel.securityMode,
1235
0
                             &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE]);
1236
0
    } else {
1237
0
        return UA_STATUSCODE_BADINTERNALERROR;
1238
0
    }
1239
1240
0
    if(copy)
1241
0
        return UA_Variant_copy(&localAttr, outValue);
1242
1243
0
    localAttr.storageType = UA_VARIANT_DATA_NODELETE;
1244
0
    *outValue = localAttr;
1245
0
    return UA_STATUSCODE_GOOD;
1246
0
}
1247
1248
UA_StatusCode
1249
UA_Client_getConnectionAttribute(UA_Client *client, const UA_QualifiedName key,
1250
0
                                 UA_Variant *outValue) {
1251
0
    lockClient(client);
1252
0
    UA_StatusCode res = getConnectionttribute(client, key, outValue, false);
1253
0
    unlockClient(client);
1254
0
    return res;
1255
0
}
1256
1257
UA_StatusCode
1258
UA_Client_getConnectionAttributeCopy(UA_Client *client, const UA_QualifiedName key,
1259
0
                                     UA_Variant *outValue) {
1260
0
    lockClient(client);
1261
0
    UA_StatusCode res = getConnectionttribute(client, key, outValue, true);
1262
0
    unlockClient(client);
1263
0
    return res;
1264
0
}
1265
1266
UA_StatusCode
1267
UA_Client_getConnectionAttribute_scalar(UA_Client *client,
1268
                                        const UA_QualifiedName key,
1269
                                        const UA_DataType *type,
1270
0
                                        void *outValue) {
1271
0
    lockClient(client);
1272
1273
0
    UA_Variant attr;
1274
0
    UA_StatusCode res = getConnectionttribute(client, key, &attr, false);
1275
0
    if(res != UA_STATUSCODE_GOOD) {
1276
0
        unlockClient(client);
1277
0
        return res;
1278
0
    }
1279
1280
0
    if(!UA_Variant_hasScalarType(&attr, type)) {
1281
0
        unlockClient(client);
1282
0
        return UA_STATUSCODE_BADNOTFOUND;
1283
0
    }
1284
1285
0
    memcpy(outValue, attr.data, type->memSize);
1286
1287
0
    unlockClient(client);
1288
0
    return UA_STATUSCODE_GOOD;
1289
0
}
1290
1291
/* Namespace Mapping */
1292
1293
UA_StatusCode
1294
UA_Client_getNamespaceUri(UA_Client *client, UA_UInt16 index,
1295
0
                          UA_String *nsUri) {
1296
0
    lockClient(client);
1297
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1298
0
    if(index < client->namespacesSize)
1299
0
        res = UA_String_copy(&client->namespaces[index], nsUri);
1300
0
    else
1301
0
        res = UA_STATUSCODE_BADNOTFOUND;
1302
0
    unlockClient(client);
1303
0
    return res;
1304
0
}
1305
1306
UA_StatusCode
1307
UA_Client_getNamespaceIndex(UA_Client *client, const UA_String nsUri,
1308
0
                            UA_UInt16 *outIndex) {
1309
0
    lockClient(client);
1310
0
    for(size_t i = 0; i < client->namespacesSize; i++) {
1311
0
        if(UA_String_equal(&nsUri, &client->namespaces[i])) {
1312
0
            *outIndex = (UA_UInt16)i;
1313
0
            unlockClient(client);
1314
0
            return UA_STATUSCODE_GOOD;
1315
0
        }
1316
0
    }
1317
0
    unlockClient(client);
1318
0
    return UA_STATUSCODE_BADNOTFOUND;
1319
0
}
1320
1321
UA_StatusCode
1322
UA_Client_addNamespace(UA_Client *client, const UA_String nsUri,
1323
0
                       UA_UInt16 *outIndex) {
1324
0
    UA_StatusCode res = UA_Client_getNamespaceIndex(client, nsUri, outIndex);
1325
0
    if(res == UA_STATUSCODE_GOOD)
1326
0
        return res;
1327
0
    lockClient(client);
1328
0
    res = UA_Array_appendCopy((void**)&client->namespaces, &client->namespacesSize,
1329
0
                              &nsUri, &UA_TYPES[UA_TYPES_STRING]);
1330
0
    if(res == UA_STATUSCODE_GOOD)
1331
0
        *outIndex = (UA_UInt16)(client->namespacesSize - 1);
1332
0
    unlockClient(client);
1333
0
    return res;
1334
0
}
1335
1336
120
void lockClient(UA_Client *client) {
1337
120
    if(UA_LIKELY(client->config.eventLoop && client->config.eventLoop->lock))
1338
120
        client->config.eventLoop->lock(client->config.eventLoop);
1339
120
    UA_LOCK(&client->clientMutex);
1340
120
}
1341
1342
120
void unlockClient(UA_Client *client) {
1343
120
    if(UA_LIKELY(client->config.eventLoop && client->config.eventLoop->unlock))
1344
120
        client->config.eventLoop->unlock(client->config.eventLoop);
1345
120
    UA_UNLOCK(&client->clientMutex);
1346
120
}
1347
1348
/**********************/
1349
/* Connect Shorthands */
1350
/**********************/
1351
1352
UA_StatusCode
1353
UA_ClientConfig_setAuthenticationUsername(UA_ClientConfig *config,
1354
                                          const char *username,
1355
0
                                          const char *password) {
1356
0
    UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
1357
0
    if(!identityToken)
1358
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1359
0
    identityToken->userName = UA_STRING_ALLOC(username);
1360
0
    identityToken->password = UA_BYTESTRING_ALLOC(password);
1361
0
    UA_ExtensionObject_clear(&config->userIdentityToken);
1362
0
    UA_ExtensionObject_setValue(&config->userIdentityToken, identityToken,
1363
0
                                &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]);
1364
0
    return UA_STATUSCODE_GOOD;
1365
0
}
1366
1367
UA_StatusCode
1368
0
UA_Client_connect(UA_Client *client, const char *endpointUrl) {
1369
0
    UA_ClientConfig *cc = UA_Client_getConfig(client);
1370
0
    cc->noSession = false;
1371
0
    return __UA_Client_connect(client, false, endpointUrl);
1372
0
}
1373
1374
UA_StatusCode
1375
0
UA_Client_connectSecureChannel(UA_Client *client, const char *endpointUrl) {
1376
0
    UA_ClientConfig *cc = UA_Client_getConfig(client);
1377
0
    cc->noSession = true; /* Don't open a Session */
1378
0
    return __UA_Client_connect(client, false, endpointUrl);
1379
0
}
1380
1381
UA_StatusCode
1382
0
UA_Client_connectAsync(UA_Client *client, const char *endpointUrl) {
1383
0
    UA_ClientConfig *cc = UA_Client_getConfig(client);
1384
0
    cc->noSession = false; /* Open a Session */
1385
0
    return __UA_Client_connect(client, true, endpointUrl);
1386
0
}
1387
1388
UA_StatusCode
1389
0
UA_Client_connectSecureChannelAsync(UA_Client *client, const char *endpointUrl) {
1390
0
    UA_ClientConfig *cc = UA_Client_getConfig(client);
1391
0
    cc->noSession = true; /* Don't open a Session */
1392
0
    return __UA_Client_connect(client, true, endpointUrl);
1393
0
}
1394
1395
UA_StatusCode
1396
UA_Client_connectUsername(UA_Client *client, const char *endpointUrl,
1397
0
                          const char *username, const char *password) {
1398
0
    UA_ClientConfig *cc = UA_Client_getConfig(client);
1399
0
    UA_StatusCode res = UA_ClientConfig_setAuthenticationUsername(cc, username, password);
1400
0
    if(res != UA_STATUSCODE_GOOD)
1401
0
        return res;
1402
0
    return UA_Client_connect(client, endpointUrl);
1403
0
}
1404
1405
/**********************/
1406
/* Service Shorthands */
1407
/**********************/
1408
1409
void
1410
__UA_Client_Service(UA_Client *client, const void *request,
1411
                    const UA_DataType *requestType, void *response,
1412
0
                    const UA_DataType *responseType) {
1413
0
    lockClient(client);
1414
0
    __Client_Service(client, request, requestType, response, responseType);
1415
0
    unlockClient(client);
1416
0
}
1417
1418
UA_ReadResponse
1419
0
UA_Client_Service_read(UA_Client *client, const UA_ReadRequest request) {
1420
0
    UA_ReadResponse response;
1421
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
1422
0
                        &response, &UA_TYPES[UA_TYPES_READRESPONSE]);
1423
0
    return response;
1424
0
}
1425
1426
UA_WriteResponse
1427
0
UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request) {
1428
0
    UA_WriteResponse response;
1429
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_WRITEREQUEST],
1430
0
                        &response, &UA_TYPES[UA_TYPES_WRITERESPONSE]);
1431
0
    return response;
1432
0
}
1433
1434
#ifdef UA_ENABLE_HISTORIZING
1435
1436
UA_HistoryReadResponse
1437
UA_Client_Service_historyRead(UA_Client *client,
1438
0
                              const UA_HistoryReadRequest request) {
1439
0
    UA_HistoryReadResponse response;
1440
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_HISTORYREADREQUEST],
1441
0
                        &response, &UA_TYPES[UA_TYPES_HISTORYREADRESPONSE]);
1442
0
    return response;
1443
0
}
1444
1445
UA_HistoryUpdateResponse
1446
UA_Client_Service_historyUpdate(UA_Client *client,
1447
0
                                const UA_HistoryUpdateRequest request) {
1448
0
    UA_HistoryUpdateResponse response;
1449
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_HISTORYUPDATEREQUEST],
1450
0
                        &response, &UA_TYPES[UA_TYPES_HISTORYUPDATERESPONSE]);
1451
0
    return response;
1452
0
}
1453
1454
#endif
1455
1456
UA_CallResponse
1457
UA_Client_Service_call(UA_Client *client,
1458
0
                       const UA_CallRequest request) {
1459
0
    UA_CallResponse response;
1460
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CALLREQUEST],
1461
0
                        &response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
1462
0
    return response;
1463
0
}
1464
1465
UA_AddNodesResponse
1466
UA_Client_Service_addNodes(UA_Client *client,
1467
0
                           const UA_AddNodesRequest request) {
1468
0
    UA_AddNodesResponse response;
1469
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
1470
0
                        &response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
1471
0
    return response;
1472
0
}
1473
1474
UA_AddReferencesResponse
1475
UA_Client_Service_addReferences(UA_Client *client,
1476
0
                                const UA_AddReferencesRequest request) {
1477
0
    UA_AddReferencesResponse response;
1478
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST],
1479
0
                        &response, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]);
1480
0
    return response;
1481
0
}
1482
1483
UA_DeleteNodesResponse
1484
UA_Client_Service_deleteNodes(UA_Client *client,
1485
0
                              const UA_DeleteNodesRequest request) {
1486
0
    UA_DeleteNodesResponse response;
1487
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
1488
0
                        &response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
1489
0
    return response;
1490
0
}
1491
1492
UA_DeleteReferencesResponse
1493
UA_Client_Service_deleteReferences(UA_Client *client,
1494
0
                                   const UA_DeleteReferencesRequest request) {
1495
0
    UA_DeleteReferencesResponse response;
1496
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST],
1497
0
                        &response, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]);
1498
0
    return response;
1499
0
}
1500
1501
UA_BrowseResponse
1502
UA_Client_Service_browse(UA_Client *client,
1503
0
                         const UA_BrowseRequest request) {
1504
0
    UA_BrowseResponse response;
1505
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSEREQUEST],
1506
0
                        &response, &UA_TYPES[UA_TYPES_BROWSERESPONSE]);
1507
0
    return response;
1508
0
}
1509
1510
UA_BrowseNextResponse
1511
UA_Client_Service_browseNext(UA_Client *client,
1512
0
                             const UA_BrowseNextRequest request) {
1513
0
    UA_BrowseNextResponse response;
1514
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST],
1515
0
                        &response, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]);
1516
0
    return response;
1517
0
}
1518
1519
UA_TranslateBrowsePathsToNodeIdsResponse
1520
UA_Client_Service_translateBrowsePathsToNodeIds(UA_Client *client,
1521
0
    const UA_TranslateBrowsePathsToNodeIdsRequest request) {
1522
0
    UA_TranslateBrowsePathsToNodeIdsResponse response;
1523
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST],
1524
0
                        &response, &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]);
1525
0
    return response;
1526
0
}
1527
1528
UA_RegisterNodesResponse
1529
UA_Client_Service_registerNodes(UA_Client *client,
1530
0
                                const UA_RegisterNodesRequest request) {
1531
0
    UA_RegisterNodesResponse response;
1532
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST],
1533
0
                        &response, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]);
1534
0
    return response;
1535
0
}
1536
1537
UA_UnregisterNodesResponse
1538
UA_Client_Service_unregisterNodes(UA_Client *client,
1539
0
                                  const UA_UnregisterNodesRequest request) {
1540
0
    UA_UnregisterNodesResponse response;
1541
0
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST],
1542
0
                        &response, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]);
1543
0
    return response;
1544
0
}
1545
1546
#ifdef UA_ENABLE_QUERY
1547
1548
UA_QueryFirstResponse
1549
UA_Client_Service_queryFirst(UA_Client *client,
1550
                             const UA_QueryFirstRequest request) {
1551
    UA_QueryFirstResponse response;
1552
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
1553
                        &response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
1554
    return response;
1555
}
1556
1557
UA_QueryNextResponse
1558
UA_Client_Service_queryNext(UA_Client *client, const UA_QueryNextRequest request) {
1559
    UA_QueryNextResponse response;
1560
    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYNEXTREQUEST],
1561
                        &response, &UA_TYPES[UA_TYPES_QUERYNEXTRESPONSE]);
1562
    return response;
1563
}
1564
1565
#endif
1566
1567
/********************/
1568
/* Async Shorthands */
1569
/********************/
1570
1571
UA_StatusCode
1572
__UA_Client_AsyncService(UA_Client *client, const void *request,
1573
                         const UA_DataType *requestType,
1574
                         UA_ClientAsyncServiceCallback callback,
1575
                         const UA_DataType *responseType,
1576
0
                         void *userdata, UA_UInt32 *requestId) {
1577
0
    lockClient(client);
1578
0
    UA_StatusCode res =
1579
0
        __Client_AsyncService(client, request, requestType, callback, responseType,
1580
0
                              userdata, requestId);
1581
0
    unlockClient(client);
1582
0
    return res;
1583
0
}
1584
1585
UA_StatusCode
1586
UA_Client_sendAsyncReadRequest(UA_Client *client, UA_ReadRequest *request,
1587
                               UA_ClientAsyncReadCallback readCallback,
1588
0
                               void *userdata, UA_UInt32 *reqId) {
1589
0
    return __UA_Client_AsyncService(client, request, &UA_TYPES[UA_TYPES_READREQUEST],
1590
0
                                    (UA_ClientAsyncServiceCallback)readCallback,
1591
0
                                    &UA_TYPES[UA_TYPES_READRESPONSE], userdata, reqId);
1592
0
}
1593
1594
UA_StatusCode
1595
UA_Client_sendAsyncWriteRequest(UA_Client *client, UA_WriteRequest *request,
1596
                                UA_ClientAsyncWriteCallback writeCallback,
1597
0
                                void *userdata, UA_UInt32 *reqId) {
1598
0
    return __UA_Client_AsyncService(client, request, &UA_TYPES[UA_TYPES_WRITEREQUEST],
1599
0
                                    (UA_ClientAsyncServiceCallback)writeCallback,
1600
0
                                    &UA_TYPES[UA_TYPES_WRITERESPONSE], userdata, reqId);
1601
0
}
1602
1603
UA_StatusCode
1604
UA_Client_sendAsyncBrowseRequest(UA_Client *client, UA_BrowseRequest *request,
1605
                                 UA_ClientAsyncBrowseCallback browseCallback,
1606
0
                                 void *userdata, UA_UInt32 *reqId) {
1607
0
    return __UA_Client_AsyncService(client, request, &UA_TYPES[UA_TYPES_BROWSEREQUEST],
1608
0
                                    (UA_ClientAsyncServiceCallback)browseCallback,
1609
0
                                    &UA_TYPES[UA_TYPES_BROWSERESPONSE], userdata, reqId);
1610
0
}
1611
1612
UA_StatusCode
1613
UA_Client_sendAsyncBrowseNextRequest(UA_Client *client,
1614
                                     UA_BrowseNextRequest *request,
1615
                                     UA_ClientAsyncBrowseNextCallback browseNextCallback,
1616
0
                                     void *userdata, UA_UInt32 *reqId) {
1617
0
    return __UA_Client_AsyncService(client, request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST],
1618
0
                                    (UA_ClientAsyncServiceCallback)browseNextCallback,
1619
0
                                    &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE], userdata, reqId);
1620
0
}