#include "ua_session.h"
#include "open62541/types.h"
#include "ua_server_internal.h"
#ifdef UA_ENABLE_SUBSCRIPTIONS
#include "ua_subscription.h"
#endif
void UA_Session_init(UA_Session *session) {
memset(session, 0, sizeof(UA_Session));
TAILQ_INIT(&session->continuationPoints);
#ifdef UA_ENABLE_SUBSCRIPTIONS
SIMPLEQ_INIT(&session->responseQueue);
TAILQ_INIT(&session->subscriptions);
#endif
}
void UA_Session_clear(UA_Session *session, UA_Server* server) {
UA_LOCK_ASSERT(&server->serviceMutex);
#ifdef UA_ENABLE_SUBSCRIPTIONS
UA_Subscription *sub, *tempsub;
TAILQ_FOREACH_SAFE(sub, &session->subscriptions, sessionListEntry, tempsub) {
UA_Subscription_delete(server, sub);
}
#endif
#ifdef UA_ENABLE_DIAGNOSTICS
deleteNode(server, session->sessionId, true);
#endif
UA_Session_detachFromSecureChannel(server, session);
UA_ApplicationDescription_clear(&session->clientDescription);
UA_ByteString_clear(&session->clientCertificate);
UA_NodeId_clear(&session->authenticationToken);
UA_String_clear(&session->clientUserIdOfSession);
UA_NodeId_clear(&session->sessionId);
UA_String_clear(&session->sessionName);
UA_ByteString_clear(&session->serverNonce);
UA_ByteString_clear(&session->clientNonce);
ContinuationPointQueue_clear(&session->continuationPoints);
session->continuationPointsSize = 0;
UA_KeyValueMap_clear(&session->attributes);
UA_Array_delete(session->localeIds, session->localeIdsSize,
&UA_TYPES[UA_TYPES_STRING]);
session->localeIds = NULL;
session->localeIdsSize = 0;
#ifdef UA_ENABLE_DIAGNOSTICS
UA_SessionDiagnosticsDataType_clear(&session->diagnostics);
UA_SessionSecurityDiagnosticsDataType_clear(&session->securityDiagnostics);
#endif
if(session->sessionSp && session->sessionSpContext) {
session->sessionSp->
deleteChannelContext(session->sessionSp, session->sessionSpContext);
session->sessionSp = NULL;
session->sessionSpContext = NULL;
}
}
void
UA_Session_attachToSecureChannel(UA_Server *server, UA_Session *session,
UA_SecureChannel *channel) {
UA_Session_detachFromSecureChannel(server, session);
session->next = channel->sessions;
channel->sessions = session;
session->channel = channel;
}
void
UA_Session_detachFromSecureChannel(UA_Server *server, UA_Session *session) {
#ifdef UA_ENABLE_SUBSCRIPTIONS
UA_PublishResponseEntry *pre;
while((pre = UA_Session_dequeuePublishReq(session))) {
UA_PublishResponse_clear(&pre->response);
UA_free(pre);
}
#endif
UA_SecureChannel *channel = session->channel;
if(!channel)
return;
if(channel->sessions == session) {
channel->sessions = session->next;
} else {
UA_Session *elm = channel->sessions;
while(elm->next != session)
elm = elm->next;
elm->next = session->next;
}
session->channel = NULL;
notifySession(server, session,
UA_APPLICATIONNOTIFICATIONTYPE_SESSION_DEACTIVATED);
}
UA_StatusCode
UA_Session_generateNonce(UA_Session *session) {
UA_SecureChannel *channel = session->channel;
if(!channel || !channel->securityPolicy)
return UA_STATUSCODE_BADINTERNALERROR;
UA_SecurityPolicy *sp = channel->securityPolicy;
void *spC = channel->channelContext;
if(session->sessionSp && session->sessionSpContext) {
sp = session->sessionSp;
spC = session->sessionSpContext;
}
size_t nonceLength = 32;
if(session->serverNonce.length != nonceLength) {
UA_ByteString_clear(&session->serverNonce);
UA_StatusCode res =
UA_ByteString_allocBuffer(&session->serverNonce, nonceLength);
if(res != UA_STATUSCODE_GOOD)
return res;
}
session->serverNonce.data[0] = 0;
return sp->generateNonce(sp, spC, &session->serverNonce);
}
void
UA_Session_updateLifetime(UA_Session *session, UA_DateTime now,
UA_DateTime nowMonotonic) {
session->validTill = nowMonotonic +
(UA_DateTime)(session->timeout * UA_DATETIME_MSEC);
#ifdef UA_ENABLE_DIAGNOSTICS
session->diagnostics.clientLastContactTime = now;
#endif
}
#ifdef UA_ENABLE_SUBSCRIPTIONS
void
UA_Session_attachSubscription(UA_Session *session, UA_Subscription *sub) {
sub->session = session;
session->subscriptionsSize++;
session->totalRetransmissionQueueSize += sub->retransmissionQueueSize;
UA_Subscription *after = NULL;
TAILQ_FOREACH(after, &session->subscriptions, sessionListEntry) {
if(after->priority < sub->priority) {
TAILQ_INSERT_BEFORE(after, sub, sessionListEntry);
return;
}
}
TAILQ_INSERT_TAIL(&session->subscriptions, sub, sessionListEntry);
}
void
UA_Session_detachSubscription(UA_Server *server, UA_Session *session,
UA_Subscription *sub, UA_Boolean releasePublishResponses) {
sub->session = NULL;
TAILQ_REMOVE(&session->subscriptions, sub, sessionListEntry);
UA_assert(session->subscriptionsSize > 0);
session->subscriptionsSize--;
session->totalRetransmissionQueueSize -= sub->retransmissionQueueSize;
if(!releasePublishResponses || !TAILQ_EMPTY(&session->subscriptions))
return;
UA_PublishResponseEntry *pre;
while((pre = UA_Session_dequeuePublishReq(session))) {
UA_PublishResponse *response = &pre->response;
response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
sendResponse(server, session->channel, pre->requestId,
(UA_Response*)response, &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
UA_PublishResponse_clear(response);
UA_free(pre);
}
}
UA_Subscription *
UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) {
UA_Subscription *sub;
TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) {
if(sub->statusChange != UA_STATUSCODE_GOOD)
continue;
if(sub->subscriptionId == subscriptionId)
break;
}
return sub;
}
UA_Subscription *
getSubscriptionById(UA_Server *server, UA_UInt32 subscriptionId) {
UA_Subscription *sub;
LIST_FOREACH(sub, &server->subscriptions, serverListEntry) {
if(sub->statusChange != UA_STATUSCODE_GOOD)
continue;
if(sub->subscriptionId == subscriptionId)
break;
}
return sub;
}
UA_PublishResponseEntry*
UA_Session_dequeuePublishReq(UA_Session *session) {
UA_PublishResponseEntry *entry = SIMPLEQ_FIRST(&session->responseQueue);
if(entry) {
SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
session->responseQueueSize--;
}
return entry;
}
void
UA_Session_queuePublishReq(UA_Session *session, UA_PublishResponseEntry* entry,
UA_Boolean head) {
if(!head)
SIMPLEQ_INSERT_TAIL(&session->responseQueue, entry, listEntry);
else
SIMPLEQ_INSERT_HEAD(&session->responseQueue, entry, listEntry);
session->responseQueueSize++;
}
#endif
UA_StatusCode
UA_Server_closeSession(UA_Server *server, const UA_NodeId *sessionId) {
lockServer(server);
UA_Session *session = getSessionById(server, sessionId);
if(!session) {
unlockServer(server);
return UA_STATUSCODE_BADSESSIONIDINVALID;
}
UA_Session_remove(server, session, UA_SHUTDOWNREASON_CLOSE);
unlockServer(server);
return UA_STATUSCODE_GOOD;
}
#define UA_PROTECTEDATTRIBUTESSIZE 4
static const UA_QualifiedName protectedAttributes[UA_PROTECTEDATTRIBUTESSIZE] = {
{0, UA_STRING_STATIC("localeIds")},
{0, UA_STRING_STATIC("clientDescription")},
{0, UA_STRING_STATIC("sessionName")},
{0, UA_STRING_STATIC("clientUserId")}
};
static UA_Boolean
protectedAttribute(const UA_QualifiedName key) {
for(size_t i = 0; i < UA_PROTECTEDATTRIBUTESSIZE; i++) {
if(UA_QualifiedName_equal(&key, &protectedAttributes[i]))
return true;
}
return false;
}
UA_StatusCode
UA_Server_setSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
const UA_QualifiedName key, const UA_Variant *value) {
if(protectedAttribute(key))
return UA_STATUSCODE_BADNOTWRITABLE;
if(!sessionId)
return UA_STATUSCODE_BADINVALIDARGUMENT;
lockServer(server);
UA_Session *session = getSessionById(server, sessionId);
UA_StatusCode res = UA_STATUSCODE_BADSESSIONIDINVALID;
if(session)
res = UA_KeyValueMap_set(&session->attributes, key, value);
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_deleteSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
const UA_QualifiedName key) {
if(protectedAttribute(key))
return UA_STATUSCODE_BADNOTWRITABLE;
if(!sessionId)
return UA_STATUSCODE_BADINVALIDARGUMENT;
lockServer(server);
UA_Session *session = getSessionById(server, sessionId);
if(!session) {
unlockServer(server);
return UA_STATUSCODE_BADSESSIONIDINVALID;
}
UA_StatusCode res = UA_KeyValueMap_remove(&session->attributes, key);
unlockServer(server);
return res;
}
static UA_StatusCode
getSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
const UA_QualifiedName key, UA_Variant *outValue,
UA_Boolean copy) {
if(!outValue)
return UA_STATUSCODE_BADINTERNALERROR;
if(!sessionId)
return UA_STATUSCODE_BADINVALIDARGUMENT;
UA_Session *session = getSessionById(server, sessionId);
if(!session)
return UA_STATUSCODE_BADSESSIONIDINVALID;
const UA_Variant *attr;
UA_Variant localAttr;
if(UA_QualifiedName_equal(&key, &protectedAttributes[0])) {
UA_Variant_setArray(&localAttr, session->localeIds,
session->localeIdsSize, &UA_TYPES[UA_TYPES_STRING]);
attr = &localAttr;
} else if(UA_QualifiedName_equal(&key, &protectedAttributes[1])) {
UA_Variant_setScalar(&localAttr, &session->clientDescription,
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
attr = &localAttr;
} else if(UA_QualifiedName_equal(&key, &protectedAttributes[2])) {
UA_Variant_setScalar(&localAttr, &session->sessionName,
&UA_TYPES[UA_TYPES_STRING]);
attr = &localAttr;
} else if(UA_QualifiedName_equal(&key, &protectedAttributes[3])) {
UA_Variant_setScalar(&localAttr, &session->clientUserIdOfSession,
&UA_TYPES[UA_TYPES_STRING]);
attr = &localAttr;
} else {
attr = UA_KeyValueMap_get(&session->attributes, key);
if(!attr)
return UA_STATUSCODE_BADNOTFOUND;
}
if(copy)
return UA_Variant_copy(attr, outValue);
*outValue = *attr;
outValue->storageType = UA_VARIANT_DATA_NODELETE;
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
UA_Server_getSessionAttribute(UA_Server *server, const UA_NodeId *sessionId,
const UA_QualifiedName key, UA_Variant *outValue) {
lockServer(server);
UA_StatusCode res = getSessionAttribute(server, sessionId, key, outValue, false);
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_getSessionAttributeCopy(UA_Server *server, const UA_NodeId *sessionId,
const UA_QualifiedName key, UA_Variant *outValue) {
lockServer(server);
UA_StatusCode res = getSessionAttribute(server, sessionId, key, outValue, true);
unlockServer(server);
return res;
}
UA_StatusCode
UA_Server_getSessionAttribute_scalar(UA_Server *server,
const UA_NodeId *sessionId,
const UA_QualifiedName key,
const UA_DataType *type,
void *outValue) {
lockServer(server);
UA_Variant attr;
UA_StatusCode res = getSessionAttribute(server, sessionId, key, &attr, false);
if(res != UA_STATUSCODE_GOOD) {
unlockServer(server);
return res;
}
if(!UA_Variant_hasScalarType(&attr, type)) {
unlockServer(server);
return UA_STATUSCODE_BADNOTFOUND;
}
memcpy(outValue, attr.data, type->memSize);
unlockServer(server);
return UA_STATUSCODE_GOOD;
}