#include "ua_server_internal.h"
#include "ua_services.h"
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
UA_NodeId unsafe_fuzz_authenticationToken = {0, UA_NODEIDTYPE_NUMERIC, {0}};
#endif
#ifdef UA_ENABLE_DIAGNOSTICS
# define UA_SERVICECOUNTER_OFFSET_NONE(requiresSession) 0, requiresSession
# define UA_SERVICECOUNTER_OFFSET(X, requiresSession) \
offsetof(UA_SessionDiagnosticsDataType, X), requiresSession
#else
# define UA_SERVICECOUNTER_OFFSET_NONE(requiresSession) requiresSession
# define UA_SERVICECOUNTER_OFFSET(X, requiresSession) requiresSession
#endif
static UA_ServiceDescription serviceDescriptions[] = {
{UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)Service_GetEndpoints,
&UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]},
{UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)Service_FindServers,
&UA_TYPES[UA_TYPES_FINDSERVERSREQUEST], &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]},
#ifdef UA_ENABLE_DISCOVERY
{UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)Service_RegisterServer,
&UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]},
{UA_NS0ID_REGISTERSERVER2REQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)Service_RegisterServer2,
&UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]},
# ifdef UA_ENABLE_DISCOVERY_MULTICAST
{UA_NS0ID_FINDSERVERSONNETWORKREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)Service_FindServersOnNetwork,
&UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST], &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]},
# endif
#endif
{UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)(uintptr_t)Service_CreateSession,
&UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE]},
{UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(false), (UA_Service)(uintptr_t)Service_ActivateSession,
&UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST], &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE]},
{UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(true), (UA_Service)(uintptr_t)Service_CloseSession,
&UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST], &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]},
{UA_NS0ID_CANCELREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET_NONE(true), (UA_Service)Service_Cancel,
&UA_TYPES[UA_TYPES_CANCELREQUEST], &UA_TYPES[UA_TYPES_CANCELRESPONSE]},
{UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(readCount, true), (UA_Service)Service_Read,
&UA_TYPES[UA_TYPES_READREQUEST], &UA_TYPES[UA_TYPES_READRESPONSE]},
{UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(writeCount, true), (UA_Service)Service_Write,
&UA_TYPES[UA_TYPES_WRITEREQUEST], &UA_TYPES[UA_TYPES_WRITERESPONSE]},
{UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(browseCount, true), (UA_Service)Service_Browse,
&UA_TYPES[UA_TYPES_BROWSEREQUEST], &UA_TYPES[UA_TYPES_BROWSERESPONSE]},
{UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(browseNextCount, true), (UA_Service)Service_BrowseNext,
&UA_TYPES[UA_TYPES_BROWSENEXTREQUEST], &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]},
{UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(registerNodesCount, true), (UA_Service)Service_RegisterNodes,
&UA_TYPES[UA_TYPES_REGISTERNODESREQUEST], &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]},
{UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(unregisterNodesCount, true), (UA_Service)Service_UnregisterNodes,
&UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST], &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]},
{UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(translateBrowsePathsToNodeIdsCount, true), (UA_Service)Service_TranslateBrowsePathsToNodeIds,
&UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]},
#ifdef UA_ENABLE_SUBSCRIPTIONS
{UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(createSubscriptionCount, true), (UA_Service)Service_CreateSubscription,
&UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST], &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE]},
{UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(publishCount, true), (UA_Service)Service_Publish,
&UA_TYPES[UA_TYPES_PUBLISHREQUEST], &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]},
{UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(republishCount, true), (UA_Service)Service_Republish,
&UA_TYPES[UA_TYPES_REPUBLISHREQUEST], &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE]},
{UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(modifySubscriptionCount, true), (UA_Service)Service_ModifySubscription,
&UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST], &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE]},
{UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(setPublishingModeCount, true), (UA_Service)Service_SetPublishingMode,
&UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST], &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]},
{UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(deleteSubscriptionsCount, true), (UA_Service)Service_DeleteSubscriptions,
&UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST], &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE]},
{UA_NS0ID_TRANSFERSUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(transferSubscriptionsCount, true), (UA_Service)Service_TransferSubscriptions,
&UA_TYPES[UA_TYPES_TRANSFERSUBSCRIPTIONSREQUEST], &UA_TYPES[UA_TYPES_TRANSFERSUBSCRIPTIONSRESPONSE]},
{UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(createMonitoredItemsCount, true), (UA_Service)Service_CreateMonitoredItems,
&UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST], &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE]},
{UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(deleteMonitoredItemsCount, true), (UA_Service)Service_DeleteMonitoredItems,
&UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST], &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE]},
{UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(modifyMonitoredItemsCount, true), (UA_Service)Service_ModifyMonitoredItems,
&UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE]},
{UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(setMonitoringModeCount, true), (UA_Service)Service_SetMonitoringMode,
&UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST], &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]},
{UA_NS0ID_SETTRIGGERINGREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(setTriggeringCount, true), (UA_Service)Service_SetTriggering,
&UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST], &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]},
#endif
#ifdef UA_ENABLE_HISTORIZING
{UA_NS0ID_HISTORYREADREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(historyReadCount, true), (UA_Service)Service_HistoryRead,
&UA_TYPES[UA_TYPES_HISTORYREADREQUEST], &UA_TYPES[UA_TYPES_HISTORYREADRESPONSE]},
{UA_NS0ID_HISTORYUPDATEREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(historyUpdateCount, true), (UA_Service)Service_HistoryUpdate,
&UA_TYPES[UA_TYPES_HISTORYUPDATEREQUEST], &UA_TYPES[UA_TYPES_HISTORYUPDATERESPONSE]},
#endif
#ifdef UA_ENABLE_METHODCALLS
{UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(callCount, true), (UA_Service)Service_Call,
&UA_TYPES[UA_TYPES_CALLREQUEST], &UA_TYPES[UA_TYPES_CALLRESPONSE]},
#endif
#ifdef UA_ENABLE_NODEMANAGEMENT
{UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(addNodesCount, true), (UA_Service)Service_AddNodes,
&UA_TYPES[UA_TYPES_ADDNODESREQUEST], &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]},
{UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(addReferencesCount, true), (UA_Service)Service_AddReferences,
&UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST], &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]},
{UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(deleteNodesCount, true), (UA_Service)Service_DeleteNodes,
&UA_TYPES[UA_TYPES_DELETENODESREQUEST], &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]},
{UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY,
UA_SERVICECOUNTER_OFFSET(deleteReferencesCount, true), (UA_Service)Service_DeleteReferences,
&UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST], &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]},
#endif
{0, UA_SERVICECOUNTER_OFFSET_NONE(false), NULL, NULL, NULL}
};
UA_ServiceDescription *
getServiceDescription(UA_UInt32 requestTypeId) {
for(size_t i = 0; serviceDescriptions[i].requestTypeId > 0; i++) {
if(serviceDescriptions[i].requestTypeId == requestTypeId)
return &serviceDescriptions[i];
}
return NULL;
}
UA_StatusCode
allocProcessServiceOperations(UA_Server *server, UA_Session *session,
UA_ServiceOperation operationCallback,
const void *context, const size_t *requestOperations,
const UA_DataType *requestOperationsType,
size_t *responseOperations,
const UA_DataType *responseOperationsType) {
size_t ops = *requestOperations;
if(ops == 0)
return UA_STATUSCODE_BADNOTHINGTODO;
void **respPos = (void**)((uintptr_t)responseOperations + sizeof(size_t));
*respPos = UA_Array_new(ops, responseOperationsType);
if(!(*respPos))
return UA_STATUSCODE_BADOUTOFMEMORY;
*responseOperations = ops;
uintptr_t respOp = (uintptr_t)*respPos;
uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t));
for(size_t i = 0; i < ops; i++) {
operationCallback(server, session, context, (void*)reqOp, (void*)respOp);
reqOp += requestOperationsType->memSize;
respOp += responseOperationsType->memSize;
}
return UA_STATUSCODE_GOOD;
}
static UA_Boolean
processServiceInternal(UA_Server *server, UA_SecureChannel *channel, UA_Session *session,
UA_UInt32 requestId, UA_ServiceDescription *sd,
const UA_Request *request, UA_Response *response) {
UA_ResponseHeader *rh = &response->responseHeader;
if(request->requestHeader.timestamp == 0 &&
server->config.verifyRequestTimestamp <= UA_RULEHANDLING_WARN) {
UA_LOG_WARNING_CHANNEL(server->config.logging, channel,
"The server sends no timestamp in the request header. "
"See the 'verifyRequestTimestamp' setting.");
if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_ABORT) {
rh->serviceResult = UA_STATUSCODE_BADINVALIDTIMESTAMP;
return true;
}
}
if(server->config.securityPolicyNoneDiscoveryOnly &&
channel->securityPolicy->policyType == UA_SECURITYPOLICYTYPE_NONE &&
sd->requestType != &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST] &&
sd->requestType != &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST]
#if defined(UA_ENABLE_DISCOVERY) && defined(UA_ENABLE_DISCOVERY_MULTICAST)
&& sd->requestType != &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKREQUEST]
#endif
) {
rh->serviceResult = UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
return true;
}
if(sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST] ||
sd->requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST] ||
sd->requestType == &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) {
UA_ChannelService cs = (UA_ChannelService)(uintptr_t)sd->serviceCallback;
cs(server, channel, request, response);
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if(sd->requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) {
UA_CreateSessionResponse *res = &response->createSessionResponse;
UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
UA_NodeId_copy(&res->authenticationToken, &unsafe_fuzz_authenticationToken);
}
#endif
return true;
}
UA_Session anonymousSession;
if(!session) {
UA_assert(!sd->sessionRequired);
UA_Session_init(&anonymousSession);
anonymousSession.sessionId = UA_NODEID_GUID(0, UA_GUID_NULL);
anonymousSession.channel = channel;
session = &anonymousSession;
}
if(sd->sessionRequired && !session->activated) {
UA_assert(session != &anonymousSession);
#ifdef UA_ENABLE_TYPEDESCRIPTION
UA_LOG_WARNING_SESSION(server->config.logging, session,
"%s refused on a non-activated session",
sd->requestType->typeName);
#else
UA_LOG_WARNING_SESSION(server->config.logging, session,
"Service %" PRIu32 " refused on a non-activated session",
sd->requestType->binaryEncodingId.identifier.numeric);
#endif
UA_Session_remove(server, session, UA_SHUTDOWNREASON_ABORT);
rh->serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED;
return true;
}
UA_EventLoop *el = server->config.eventLoop;
UA_DateTime nowMonotonic = el->dateTime_nowMonotonic(el);
UA_DateTime now = el->dateTime_now(el);
UA_Session_updateLifetime(session, now, nowMonotonic);
server->asyncManager.currentRequestId = requestId;
server->asyncManager.currentRequestHandle = request->requestHeader.requestHandle;
return sd->serviceCallback(server, session, request, response);
}
UA_Boolean
processRequest(UA_Server *server, UA_SecureChannel *channel,
UA_UInt32 requestId, UA_ServiceDescription *sd,
const UA_Request *request, UA_Response *response) {
UA_LOCK_ASSERT(&server->serviceMutex);
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
UA_NodeId *authenticationToken = (UA_NodeId *)(uintptr_t)
&request->requestHeader.authenticationToken;
if(!UA_NodeId_isNull(authenticationToken) &&
!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken)) {
UA_NodeId_clear(authenticationToken);
UA_NodeId_copy(&unsafe_fuzz_authenticationToken, authenticationToken);
}
#endif
UA_Session *session = NULL;
response->responseHeader.serviceResult =
getBoundSession(server, channel, &request->requestHeader.authenticationToken, &session);
if(!session && sd->sessionRequired)
return true;
response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
UA_NodeId sessionId = (session) ? session->sessionId : UA_NODEID_NULL;
UA_ServerConfig *config = &server->config;
static UA_THREAD_LOCAL UA_KeyValuePair notifyPayload[4] = {
{{0, UA_STRING_STATIC("securechannel-id")}, {0}},
{{0, UA_STRING_STATIC("session-id")}, {0}},
{{0, UA_STRING_STATIC("request-id")}, {0}},
{{0, UA_STRING_STATIC("service-type")}, {0}}
};
UA_KeyValueMap notifyPayloadMap = {4, notifyPayload};
if(config->globalNotificationCallback || config->serviceNotificationCallback) {
UA_Variant_setScalar(¬ifyPayload[0].value, &channel->securityToken.channelId,
&UA_TYPES[UA_TYPES_UINT32]);
UA_Variant_setScalar(¬ifyPayload[1].value, &sessionId,
&UA_TYPES[UA_TYPES_NODEID]);
UA_Variant_setScalar(¬ifyPayload[2].value, &requestId,
&UA_TYPES[UA_TYPES_UINT32]);
UA_Variant_setScalar(¬ifyPayload[3].value,
(void *)(uintptr_t)&sd->requestType->typeId,
&UA_TYPES[UA_TYPES_NODEID]);
}
UA_ApplicationNotificationType nt = UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_BEGIN;
if(config->serviceNotificationCallback)
config->serviceNotificationCallback(server, nt, notifyPayloadMap);
if(config->globalNotificationCallback)
config->globalNotificationCallback(server, nt, notifyPayloadMap);
UA_Boolean done = processServiceInternal(server, channel, session,
requestId, sd, request, response);
nt = (done) ? UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_END :
UA_APPLICATIONNOTIFICATIONTYPE_SERVICE_ASYNC;
if(config->serviceNotificationCallback)
config->serviceNotificationCallback(server, nt, notifyPayloadMap);
if(config->globalNotificationCallback)
config->globalNotificationCallback(server, nt, notifyPayloadMap);
#ifdef UA_ENABLE_DIAGNOSTICS
if(session) {
session->diagnostics.totalRequestCount.totalCount++;
if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
session->diagnostics.totalRequestCount.errorCount++;
if(sd->counterOffset != 0) {
UA_ServiceCounterDataType *serviceCounter = (UA_ServiceCounterDataType*)
(((uintptr_t)&session->diagnostics) + sd->counterOffset);
serviceCounter->totalCount++;
if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
serviceCounter->errorCount++;
}
}
#endif
return done;
}