/src/open62541/src/server/ua_services.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 2014-2024 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2014-2016 (c) Sten Grüner |
7 | | * Copyright 2014-2015, 2017 (c) Florian Palm |
8 | | * Copyright 2015-2016 (c) Chris Iatrou |
9 | | * Copyright 2015-2016 (c) Oleksiy Vasylyev |
10 | | * Copyright 2016 (c) Joakim L. Gilje |
11 | | * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH |
12 | | * Copyright 2016 (c) TorbenD |
13 | | * Copyright 2017 (c) frax2222 |
14 | | * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB |
15 | | * Copyright 2019 (c) Kalycito Infotech Private Limited |
16 | | * Copyright 2023 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Phuong Nguyen) |
17 | | * Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer) |
18 | | */ |
19 | | |
20 | | /* This file contains the service invocation logic that is called from all |
21 | | * communication backends */ |
22 | | |
23 | | #include "ua_server_internal.h" |
24 | | #include "ua_services.h" |
25 | | #include "../ua_types_encoding_binary.h" |
26 | | |
27 | | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
28 | | /* store the authentication token and session ID so we can help fuzzing by |
29 | | * setting these values in the next request automatically */ |
30 | | UA_NodeId unsafe_fuzz_authenticationToken = {0, UA_NODEIDTYPE_NUMERIC, {0}}; |
31 | | #endif |
32 | | |
33 | | /* The counterOffset is the offset of the UA_ServiceCounterDataType for the |
34 | | * service in the UA_ SessionDiagnosticsDataType. */ |
35 | | #ifdef UA_ENABLE_DIAGNOSTICS |
36 | | # define UA_SERVICECOUNTER_OFFSET_NONE(requiresSession) 0, requiresSession |
37 | | # define UA_SERVICECOUNTER_OFFSET(X, requiresSession) \ |
38 | | offsetof(UA_SessionDiagnosticsDataType, X), requiresSession |
39 | | #else |
40 | | # define UA_SERVICECOUNTER_OFFSET_NONE(requiresSession) requiresSession |
41 | | # define UA_SERVICECOUNTER_OFFSET(X, requiresSession) requiresSession |
42 | | #endif |
43 | | |
44 | | static UA_ServiceDescription serviceDescriptions[] = { |
45 | | {UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY, |
46 | | UA_SERVICECOUNTER_OFFSET_NONE(false), Service_GetEndpoints, |
47 | | &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]}, |
48 | | {UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY, |
49 | | UA_SERVICECOUNTER_OFFSET_NONE(false), Service_FindServers, |
50 | | &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST], &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]}, |
51 | | #ifdef UA_ENABLE_DISCOVERY |
52 | | {UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTBINARY, |
53 | | UA_SERVICECOUNTER_OFFSET_NONE(false), Service_RegisterServer, |
54 | | &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]}, |
55 | | {UA_NS0ID_REGISTERSERVER2REQUEST_ENCODING_DEFAULTBINARY, |
56 | | UA_SERVICECOUNTER_OFFSET_NONE(false), Service_RegisterServer2, |
57 | | &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]}, |
58 | | {UA_NS0ID_FINDSERVERSONNETWORKREQUEST_ENCODING_DEFAULTBINARY, |
59 | | UA_SERVICECOUNTER_OFFSET_NONE(false), Service_FindServersOnNetwork, |
60 | | &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST], &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]}, |
61 | | #endif |
62 | | {UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY, |
63 | | UA_SERVICECOUNTER_OFFSET_NONE(false), NULL, |
64 | | &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]}, |
65 | | {UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY, |
66 | | UA_SERVICECOUNTER_OFFSET_NONE(false), NULL, |
67 | | &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]}, |
68 | | {UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY, |
69 | | UA_SERVICECOUNTER_OFFSET_NONE(true), NULL, |
70 | | &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]}, |
71 | | {UA_NS0ID_CANCELREQUEST_ENCODING_DEFAULTBINARY, |
72 | | UA_SERVICECOUNTER_OFFSET_NONE(true), Service_Cancel, |
73 | | &UA_TYPES[UA_TYPES_CANCELREQUEST], &UA_TYPES[UA_TYPES_CANCELRESPONSE]}, |
74 | | {UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY, |
75 | | UA_SERVICECOUNTER_OFFSET(readCount, true), Service_Read, |
76 | | &UA_TYPES[UA_TYPES_READREQUEST], &UA_TYPES[UA_TYPES_READRESPONSE]}, |
77 | | {UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY, |
78 | | UA_SERVICECOUNTER_OFFSET(writeCount, true), Service_Write, |
79 | | &UA_TYPES[UA_TYPES_WRITEREQUEST], &UA_TYPES[UA_TYPES_WRITERESPONSE]}, |
80 | | {UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY, |
81 | | UA_SERVICECOUNTER_OFFSET(browseCount, true), Service_Browse, |
82 | | &UA_TYPES[UA_TYPES_BROWSEREQUEST], &UA_TYPES[UA_TYPES_BROWSERESPONSE]}, |
83 | | {UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY, |
84 | | UA_SERVICECOUNTER_OFFSET(browseNextCount, true), Service_BrowseNext, |
85 | | &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST], &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]}, |
86 | | {UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY, |
87 | | UA_SERVICECOUNTER_OFFSET(registerNodesCount, true), Service_RegisterNodes, |
88 | | &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST], &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]}, |
89 | | {UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY, |
90 | | UA_SERVICECOUNTER_OFFSET(unregisterNodesCount, true), Service_UnregisterNodes, |
91 | | &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST], &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]}, |
92 | | {UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY, |
93 | | UA_SERVICECOUNTER_OFFSET(translateBrowsePathsToNodeIdsCount, true), Service_TranslateBrowsePathsToNodeIds, |
94 | | &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]}, |
95 | | #ifdef UA_ENABLE_SUBSCRIPTIONS |
96 | | {UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY, |
97 | | UA_SERVICECOUNTER_OFFSET(createSubscriptionCount, true), Service_CreateSubscription, |
98 | | &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]}, |
99 | | {UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY, |
100 | | UA_SERVICECOUNTER_OFFSET(publishCount, true), Service_Publish, |
101 | | &UA_TYPES[UA_TYPES_PUBLISHREQUEST], &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]}, |
102 | | {UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY, |
103 | | UA_SERVICECOUNTER_OFFSET(republishCount, true), Service_Republish, |
104 | | &UA_TYPES[UA_TYPES_REPUBLISHREQUEST], &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE]}, |
105 | | {UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY, |
106 | | UA_SERVICECOUNTER_OFFSET(modifySubscriptionCount, true), Service_ModifySubscription, |
107 | | &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]}, |
108 | | {UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY, |
109 | | UA_SERVICECOUNTER_OFFSET(setPublishingModeCount, true), Service_SetPublishingMode, |
110 | | &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST], &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]}, |
111 | | {UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY, |
112 | | UA_SERVICECOUNTER_OFFSET(deleteSubscriptionsCount, true), Service_DeleteSubscriptions, |
113 | | &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]}, |
114 | | {UA_NS0ID_TRANSFERSUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY, |
115 | | UA_SERVICECOUNTER_OFFSET(transferSubscriptionsCount, true), Service_TransferSubscriptions, |
116 | | &UA_TYPES[UA_TYPES_TRANSFERSUBSCRIPTIONSREQUEST], &UA_TYPES[UA_TYPES_TRANSFERSUBSCRIPTIONSRESPONSE]}, |
117 | | {UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY, |
118 | | UA_SERVICECOUNTER_OFFSET(createMonitoredItemsCount, true), Service_CreateMonitoredItems, |
119 | | &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]}, |
120 | | {UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY, |
121 | | UA_SERVICECOUNTER_OFFSET(deleteMonitoredItemsCount, true), Service_DeleteMonitoredItems, |
122 | | &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]}, |
123 | | {UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY, |
124 | | UA_SERVICECOUNTER_OFFSET(modifyMonitoredItemsCount, true), Service_ModifyMonitoredItems, |
125 | | &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]}, |
126 | | {UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY, |
127 | | UA_SERVICECOUNTER_OFFSET(setMonitoringModeCount, true), Service_SetMonitoringMode, |
128 | | &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST], &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]}, |
129 | | {UA_NS0ID_SETTRIGGERINGREQUEST_ENCODING_DEFAULTBINARY, |
130 | | UA_SERVICECOUNTER_OFFSET(setTriggeringCount, true), Service_SetTriggering, |
131 | | &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST], &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]}, |
132 | | #endif |
133 | | #ifdef UA_ENABLE_HISTORIZING |
134 | | {UA_NS0ID_HISTORYREADREQUEST_ENCODING_DEFAULTBINARY, |
135 | | UA_SERVICECOUNTER_OFFSET(historyReadCount, true), Service_HistoryRead, |
136 | | &UA_TYPES[UA_TYPES_HISTORYREADREQUEST], &UA_TYPES[UA_TYPES_HISTORYREADRESPONSE]}, |
137 | | {UA_NS0ID_HISTORYUPDATEREQUEST_ENCODING_DEFAULTBINARY, |
138 | | UA_SERVICECOUNTER_OFFSET(historyUpdateCount, true), Service_HistoryUpdate, |
139 | | &UA_TYPES[UA_TYPES_HISTORYUPDATEREQUEST], &UA_TYPES[UA_TYPES_HISTORYUPDATERESPONSE]}, |
140 | | #endif |
141 | | #ifdef UA_ENABLE_METHODCALLS |
142 | | {UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY, |
143 | | UA_SERVICECOUNTER_OFFSET(callCount, true), Service_Call, |
144 | | &UA_TYPES[UA_TYPES_CALLREQUEST], &UA_TYPES[UA_TYPES_CALLRESPONSE]}, |
145 | | #endif |
146 | | #ifdef UA_ENABLE_NODEMANAGEMENT |
147 | | {UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY, |
148 | | UA_SERVICECOUNTER_OFFSET(addNodesCount, true), Service_AddNodes, |
149 | | &UA_TYPES[UA_TYPES_ADDNODESREQUEST], &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]}, |
150 | | {UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY, |
151 | | UA_SERVICECOUNTER_OFFSET(addReferencesCount, true), Service_AddReferences, |
152 | | &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST], &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]}, |
153 | | {UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY, |
154 | | UA_SERVICECOUNTER_OFFSET(deleteNodesCount, true), Service_DeleteNodes, |
155 | | &UA_TYPES[UA_TYPES_DELETENODESREQUEST], &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]}, |
156 | | {UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY, |
157 | | UA_SERVICECOUNTER_OFFSET(deleteReferencesCount, true), Service_DeleteReferences, |
158 | | &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST], &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]}, |
159 | | #endif |
160 | | {0, UA_SERVICECOUNTER_OFFSET_NONE(false), NULL, NULL, NULL} |
161 | | }; |
162 | | |
163 | | UA_ServiceDescription * |
164 | 22.8k | getServiceDescription(UA_UInt32 requestTypeId) { |
165 | 257k | for(size_t i = 0; serviceDescriptions[i].requestTypeId > 0; i++) { |
166 | 256k | if(serviceDescriptions[i].requestTypeId == requestTypeId) |
167 | 21.9k | return &serviceDescriptions[i]; |
168 | 256k | } |
169 | 927 | return NULL; |
170 | 22.8k | } |
171 | | |
172 | | UA_StatusCode |
173 | | decodeBinaryServiceRequest(UA_Server *server, const UA_ByteString *message, |
174 | | UA_ServiceDescription **description, |
175 | | UA_Request *request, size_t *requestOffset, |
176 | 813 | UA_UInt32 *requestTypeId) { |
177 | 813 | *description = NULL; |
178 | 813 | size_t offset = 0; |
179 | 813 | UA_NodeId typeId; |
180 | 813 | UA_NodeId_init(&typeId); |
181 | 813 | UA_StatusCode res = UA_NodeId_decodeBinary(message, &offset, &typeId); |
182 | 813 | if(res != UA_STATUSCODE_GOOD) |
183 | 15 | return res; |
184 | 798 | if(requestOffset) |
185 | 798 | *requestOffset = offset; |
186 | 798 | if(typeId.namespaceIndex != 0 || |
187 | 782 | typeId.identifierType != UA_NODEIDTYPE_NUMERIC) { |
188 | 17 | UA_NodeId_clear(&typeId); |
189 | 17 | return UA_STATUSCODE_BADSERVICEUNSUPPORTED; |
190 | 17 | } |
191 | | |
192 | 781 | UA_UInt32 numericTypeId = typeId.identifier.numeric; |
193 | 781 | UA_NodeId_clear(&typeId); |
194 | 781 | if(requestTypeId) |
195 | 781 | *requestTypeId = numericTypeId; |
196 | 781 | *description = getServiceDescription(numericTypeId); |
197 | 781 | if(!*description) |
198 | 36 | return UA_STATUSCODE_BADSERVICEUNSUPPORTED; |
199 | | |
200 | 745 | memset(request, 0, sizeof(*request)); |
201 | 745 | UA_DecodeBinaryOptions options; |
202 | 745 | memset(&options, 0, sizeof(options)); |
203 | 745 | options.customTypes = serverCustomTypes(server); |
204 | 745 | res = UA_decodeBinaryInternal(message, &offset, request, |
205 | 745 | (*description)->requestType, &options); |
206 | 745 | if(res == UA_STATUSCODE_GOOD && offset != message->length) |
207 | 190 | res = UA_STATUSCODE_BADDECODINGERROR; |
208 | 745 | if(res != UA_STATUSCODE_GOOD) |
209 | 336 | UA_clear(request, (*description)->requestType); |
210 | 745 | return res; |
211 | 781 | } |
212 | | |
213 | | /* Allocates the results array and iterates over it to execute the operations |
214 | | * within a request */ |
215 | | UA_StatusCode |
216 | | allocProcessServiceOperations(UA_Server *server, UA_Session *session, |
217 | | UA_ServiceOperation operationCallback, |
218 | | const void *context, const size_t *requestOperations, |
219 | | const UA_DataType *requestOperationsType, |
220 | | size_t *responseOperations, |
221 | 860 | const UA_DataType *responseOperationsType) { |
222 | 860 | size_t ops = *requestOperations; |
223 | 860 | if(ops == 0) |
224 | 217 | return UA_STATUSCODE_BADNOTHINGTODO; |
225 | | |
226 | | /* No padding after size_t */ |
227 | 643 | void **respPos = (void**)((uintptr_t)responseOperations + sizeof(size_t)); |
228 | 643 | *respPos = UA_Array_new(ops, responseOperationsType); |
229 | 643 | if(!(*respPos)) |
230 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
231 | | |
232 | 643 | *responseOperations = ops; |
233 | 643 | uintptr_t respOp = (uintptr_t)*respPos; |
234 | | /* No padding after size_t */ |
235 | 643 | uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t)); |
236 | 82.0k | for(size_t i = 0; i < ops; i++) { |
237 | 81.4k | operationCallback(server, session, context, (void*)reqOp, (void*)respOp); |
238 | 81.4k | if(session && session->state == UA_SESSIONSTATE_CLOSED) |
239 | 0 | return UA_STATUSCODE_BADSESSIONCLOSED; |
240 | 81.4k | reqOp += requestOperationsType->memSize; |
241 | 81.4k | respOp += responseOperationsType->memSize; |
242 | 81.4k | } |
243 | 643 | return UA_STATUSCODE_GOOD; |
244 | 643 | } |
245 | | |
246 | | static UA_UInt32 |
247 | 9.76k | getUacpRequestId(const UA_SecureChannel *channel, UA_UInt64 responseToken) { |
248 | 9.76k | if(channel->transport != UA_SECURECHANNEL_TRANSPORT_UACP) |
249 | 0 | return 0; |
250 | 9.76k | UA_assert(responseToken <= UINT32_MAX); |
251 | 9.76k | return (UA_UInt32)responseToken; |
252 | 9.76k | } |
253 | | |
254 | | static UA_Boolean |
255 | | processServiceInternal(UA_Server *server, UA_SecureChannel *channel, UA_Session *session, |
256 | | UA_UInt64 responseToken, UA_ServiceDescription *sd, |
257 | 8.65k | const UA_Request *request, UA_Response *response) { |
258 | 8.65k | UA_ResponseHeader *rh = &response->responseHeader; |
259 | | |
260 | | /* A callback before service execution can close the resolved Session. */ |
261 | 8.65k | if(session && session->state == UA_SESSIONSTATE_CLOSED) { |
262 | 0 | rh->serviceResult = UA_STATUSCODE_BADSESSIONCLOSED; |
263 | 0 | return true; |
264 | 0 | } |
265 | | |
266 | | /* Check timestamp in the request header */ |
267 | 8.65k | if(request->requestHeader.timestamp == 0 && |
268 | 2.25k | server->config.verifyRequestTimestamp <= UA_RULEHANDLING_WARN) { |
269 | 1.84k | UA_LOG_WARNING_CHANNEL(server->config.logging, channel, |
270 | 1.84k | "The server sends no timestamp in the request header. " |
271 | 1.84k | "See the 'verifyRequestTimestamp' setting."); |
272 | 1.84k | if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_ABORT) { |
273 | 84 | rh->serviceResult = UA_STATUSCODE_BADINVALIDTIMESTAMP; |
274 | 84 | return true; |
275 | 84 | } |
276 | 1.84k | } |
277 | | |
278 | | /* If it is an unencrypted (#None) channel, only allow the discovery services */ |
279 | 8.56k | if(server->config.securityPolicyNoneDiscoveryOnly && |
280 | 164 | channel->securityPolicy->policyType == UA_SECURITYPOLICYTYPE_NONE && |
281 | 164 | sd->requestType != &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST] && |
282 | 89 | sd->requestType != &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST] |
283 | 35 | #if defined(UA_ENABLE_DISCOVERY) |
284 | 35 | && sd->requestType != &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST] |
285 | 8.56k | #endif |
286 | 8.56k | ) { |
287 | 35 | rh->serviceResult = UA_STATUSCODE_BADSECURITYPOLICYREJECTED; |
288 | 35 | return true; |
289 | 35 | } |
290 | | |
291 | | /* Session lifecycle services */ |
292 | 8.53k | if(sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST] || |
293 | 2.54k | sd->requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST] || |
294 | 7.42k | sd->requestType == &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) { |
295 | 7.42k | if(sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) |
296 | 5.99k | Service_CreateSession(server, channel, request, response); |
297 | 1.43k | else if(sd->requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]) |
298 | 1.43k | Service_ActivateSession(server, channel, request, response); |
299 | 0 | else |
300 | 0 | Service_CloseSession(server, channel, request, response); |
301 | | /* Store the authentication token created during CreateSession to help |
302 | | * fuzzing cover more lines */ |
303 | 7.42k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
304 | 7.42k | if(sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) { |
305 | 5.99k | UA_CreateSessionResponse *res = &response->createSessionResponse; |
306 | 5.99k | UA_NodeId_clear(&unsafe_fuzz_authenticationToken); |
307 | 5.99k | UA_NodeId_copy(&res->authenticationToken, &unsafe_fuzz_authenticationToken); |
308 | 5.99k | } |
309 | 7.42k | #endif |
310 | 7.42k | return true; |
311 | 7.42k | } |
312 | | |
313 | | /* Set an anonymous, inactive session for services that need no session */ |
314 | 1.11k | UA_Session anonymousSession; |
315 | 1.11k | if(!session) { |
316 | 236 | UA_assert(!sd->sessionRequired); |
317 | 236 | UA_Session_init(&anonymousSession); |
318 | 236 | anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL); |
319 | 236 | anonymousSession.channel = channel; |
320 | 236 | session = &anonymousSession; |
321 | 236 | } |
322 | | |
323 | | /* Trying to use a non-activated session? */ |
324 | 1.11k | if(sd->sessionRequired && |
325 | 875 | session->state != UA_SESSIONSTATE_ACTIVATED) { |
326 | 0 | UA_assert(session != &anonymousSession); /* because sd->sessionRequired */ |
327 | 0 | #ifdef UA_ENABLE_TYPEDESCRIPTION |
328 | 0 | UA_LOG_WARNING_SESSION(server->config.logging, session, |
329 | 0 | "%s refused on a non-activated session", |
330 | 0 | sd->requestType->typeName); |
331 | | #else |
332 | | UA_LOG_WARNING_SESSION(server->config.logging, session, |
333 | | "Service %" PRIu32 " refused on a non-activated session", |
334 | | sd->requestType->binaryEncodingId.identifier.numeric); |
335 | | #endif |
336 | 0 | UA_Session_remove(server, session, UA_SHUTDOWNREASON_ABORT); |
337 | 0 | rh->serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED; |
338 | 0 | return true; |
339 | 0 | } |
340 | | |
341 | | /* Update the session lifetime */ |
342 | 1.11k | UA_EventLoop *el = server->config.eventLoop; |
343 | 1.11k | UA_DateTime nowMonotonic = el->dateTime_nowMonotonic(el); |
344 | 1.11k | UA_DateTime now = el->dateTime_now(el); |
345 | 1.11k | UA_Session_updateLifetime(session, now, nowMonotonic); |
346 | | |
347 | | /* Store the response token -- will be used to create async responses */ |
348 | 1.11k | server->asyncManager.currentResponseToken = responseToken; |
349 | 1.11k | server->asyncManager.currentUacpRequestId = |
350 | 1.11k | getUacpRequestId(channel, responseToken); |
351 | 1.11k | server->asyncManager.currentRequestHandle = request->requestHeader.requestHandle; |
352 | | |
353 | | /* Execute the service. A user callback inside the service can close the |
354 | | * Session. For synchronous services, do not return a successful response |
355 | | * for a Session that became closed during the operation. */ |
356 | 1.11k | UA_Boolean done = sd->serviceCallback(server, session, request, response); |
357 | 1.11k | if(done && session->state == UA_SESSIONSTATE_CLOSED) |
358 | 0 | rh->serviceResult = UA_STATUSCODE_BADSESSIONCLOSED; |
359 | 1.11k | return done; |
360 | 1.11k | } |
361 | | |
362 | | UA_Boolean |
363 | | processRequest(UA_Server *server, UA_SecureChannel *channel, |
364 | | UA_UInt64 responseToken, UA_ServiceDescription *sd, |
365 | 9.42k | const UA_Request *request, UA_Response *response) { |
366 | 9.42k | UA_LOCK_ASSERT(&server->serviceMutex); |
367 | | |
368 | | /* Set the authenticationToken from the create session request to help |
369 | | * fuzzing cover more lines */ |
370 | 9.42k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
371 | 9.42k | UA_NodeId *authenticationToken = (UA_NodeId *)(uintptr_t) |
372 | 9.42k | &request->requestHeader.authenticationToken; |
373 | 9.42k | if(!UA_NodeId_isNull(authenticationToken) && |
374 | 6.79k | !UA_NodeId_isNull(&unsafe_fuzz_authenticationToken)) { |
375 | 5.16k | UA_NodeId_clear(authenticationToken); |
376 | 5.16k | UA_NodeId_copy(&unsafe_fuzz_authenticationToken, authenticationToken); |
377 | 5.16k | } |
378 | 9.42k | #endif |
379 | | |
380 | | /* Get the session bound to the SecureChannel (not necessarily activated) */ |
381 | 9.42k | UA_Session *session = NULL; |
382 | 9.42k | response->responseHeader.serviceResult = |
383 | 9.42k | getBoundSession(server, channel, &request->requestHeader.authenticationToken, &session); |
384 | 9.42k | if(!session && sd->sessionRequired) |
385 | 767 | return true; |
386 | | |
387 | | /* The session can be NULL if not required */ |
388 | 8.65k | response->responseHeader.serviceResult = UA_STATUSCODE_GOOD; |
389 | 8.65k | UA_NodeId sessionId = (session) ? session->sessionId : UA_NODEID_NULL; |
390 | 8.65k | UA_UInt32 uacpRequestId = getUacpRequestId(channel, responseToken); |
391 | | |
392 | | /* Notify with UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_BEGIN */ |
393 | 8.65k | UA_STATIC_THREAD_LOCAL UA_KeyValuePair notifyPayload[4] = { |
394 | 8.65k | {{0, UA_STRING_STATIC("securechannel-id")}, {0}}, |
395 | 8.65k | {{0, UA_STRING_STATIC("session-id")}, {0}}, |
396 | 8.65k | {{0, UA_STRING_STATIC("request-id")}, {0}}, |
397 | 8.65k | {{0, UA_STRING_STATIC("service-type")}, {0}} |
398 | 8.65k | }; |
399 | 8.65k | UA_KeyValueMap notifyPayloadMap = {4, notifyPayload}; |
400 | 8.65k | UA_Variant_setScalar(¬ifyPayload[0].value, &channel->securityToken.channelId, |
401 | 8.65k | &UA_TYPES[UA_TYPES_UINT32]); |
402 | 8.65k | UA_Variant_setScalar(¬ifyPayload[1].value, &sessionId, |
403 | 8.65k | &UA_TYPES[UA_TYPES_NODEID]); |
404 | 8.65k | UA_Variant_setScalar(¬ifyPayload[2].value, &uacpRequestId, |
405 | 8.65k | &UA_TYPES[UA_TYPES_UINT32]); |
406 | 8.65k | UA_Variant_setScalar(¬ifyPayload[3].value, |
407 | 8.65k | (void *)(uintptr_t)&sd->requestType->typeId, |
408 | 8.65k | &UA_TYPES[UA_TYPES_NODEID]); |
409 | 8.65k | UA_ApplicationNotificationType nt = UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_BEGIN; |
410 | 8.65k | notifyApplication(server, nt, notifyPayloadMap); |
411 | | |
412 | | /* Process the service */ |
413 | 8.65k | beginModelChange(server); |
414 | 8.65k | UA_Boolean done = processServiceInternal(server, channel, session, |
415 | 8.65k | responseToken, sd, request, |
416 | 8.65k | response); |
417 | 8.65k | endModelChange(server); |
418 | 8.65k | #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS |
419 | 8.65k | UA_assert(server->modelChangeDepth == 0); |
420 | 8.65k | #endif |
421 | | |
422 | | /* Notify with UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_END if the service was |
423 | | * completed synchronously. For async completion of a service, this gets |
424 | | * called eventually in ua_server_async.c. */ |
425 | 8.65k | nt = (done) ? UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_END : |
426 | 8.65k | UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_ASYNC; |
427 | 8.65k | notifyApplication(server, nt, notifyPayloadMap); |
428 | | |
429 | | /* Update the service statistics */ |
430 | 8.65k | #ifdef UA_ENABLE_DIAGNOSTICS |
431 | 8.65k | if(session) { |
432 | 5.01k | session->diagnostics.totalRequestCount.totalCount++; |
433 | 5.01k | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) |
434 | 286 | session->diagnostics.totalRequestCount.errorCount++; |
435 | 5.01k | if(sd->counterOffset != 0) { |
436 | 879 | UA_ServiceCounterDataType *serviceCounter = (UA_ServiceCounterDataType*) |
437 | 879 | (((uintptr_t)&session->diagnostics) + sd->counterOffset); |
438 | 879 | serviceCounter->totalCount++; |
439 | 879 | if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) |
440 | 256 | serviceCounter->errorCount++; |
441 | 879 | } |
442 | 5.01k | } |
443 | 8.65k | #endif |
444 | | |
445 | 8.65k | return done; |
446 | 8.65k | } |
447 | | |
448 | | UA_Boolean |
449 | | processDecodedServiceRequest(UA_Server *server, UA_SecureChannel *channel, |
450 | | UA_UInt64 responseToken, |
451 | | UA_ServiceDescription *sd, |
452 | 9.42k | const UA_Request *request, UA_Response *response) { |
453 | 9.42k | UA_LOCK_ASSERT(&server->serviceMutex); |
454 | 9.42k | UA_init(response, sd->responseType); |
455 | 9.42k | response->responseHeader.requestHandle = |
456 | 9.42k | request->requestHeader.requestHandle; |
457 | 9.42k | return processRequest(server, channel, responseToken, sd, request, |
458 | 9.42k | response); |
459 | 9.42k | } |
460 | | |
461 | | void |
462 | | abandonServiceRequest(UA_Server *server, UA_SecureChannel *channel, |
463 | 0 | UA_UInt64 responseToken) { |
464 | 0 | UA_LOCK_ASSERT(&server->serviceMutex); |
465 | 0 | #ifdef UA_ENABLE_SUBSCRIPTIONS |
466 | 0 | for(UA_Session *session = channel->sessions; session; |
467 | 0 | session = session->next) { |
468 | 0 | UA_PublishResponseEntry *pre, *next, *previous = NULL; |
469 | 0 | SIMPLEQ_FOREACH_SAFE(pre, &session->responseQueue, listEntry, next) { |
470 | 0 | if(pre->responseToken != responseToken) { |
471 | 0 | previous = pre; |
472 | 0 | continue; |
473 | 0 | } |
474 | 0 | if(previous) |
475 | 0 | SIMPLEQ_REMOVE_AFTER(&session->responseQueue, previous, |
476 | 0 | listEntry); |
477 | 0 | else |
478 | 0 | SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry); |
479 | 0 | session->responseQueueSize--; |
480 | 0 | UA_PublishResponse_clear(&pre->response); |
481 | 0 | UA_free(pre); |
482 | 0 | UA_LOG_DEBUG_SESSION(server->config.logging, session, |
483 | 0 | "Removed abandoned Publish response token %" |
484 | 0 | PRIu64, responseToken); |
485 | 0 | break; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | #endif |
489 | 0 | UA_AsyncManager_abandon(server, channel, responseToken); |
490 | 0 | } |