diff --git a/config/stack_config.h b/config/stack_config.h index 2559069c..36d0b246 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -170,7 +170,7 @@ /* allow application to set server identity (for MMS identity service) at runtime */ #define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1 -/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ +/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 /* overwrite default results for MMS identify service */ @@ -240,10 +240,24 @@ /* enable to configure MmsServer at runtime */ #define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1 +/* Define the default number of the maximum outstanding calls allowed by the caller (client) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5 + +/* Define the default number of the maximum outstanding calls allowed by the calling endpoint (server) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5 + /************************************************************************************ * Check configuration for consistency - DO NOT MODIFY THIS PART! ************************************************************************************/ +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING must be greater than 0!" +#endif + +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED must be greater than 0!" +#endif + #if (MMS_JOURNAL_SERVICE != 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1) diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index 437d87d9..c61f6e26 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -160,7 +160,7 @@ /* allow application to set server identity (for MMS identity service) at runtime */ #define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1 -/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ +/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 /* default results for MMS identify service */ @@ -229,10 +229,24 @@ /* enable to configure MmsServer at runtime */ #define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1 +/* Define the default number of the maximum outstanding calls allowed by the caller (client) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5 + +/* Define the default number of the maximum outstanding calls allowed by the calling endpoint (server) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5 + /************************************************************************************ * Check configuration for consistency - DO NOT MODIFY THIS PART! ************************************************************************************/ +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING must be greater than 0!" +#endif + +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED must be greater than 0!" +#endif + #if (MMS_JOURNAL_SERVICE != 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1) diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 16d79a56..38db124c 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -431,6 +431,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 IedConnection_getRequestTimeout(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_setMaxOutstandingCalls(IntPtr self, int calling, int called); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision); @@ -559,6 +562,12 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_installStateChangedHandler(IntPtr connection, InternalStateChangedHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_ignoreReadAccess(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool ignore); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_ignoreClientRequests(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool ignore); + /********************* * Async functions *********************/ @@ -815,6 +824,16 @@ namespace IEC61850 } } + /// + /// Set the maximum number outstanding calls allowed for this connection + /// + /// the maximum outstanding calls allowed by the caller (client) + /// the maximum outstanding calls allowed by the called endpoint (server) + public void SetMaxOutstandingCalls(int calling, int called) + { + IedConnection_setMaxOutstandingCalls(connection, calling, called); + } + /// /// Gets or sets the maximum size if a PDU (has to be set before calling connect!). /// @@ -1175,6 +1194,9 @@ namespace IEC61850 private static List WrapNativeLogQueryResult(IntPtr linkedList) { + if (linkedList == IntPtr.Zero) + return null; + List journalEntries = new List(); IntPtr element = LinkedList_getNext(linkedList); @@ -1945,6 +1967,24 @@ namespace IEC61850 } } + /// + /// Ignore all MMS requests from clients (for testing purposes) + /// + /// when true all requests from clients will be ignored + public void IgnoreClientRequests(bool ignore) + { + IedServer_ignoreClientRequests(connection, ignore); + } + + /// + /// Temporarily ignore read requests (for testing purposes) + /// + /// true to ignore read requests, false to handle read requests. + public void IgnoreReadAccess(bool ignore) + { + IedServer_ignoreReadAccess(connection, ignore); + } + /// /// Read the values of a data set (GetDataSetValues service). /// @@ -2238,22 +2278,27 @@ namespace IEC61850 GetDataSetDirectoryHandler handler = callbackInfo.Item1; object handlerParameter = callbackInfo.Item2; - IntPtr element = LinkedList_getNext(dataSetDirectory); - handle.Free(); - List newList = new List(); + List newList = null; - while (element != IntPtr.Zero) + if (dataSetDirectory != IntPtr.Zero) { - string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element)); + newList = new List(); - newList.Add(dataObject); + IntPtr element = LinkedList_getNext(dataSetDirectory); - element = LinkedList_getNext(element); - } + while (element != IntPtr.Zero) + { + string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element)); - LinkedList_destroy(dataSetDirectory); + newList.Add(dataObject); + + element = LinkedList_getNext(element); + } + + LinkedList_destroy(dataSetDirectory); + } handler.Invoke(invokeId, handlerParameter, (IedClientError)err, newList, isDeletable); } @@ -2428,11 +2473,9 @@ namespace IEC61850 dataSet = new DataSet(nativeDataSet); } - handler(invokeId, handlerParameter, clientError, dataSet); } - public delegate void ReadDataSetHandler(UInt32 invokeId,object parameter,IedClientError err,DataSet dataSet); /// @@ -2566,7 +2609,6 @@ namespace IEC61850 { handler(invokeId, handlerParameter, clientError, null, moreFollows); } - } /// @@ -2632,7 +2674,6 @@ namespace IEC61850 return GetLogicalDeviceDataSetsAsync(null, ldName, continueAfter, handler, parameter); } - public UInt32 GetLogicalDeviceDataSetsAsync(List result, string ldName, string continueAfter, GetNameListHandler handler, object parameter) { int error; @@ -2977,6 +3018,19 @@ namespace IEC61850 IED_STATE_CLOSING = 3 } + public static class IedClientErrorExtension + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedClientError_toString(int err); + + public static string ToString(this IedClientError err) + { + string stringVal = Marshal.PtrToStringAnsi(IedClientError_toString((int)err)); + + return stringVal; + } + } + /// /// Error codes for client side functions /// @@ -3062,6 +3116,9 @@ namespace IEC61850 /** Received an invalid response message from the server */ IED_ERROR_MALFORMED_MESSAGE = 34, + /** Service was not executed because required resource is still in use */ + IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35, + /** Service not implemented */ IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98, diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs index 818276c4..980d9ceb 100644 --- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850CommonAPI.cs * - * Copyright 2014-2017 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -454,6 +454,9 @@ namespace IEC61850 SetByMmsUtcTime (mmsUtcTime); } + /// + /// Initializes a new instance of the class. + /// public Timestamp() { self = Timestamp_create (); @@ -461,6 +464,19 @@ namespace IEC61850 responsibleForDeletion = true; } + /// + /// Initializes a new instance of the class. + /// + public Timestamp(Timestamp other) : this() + { + SetTimeInSeconds (other.GetTimeInSeconds ()); + SetSubsecondPrecision (other.GetSubsecondPrecision ()); + SetFractionOfSecondPart (other.GetFractionOfSecondPart ()); + SetLeapSecondKnow(other.IsLeapSecondKnown()); + SetClockFailure(other.HasClockFailure()); + SetClockNotSynchronized(other.IsClockNotSynchronized()); + } + public Timestamp(byte[] value) { self = Timestamp_createFromByteArray (value); diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index b0d2c054..8a0c5bf3 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1874,6 +1874,9 @@ namespace IEC61850 [return: MarshalAs(UnmanagedType.I1)] static extern bool ControlAction_getInterlockCheck(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ControlAction_getT(IntPtr self); + private IntPtr self; private IedServer.ControlHandlerInfo info; private IedServer iedServer; @@ -2003,6 +2006,18 @@ namespace IEC61850 { return ControlAction_getInterlockCheck(self); } + + /// + /// Gets the time (paramter T) of the control action + /// + public Timestamp GetT() + { + IntPtr tPtr = ControlAction_getT(self); + + Timestamp t = new Timestamp(tPtr, false); + + return new Timestamp(t); + } } public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); @@ -2247,6 +2262,10 @@ namespace IEC61850 static extern void IedServer_handleWriteAccessForComplexAttribute(IntPtr self, IntPtr dataAttribute, InternalWriteAccessHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_handleWriteAccessForDataObject(IntPtr self, IntPtr dataObject, int fc, + InternalWriteAccessHandler handler, IntPtr parameter); + public delegate void ConnectionIndicationHandler(IedServer iedServer, ClientConnection clientConnection, bool connected, object parameter); private ConnectionIndicationHandler connectionHandler = null; @@ -2713,12 +2732,27 @@ namespace IEC61850 } } + private void AddHandlerInfoForDataObjectRecursive(DataObject dataObject, FunctionalConstraint fc, WriteAccessHandler handler, object parameter, InternalWriteAccessHandler internalHandler) + { + foreach (ModelNode child in dataObject.GetChildren()) + { + if (child is DataAttribute && (child as DataAttribute).FC == fc) + { + AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter, internalHandler); + } + else if (child is DataObject) + { + AddHandlerInfoForDataObjectRecursive(child as DataObject, fc, handler, parameter, internalHandler); + } + } + } + /// /// Install a WriteAccessHandler for a data attribute and for all sub data attributes /// /// This instructs the server to monitor write attempts by MMS clients to specific - /// data attributes.If a client tries to write to the monitored data attribute the - /// handler is invoked.The handler can decide if the write access will be allowed + /// data attributes. If a client tries to write to the monitored data attribute the + /// handler is invoked. The handler can decide if the write access will be allowed /// or denied.If a WriteAccessHandler is set for a specific data attribute - the /// default write access policy will not be performed for that data attribute. /// @@ -2738,6 +2772,27 @@ namespace IEC61850 IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, internalHandler, IntPtr.Zero); } + /// + /// Install a WriteAccessHandler for a data object and for all sub data objects and sub data attributes that have the same functional constraint + /// + /// This instructs the server to monitor write attempts by MMS clients to specific + /// data attributes. If a client tries to write to the monitored data attribute the + /// handler is invoked. The handler can decide if the write access will be allowed + /// or denied. If a WriteAccessHandler is set the + /// default write access policy will not be performed for the matching data attributes. + /// the data object to monitor + /// the functional constraint (FC) to monitor + /// the callback function that is invoked if a client tries to write to a monitored data attribute that is a child of the data object. + /// a user provided parameter that is passed to the WriteAccessHandler when called. + public void HandleWriteAccessForDataObject(DataObject dataObj, FunctionalConstraint fc, WriteAccessHandler handler, object parameter) + { + InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl); + + AddHandlerInfoForDataObjectRecursive(dataObj, fc, handler, parameter, internalHandler); + + IedServer_handleWriteAccessForDataObject(self, dataObj.self, (int)fc, internalHandler, IntPtr.Zero); + } + /// /// Set the defualt write access policy for a specific FC. The default policy is applied when no handler is installed for a data attribute /// diff --git a/dotnet/example2/WriteValueExample.cs b/dotnet/example2/WriteValueExample.cs index eb8b0228..b3ea3c04 100644 --- a/dotnet/example2/WriteValueExample.cs +++ b/dotnet/example2/WriteValueExample.cs @@ -36,7 +36,7 @@ namespace example2 } catch (IedConnectionException e) { - Console.WriteLine("IED connection excepion: " + e.Message); + Console.WriteLine("IED connection exception: " + e.Message + " err: " + e.GetIedClientError().ToString()); } // release all resources - do NOT use the object after this call!! diff --git a/examples/iec61850_9_2_LE_example/sv.icd b/examples/iec61850_9_2_LE_example/sv.icd index b52f8d92..af5fe962 100644 --- a/examples/iec61850_9_2_LE_example/sv.icd +++ b/examples/iec61850_9_2_LE_example/sv.icd @@ -93,7 +93,7 @@ SCL.xsd"> + smvID="xxxxMUnn01" smpRate="80" nofASDU="1" confRev="1" smpMod="SmpPerPeriod"> diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index e0b789af..6b13ca7b 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -23,6 +23,38 @@ sigint_handler(int signalId) running = 0; } +static const char* +ACSIClassToStr(ACSIClass acsiClass) +{ + switch (acsiClass) + { + case ACSI_CLASS_BRCB: + return "BRCB"; + case ACSI_CLASS_URCB: + return "URCB"; + case ACSI_CLASS_GoCB: + return "GoCB"; + case ACSI_CLASS_SGCB: + return "SGCB"; + case ACSI_CLASS_LCB: + return "LCB"; + case ACSI_CLASS_GsCB: + return "GsCB"; + case ACSI_CLASS_LOG: + return "log"; + case ACSI_CLASS_DATA_SET: + return "dataset"; + case ACSI_CLASS_DATA_OBJECT: + return "data-object"; + case ACSI_CLASS_MSVCB: + return "MSVCB"; + case ACSI_CLASS_USVCB: + return "USVCB"; + default: + return "unknown"; + } +} + static ControlHandlerResult controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test) { @@ -65,7 +97,6 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v return CONTROL_RESULT_OK; } - static void connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) { @@ -79,43 +110,41 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, * This handler is called before the rcbEventHandler and can be use to allow or permit read or write access to the RCB */ static bool -rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation) +controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType) { - printf("RCB: %s access: %s\n", ReportControlBlock_getName(rcb), operation == RCB_EVENT_GET_PARAMETER ? "READ" : "WRITE"); + printf("%s %s access %s/%s.%s.%s\n", ACSIClassToStr(acsiClass), accessType == IEC61850_CB_ACCESS_TYPE_WRITE ? "write" : "read", ld->name, ln->name, objectName, subObjectName); - if (operation == RCB_EVENT_GET_PARAMETER) { - return true; + /* allow only read access to LCBs */ + if (acsiClass == ACSI_CLASS_LCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; } - else { - /* change to false to disallow write access to control block */ - return true; + + /* allow only read access to BRCBs */ + if (acsiClass == ACSI_CLASS_BRCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; } -} - -static bool -lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation) -{ - printf("LCB: %s access: %s\n", LogControlBlock_getName(lcb), operation == LCB_EVENT_GET_PARAMETER ? "READ" : "WRITE"); - if (operation == LCB_EVENT_GET_PARAMETER) { - return true; - } - else { - return false; - } + /* to all other control blocks allow read and write access */ + return true; } static void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) { - printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); - if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) { + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); printf(" param: %s\n", parameterName); printf(" result: %i\n", serviceError); } if (event == RCB_EVENT_ENABLE) { + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); char* rptId = ReportControlBlock_getRptID(rcb); printf(" rptID: %s\n", rptId); char* dataSet = ReportControlBlock_getDataSet(rcb); @@ -137,15 +166,39 @@ dataSetAccessHandler(void* parameter, ClientConnection connection, IedServer_Dat static MmsDataAccessError readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter) { - printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name); + printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject ? dataObject->name : "-"); - if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) { - return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + if (dataObject == NULL) { + if (!strcmp(ln->name, "GGIO1")) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } } return DATA_ACCESS_ERROR_SUCCESS; } +static bool +listObjectsAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc) +{ + if (subObjectName) + printf("list objects access[2] to %s/%s.%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, subObjectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + else + printf("list objects access[2] to %s/%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + + // if (acsiClass == ACSI_CLASS_BRCB) { + // return true; + // } + + // return false; + + return true; +} + static bool directoryAccessHandler(void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice) { @@ -231,15 +284,12 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); - /* Install handler to perform access control on RCB */ - IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL); - - /* Install handler to perform access control on LCB */ - IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); - /* Install handler to log RCB events */ IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); + /* Install handler to control access to control blocks (RCBs, LCBs, GoCBs, SVCBs, SGCBs)*/ + IedServer_setControlBlockAccessHandler(iedServer, controlBlockAccessHandler, NULL); + /* By default access to variables with FC=DC and FC=CF is not allowed. * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used * by iec61850_client_example1. @@ -258,6 +308,9 @@ main(int argc, char** argv) IedServer_setDirectoryAccessHandler(iedServer, directoryAccessHandler, NULL); + /* control visibility of data objects in directory (get-name-list) and variable description (get-variable-access-attributes) services */ + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); + /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, tcpPort); diff --git a/examples/server_example_access_control/static_model.c b/examples/server_example_access_control/static_model.c index 26f5f4fc..f38f886e 100644 --- a/examples/server_example_access_control/static_model.c +++ b/examples/server_example_access_control/static_model.c @@ -1,7 +1,7 @@ /* * static_model.c * - * automatically generated from simpleIO_direct_control.cid + * automatically generated from ../../examples/server_example_basic_io/simpleIO_direct_control.cid */ #include "static_model.h" @@ -2169,7 +2169,15 @@ ReportControlBlock iedModel_GenericIO_LLN0_report9 = {&iedModel_GenericIO_LLN0, +extern LogControlBlock iedModel_GenericIO_LLN0_lcb0; +extern LogControlBlock iedModel_GenericIO_LLN0_lcb1; +LogControlBlock iedModel_GenericIO_LLN0_lcb0 = {&iedModel_GenericIO_LLN0, "EventLog", "Events", "GenericIO/LLN0$EventLog", 3, 0, true, true, &iedModel_GenericIO_LLN0_lcb1}; +LogControlBlock iedModel_GenericIO_LLN0_lcb1 = {&iedModel_GenericIO_LLN0, "GeneralLog", NULL, NULL, 3, 0, true, true, NULL}; +extern Log iedModel_GenericIO_LLN0_log0; +extern Log iedModel_GenericIO_LLN0_log1; +Log iedModel_GenericIO_LLN0_log0 = {&iedModel_GenericIO_LLN0, "GeneralLog", &iedModel_GenericIO_LLN0_log1}; +Log iedModel_GenericIO_LLN0_log1 = {&iedModel_GenericIO_LLN0, "EventLog", NULL}; IedModel iedModel = { @@ -2180,8 +2188,8 @@ IedModel iedModel = { NULL, NULL, NULL, - NULL, - NULL, + &iedModel_GenericIO_LLN0_lcb0, + &iedModel_GenericIO_LLN0_log0, initializeValues }; diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c index ee388be8..e086d52a 100644 --- a/examples/server_example_logging/server_example_logging.c +++ b/examples/server_example_logging/server_example_logging.c @@ -115,26 +115,62 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in return true; } -static bool -lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation) +static const char* +ACSIClassToStr(ACSIClass acsiClass) { - printf("%s access to LCB %s from %s\n", operation == LCB_EVENT_GET_PARAMETER ? "read" : "write", LogControlBlock_getName(lcb), ClientConnection_getPeerAddress(connection)); - - /* only allow read access */ - if (operation == LCB_EVENT_GET_PARAMETER) { - return true; + switch (acsiClass) + { + case ACSI_CLASS_BRCB: + return "BRCB"; + case ACSI_CLASS_URCB: + return "URCB"; + case ACSI_CLASS_GoCB: + return "GoCB"; + case ACSI_CLASS_SGCB: + return "SGCB"; + case ACSI_CLASS_LCB: + return "LCB"; + case ACSI_CLASS_GsCB: + return "GsCB"; + case ACSI_CLASS_LOG: + return "log"; + case ACSI_CLASS_DATA_SET: + return "dataset"; + case ACSI_CLASS_DATA_OBJECT: + return "data-object"; + case ACSI_CLASS_MSVCB: + return "MSVCB"; + case ACSI_CLASS_USVCB: + return "USVCB"; + default: + return "unknown"; } - else { - return false; +} + +bool +controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType) +{ + printf("Access to %s %s/%s.%s\n", ACSIClassToStr(acsiClass), ld->name, ln ? ln->name : "-", objectName); + + if (acsiClass == ACSI_CLASS_LCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; } + + return true; } static bool -logAccessHandler(void* parameter, const char* logName, ClientConnection connection) +listObjectsAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc) { - printf("Access to log %s from %s\n", logName, ClientConnection_getPeerAddress(connection)); + if (subObjectName) + printf("list objects access[2] to %s/%s.%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, subObjectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + else + printf("list objects access[2] to %s/%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); - return false; + return true; } int @@ -169,9 +205,7 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); - IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); - - IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL); + IedServer_setControlBlockAccessHandler(iedServer, controlBlockAccessHandler, NULL); LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); @@ -204,6 +238,7 @@ main(int argc, char** argv) LogStorage_getEntries(statusLog, 0, Hal_getTimeInMs(), entryCallback, (LogEntryDataCallback) entryDataCallback, NULL); #endif + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, tcpPort); diff --git a/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c index eaf1897a..bb989600 100644 --- a/hal/ethernet/linux/ethernet_linux.c +++ b/hal/ethernet/linux/ethernet_linux.c @@ -1,7 +1,7 @@ /* * ethernet_linux.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -57,7 +57,8 @@ EthernetHandleSet_new(void) { EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet)); - if (result != NULL) { + if (result != NULL) + { result->handles = NULL; result->nhandles = 0; } @@ -68,8 +69,8 @@ EthernetHandleSet_new(void) void EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) { - if (self != NULL && sock != NULL) { - + if (self != NULL && sock != NULL) + { int i = self->nhandles++; self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd)); @@ -82,12 +83,14 @@ EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) void EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock) { - if ((self != NULL) && (sock != NULL)) { - + if ((self != NULL) && (sock != NULL)) + { int i; - for (i = 0; i < self->nhandles; i++) { - if (self->handles[i].fd == sock->rawSocket) { + for (i = 0; i < self->nhandles; i++) + { + if (self->handles[i].fd == sock->rawSocket) + { memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1)); self->nhandles--; return; @@ -128,7 +131,8 @@ getInterfaceIndex(int sock, const char* deviceName) strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1); - if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) { + if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) + { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Failed to get interface index"); return -1; @@ -157,7 +161,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr) int i; - for(i = 0; i < 6; i++ ) + for (i = 0; i < 6; i++ ) { addr[i] = (unsigned char)buffer.ifr_hwaddr.sa_data[i]; } @@ -166,106 +170,112 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr) EthernetSocket Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress) { - EthernetSocket ethernetSocket = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket)); + EthernetSocket self = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket)); - if (ethernetSocket) { - ethernetSocket->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (self) + { + self->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (ethernetSocket->rawSocket == -1) { + if (self->rawSocket == -1) + { if (DEBUG_SOCKET) printf("Error creating raw socket!\n"); - GLOBAL_FREEMEM(ethernetSocket); + GLOBAL_FREEMEM(self); return NULL; } - ethernetSocket->socketAddress.sll_family = PF_PACKET; - ethernetSocket->socketAddress.sll_protocol = htons(ETH_P_ALL); + self->socketAddress.sll_family = PF_PACKET; + self->socketAddress.sll_protocol = htons(ETH_P_ALL); - int ifcIdx = getInterfaceIndex(ethernetSocket->rawSocket, interfaceId); + int ifcIdx = getInterfaceIndex(self->rawSocket, interfaceId); - if (ifcIdx == -1) { - Ethernet_destroySocket(ethernetSocket); + if (ifcIdx == -1) + { + Ethernet_destroySocket(self); return NULL; } - ethernetSocket->socketAddress.sll_ifindex = ifcIdx; + self->socketAddress.sll_ifindex = ifcIdx; - ethernetSocket->socketAddress.sll_hatype = ARPHRD_ETHER; - ethernetSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; + self->socketAddress.sll_hatype = ARPHRD_ETHER; + self->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; - ethernetSocket->socketAddress.sll_halen = ETH_ALEN; + self->socketAddress.sll_halen = ETH_ALEN; - memset(ethernetSocket->socketAddress.sll_addr, 0, 8); + memset(self->socketAddress.sll_addr, 0, 8); if (destAddress != NULL) - memcpy(ethernetSocket->socketAddress.sll_addr, destAddress, 6); + memcpy(self->socketAddress.sll_addr, destAddress, 6); + + self->isBind = false; - ethernetSocket->isBind = false; + Ethernet_setMode(self, ETHERNET_SOCKET_MODE_PROMISC); } - return ethernetSocket; + return self; } void -Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode) +Ethernet_setMode(EthernetSocket self, EthernetSocketMode mode) { - if (ethSocket) { - - if (mode == ETHERNET_SOCKET_MODE_PROMISC) { - + if (self) + { + if (mode == ETHERNET_SOCKET_MODE_PROMISC) + { struct ifreq ifr; - if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCGIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Problem getting device flags"); return; } - ifr.ifr_flags |= IFF_PROMISC; - if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCSIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Setting device to promiscuous mode failed"); return; } } - else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST) { + else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST) + { struct ifreq ifr; - if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCGIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Problem getting device flags"); return; } - ifr.ifr_flags |= IFF_ALLMULTI; - if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCSIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Setting device to promiscuous mode failed"); return; } } - else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY) { - ethSocket->socketAddress.sll_pkttype = PACKET_HOST; + else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY) + { + self->socketAddress.sll_pkttype = PACKET_HOST; } - else if (mode == ETHERNET_SOCKET_MODE_MULTICAST) { - ethSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; + else if (mode == ETHERNET_SOCKET_MODE_MULTICAST) + { + self->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; } } } void -Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress) +Ethernet_addMulticastAddress(EthernetSocket self, uint8_t* multicastAddress) { struct packet_mreq mreq; memset(&mreq, 0, sizeof(struct packet_mreq)); - mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex; + mreq.mr_ifindex = self->socketAddress.sll_ifindex; mreq.mr_alen = ETH_ALEN; mreq.mr_type = PACKET_MR_MULTICAST; mreq.mr_address[0] = multicastAddress[0]; @@ -275,17 +285,17 @@ Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress mreq.mr_address[4] = multicastAddress[4]; mreq.mr_address[5] = multicastAddress[5]; - int res = setsockopt(ethSocket->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + int res = setsockopt(self->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); - - if (res != 0) { + if (res != 0) + { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Setting multicast address failed"); } } void -Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) +Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType) { if (etherType == 0x88b8) { @@ -303,22 +313,22 @@ Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) fprog.len = sizeof(filter) / sizeof(*filter); fprog.filter = filter; - if (setsockopt(ethSocket->rawSocket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1) + if (setsockopt(self->rawSocket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1) if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Applying filter failed"); } else { - ethSocket->socketAddress.sll_protocol = htons(etherType); + self->socketAddress.sll_protocol = htons(etherType); } } - /* non-blocking receive */ int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize) { - if (self->isBind == false) { + if (self->isBind == false) + { if (bind(self->rawSocket, (struct sockaddr*) &self->socketAddress, sizeof(self->socketAddress)) == 0) self->isBind = true; else @@ -329,17 +339,20 @@ Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize) } void -Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize) +Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize) { - sendto(ethSocket->rawSocket, buffer, packetSize, - 0, (struct sockaddr*) &(ethSocket->socketAddress), sizeof(ethSocket->socketAddress)); + sendto(self->rawSocket, buffer, packetSize, + 0, (struct sockaddr*) &(self->socketAddress), sizeof(self->socketAddress)); } void -Ethernet_destroySocket(EthernetSocket ethSocket) +Ethernet_destroySocket(EthernetSocket self) { - close(ethSocket->rawSocket); - GLOBAL_FREEMEM(ethSocket); + if (self) + { + close(self->rawSocket); + GLOBAL_FREEMEM(self); + } } bool @@ -347,4 +360,3 @@ Ethernet_isSupported() { return true; } - diff --git a/hal/filesystem/linux/file_provider_linux.c b/hal/filesystem/linux/file_provider_linux.c index 7b501f00..d37a7db1 100644 --- a/hal/filesystem/linux/file_provider_linux.c +++ b/hal/filesystem/linux/file_provider_linux.c @@ -53,13 +53,13 @@ FileSystem_openFile(char* fileName, bool readWrite) int FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize) { - return fread(buffer, maxSize, 1, (FILE*) handle); + return fread(buffer, 1, maxSize, (FILE*) handle); } int FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size) { - return fwrite(buffer, size, 1, (FILE*) handle); + return fwrite(buffer, 1, size, (FILE*) handle); } void diff --git a/hal/filesystem/win32/file_provider_win32.c b/hal/filesystem/win32/file_provider_win32.c index 1a08e8d1..b51a1a45 100644 --- a/hal/filesystem/win32/file_provider_win32.c +++ b/hal/filesystem/win32/file_provider_win32.c @@ -62,13 +62,13 @@ FileSystem_openFile(char* fileName, bool readWrite) int FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize) { - return fread(buffer, maxSize, 1, (FILE*) handle); + return fread(buffer, 1, maxSize, (FILE*) handle); } int FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size) { - return fwrite(buffer, size, 1, (FILE*) handle); + return fwrite(buffer, 1, size, (FILE*) handle); } void diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index c188c24f..fffc72ae 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -816,6 +816,7 @@ int UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize) { struct sockaddr_storage remoteAddress; + memset(&remoteAddress, 0, sizeof(struct sockaddr_storage)); socklen_t structSize = sizeof(struct sockaddr_storage); int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, (struct sockaddr*)&remoteAddress, &structSize); diff --git a/hal/socket/win32/socket_win32.c b/hal/socket/win32/socket_win32.c index 65be99d7..5b8c448f 100644 --- a/hal/socket/win32/socket_win32.c +++ b/hal/socket/win32/socket_win32.c @@ -749,6 +749,7 @@ int UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize) { struct sockaddr_storage remoteAddress; + memset(&remoteAddress, 0, sizeof(struct sockaddr_storage)); socklen_t structSize = sizeof(struct sockaddr_storage); int result = recvfrom(self->fd, (char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, &structSize); diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index e2f152d0..06b98234 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -37,9 +37,9 @@ #endif #if (CONFIG_DEBUG_TLS == 1) -#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__); +#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__) #else -#define DEBUG_PRINT(fmt, ...) {do {} while(0);} +#define DEBUG_PRINT(fmt, ...) do {} while(0) #endif @@ -265,7 +265,7 @@ TLSConfiguration_create() mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED); - mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION); + mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED); /* static int hashes[] = {3,4,5,6,7,8,0}; */ /* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */ @@ -297,7 +297,7 @@ TLSConfiguration_create() void TLSConfiguration_setClientMode(TLSConfiguration self) { - self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT; + mbedtls_ssl_conf_endpoint(&(self->conf), MBEDTLS_SSL_IS_CLIENT); } void @@ -852,20 +852,26 @@ TLSSocket_performHandshake(TLSSocket self) { int ret = mbedtls_ssl_renegotiate(&(self->ssl)); - if (ret == 0) { + if (ret == 0 || ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE || + ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS || ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) { if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self); } + + DEBUG_PRINT("TLS", "TLSSocket_performHandshake Success -> ret=%i\n", ret); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "TLS session renegotiation completed", self); return true; } else { DEBUG_PRINT("TLS", "TLSSocket_performHandshake failed -> ret=%i\n", ret); - if (self->tlsConfig->eventHandler) { - uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Alarm: TLS session renegotiation failed", self); - createSecurityEvents(self->tlsConfig, ret, flags, self); + /* mbedtls_ssl_renegotiate mandates to reset the ssl session in case of errors */ + ret = mbedtls_ssl_session_reset(&(self->ssl)); + if (ret != 0) { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -> ret: -0x%X\n", -ret); } return false; @@ -898,16 +904,10 @@ startRenegotiationIfRequired(TLSSocket self) if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) return true; - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self); - - if (TLSSocket_performHandshake(self) == false) { - DEBUG_PRINT("TLS", " renegotiation failed\n"); + if (TLSSocket_performHandshake(self) == false) return false; - } - DEBUG_PRINT("TLS", " started renegotiation\n"); self->lastRenegotiationTime = Hal_getTimeInMs(); - return true; } @@ -920,22 +920,30 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size) return -1; } - int ret = mbedtls_ssl_read(&(self->ssl), buf, size); - - if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) - return 0; + int len = 0; + while (len < size) { + int ret = mbedtls_ssl_read(&(self->ssl), (buf + len), (size - len)); + if (ret > 0) { + len += ret; + continue; + } - if (ret < 0) { + switch (ret) { + case 0: // falling through + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + // Known "good" cases indicating the read is done + return len; - switch (ret) - { case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: DEBUG_PRINT("TLS", " connection was closed gracefully\n"); - return -1; + break; case MBEDTLS_ERR_NET_CONN_RESET: DEBUG_PRINT("TLS", " connection was reset by peer\n"); - return -1; + break; default: DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret); @@ -945,19 +953,23 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size) createSecurityEvents(self->tlsConfig, ret, flags, self); } + } - return -1; + int reset_err = mbedtls_ssl_session_reset(&(self->ssl)); + if (0 != reset_err) { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -reset_err); } + + return ret; } - return ret; + return len; } int TLSSocket_write(TLSSocket self, uint8_t* buf, int size) { - int ret; - int len = size; + int len = 0; checkForCRLUpdate(self); @@ -965,22 +977,26 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size) return -1; } - while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0) + while (len < size) { - if (ret == MBEDTLS_ERR_NET_CONN_RESET) - { - DEBUG_PRINT("TLS", "peer closed the connection\n"); - return -1; + int ret = mbedtls_ssl_write(&(self->ssl), (buf + len), (size -len)); + if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE) || + (ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) || (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)) { + continue; } - if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) - { - DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret); + if (ret < 0) { + DEBUG_PRINT("TLS", "mbedtls_ssl_write returned -0x%X\n", -ret); + + if (0 != (ret = mbedtls_ssl_session_reset(&(self->ssl)))) { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -ret); + } + return -1; } - } - len = ret; + len += ret; + } return len; } @@ -1008,6 +1024,7 @@ TLSSocket_close(TLSSocket self) if (self->peerCert) GLOBAL_FREEMEM(self->peerCert); + GLOBAL_FREEMEM(self); } diff --git a/pyiec61850/CMakeLists.txt b/pyiec61850/CMakeLists.txt index 159ef4bc..a42960f9 100644 --- a/pyiec61850/CMakeLists.txt +++ b/pyiec61850/CMakeLists.txt @@ -1,18 +1,22 @@ -# The SWIG functions/macros used in this module, swig_add_module and swig_add_library -# are not available in CMake versions earlier than 3.8 -# cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.12) + +cmake_policy(SET CMP0078 NEW) + +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") + cmake_policy(SET CMP0086 NEW) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -find_package(PythonInterp ${BUILD_PYTHON_VERSION} REQUIRED) -find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT REQUIRED) +find_package(Python COMPONENTS Interpreter Development REQUIRED) -include_directories(${PYTHON_INCLUDE_PATH}) +include_directories(${Python_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_SWIG_FLAGS "") set_property(SOURCE iec61850.i PROPERTY CPLUSPLUS ON) +set_property(SOURCE iec61850.i PROPERTY SWIG_MODULE_NAME pyiec61850) if(WIN32) set(LIBS iec61850 ws2_32) @@ -20,25 +24,21 @@ else() set(LIBS iec61850-shared) endif() -if(${CMAKE_VERSION} VERSION_LESS 3.8) - swig_add_module(iec61850 python iec61850.i) -else() - swig_add_library(iec61850 - LANGUAGE python - SOURCES iec61850.i - ) -endif() +swig_add_library(pyiec61850 + LANGUAGE python + SOURCES iec61850.i +) -swig_link_libraries(iec61850 ${PYTHON_LIBRARIES} ${LIBS}) +swig_link_libraries(pyiec61850 ${LIBS}) # Finding python modules install path execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c - "from distutils.sysconfig import get_python_lib; import sys; sys.stdout.write(get_python_lib())" + COMMAND ${Python_EXECUTABLE} -c + "from sysconfig import get_path; import sys; sys.stdout.write(get_path('platlib'))" OUTPUT_VARIABLE PYTHON_SITE_DIR ) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR}) -install(TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyiec61850.py DESTINATION ${PYTHON_SITE_DIR}) +install(TARGETS pyiec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) -add_test(test_pyiec61850 ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py) +add_test(test_pyiec61850 ${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py) diff --git a/pyiec61850/README.md b/pyiec61850/README.md new file mode 100644 index 00000000..b2398919 --- /dev/null +++ b/pyiec61850/README.md @@ -0,0 +1,95 @@ +# Python wrapper for libIEC61850 + +## Building +Before building you should install SWIG and Python +(see the '[Setup development environment on Linux](#setup-development-environment-on-linux-ubuntu)' section for help). + +To build the Python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line: +```sh +$ mkdir build && cd build +$ cmake -DBUILD_PYTHON_BINDINGS=ON .. +``` + +Then compile the library and install it: +```sh +$ make +$ sudo make install +``` +(Eventually, update your ld cache with: `sudo ldconfig`) + +CMake and SWIG will automatically detect your Python version and install the Python library in Python library directories. + +For running the integrated tests: +```sh +$ make test +``` + +pyiec61850 library is to be imported calling: +```python +import pyiec61850 as iec61850 +``` + +## Client tutorial + +The Python bindings works similarly to the basic C library. However there are some differences: + +* a specific function is to be called to cast variables from one type to another +* arguments passed by pointer are to be removed from arguments and append to the return list + + +For example to create a connection, call: +```python + con = iec61850.IedConnection_create() + error = iec61850.IedConnection_connect(con, "localhost", 102) + if (error == iec61850.IED_ERROR_OK): + # Do some work + iec61850.IedConnection_close(con) + iec61850.IedConnection_destroy(con) +``` + +To iterate over a list of logical devices, the code becomes: +```python +[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con) +device = iec61850.LinkedList_getNext(deviceList) + +while device: + print("LD: %s" % iec61850.toCharP(device.data)) + [logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory( + con, iec61850.toCharP(device.data)) + device = iec61850.LinkedList_getNext(device) +iec61850.LinkedList_destroy(deviceList) +``` + +Reading and writing operations can be performed using this syntax: +```python +[floatValue, error] = iec61850.IedConnection_readFloatValue(con, + "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX) +err = iec61850.IedConnection_writeFloatValue(con, + "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0) +``` + +## Appendix + +## Setup development environment on Linux Ubuntu + +_[Tested on Ubuntu 20.04 LTS, Ubuntu 22.04 LTS, Ubuntu 23.10]_ + +Here the minimum required packages for compiling libiec61850 and the Python +wrapper (without TLS, SQlite, ...): + +```sh +$ sudo apt-get update +$ sudo apt-get install g++ cmake swig git python3 python3-all-dev +``` + +## Setup development environment on Linux Alpine + +_[Tested on Alpine 3.18]_ + +Here the minimum required packages for compiling libiec61850 and the Python +wrapper (without TLS, SQlite, ...): + +```sh +$ apk update +$ apk add git g++ swig make cmake python3 python3-dev linux-headers +``` diff --git a/pyiec61850/examples/dispServerStruct.py b/pyiec61850/examples/dispServerStruct.py index aea02b4f..2b49479b 100755 --- a/pyiec61850/examples/dispServerStruct.py +++ b/pyiec61850/examples/dispServerStruct.py @@ -1,6 +1,6 @@ #!/usr/bin/python import os,sys -import iec61850 +import pyiec61850 as iec61850 if __name__=="__main__": hostname = "localhost"; tcpPort = 102 diff --git a/pyiec61850/examples/rcbSubscriptionExample.py b/pyiec61850/examples/rcbSubscriptionExample.py index d4d05aa5..7d1243be 100644 --- a/pyiec61850/examples/rcbSubscriptionExample.py +++ b/pyiec61850/examples/rcbSubscriptionExample.py @@ -27,7 +27,7 @@ The user needs to: import time import sys -import iec61850 +import pyiec61850 as iec61850 def open_connection(ip_address, mms_port): diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 254fec64..4cdbe0ea 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -1,5 +1,5 @@ /* File : iec61850.i */ -%module(directors="1") iec61850 +%module(directors="1") pyiec61850 %ignore ControlObjectClient_setTestMode(ControlObjectClient self); %ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated); %ignore LogicalNode_hasBufferedReports(LogicalNode* node); diff --git a/pyiec61850/test_pyiec61850.py b/pyiec61850/test_pyiec61850.py index aceb3fdb..6f6d8cdd 100755 --- a/pyiec61850/test_pyiec61850.py +++ b/pyiec61850/test_pyiec61850.py @@ -6,7 +6,7 @@ import traceback import signal import sys sys.path.append('.') -import iec61850 +import pyiec61850 as iec61850 def signal_handler(signal, frame): global running running =0 diff --git a/pyiec61850/tutorial.md b/pyiec61850/tutorial.md deleted file mode 100644 index ce766cab..00000000 --- a/pyiec61850/tutorial.md +++ /dev/null @@ -1,50 +0,0 @@ -# Building -Before building you should install swig and python. -To build python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line: -```sh -$ cmake -DBUILD_PYTHON_BINDINGS=ON . -``` -Then compile the library and install it. CMake and swig will automatically detect your python version and install the python library in python library directories. - -pyiec61850 library is to be imported calling -```python -import iec61850 -``` -# Client tutorial - -The python bindings works similarly to the basic C library. However there are some differences: - -* a specific function is to be called to cast variables from one type to another -* arguments passed by pointer are to be removed from arguments and append to the return list - - -For example to create a connection, call: -```python - con = iec61850.IedConnection_create() - error = iec61850.IedConnection_connect(con, "localhost", 102) - if (error == iec61850.IED_ERROR_OK): - # Do some work - iec61850.IedConnection_close(con) - iec61850.IedConnection_destroy(con) -``` - -To iterate over a list of logical devices, the code becomes: -```python -[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con) -device = iec61850.LinkedList_getNext(deviceList) - -while device: - print("LD: %s" % iec61850.toCharP(device.data)) - [logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory( - con, iec61850.toCharP(device.data)) - device = iec61850.LinkedList_getNext(device) -iec61850.LinkedList_destroy(deviceList) -``` - -Reading and writing operations can be performed using this syntax: -```python -[floatValue, error] = iec61850.IedConnection_readFloatValue(con, - "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX) -err = iec61850.IedConnection_writeFloatValue(con, - "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0) -``` diff --git a/src/common/inc/string_utilities.h b/src/common/inc/string_utilities.h index b6b238ff..404aec3e 100644 --- a/src/common/inc/string_utilities.h +++ b/src/common/inc/string_utilities.h @@ -40,6 +40,19 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1); LIB61850_INTERNAL char* StringUtils_copyStringToBuffer(const char* string, char* buffer); +/** + * \brief Copy string to buffer and replace characters + * + * NOTE: str should be a 0 terminated string. The terminating 0 is also copied. + * + * \param str the source string to copy + * \param buffer the destination buffer + * \param oldChar the character that has to be replaced while copying + * \param newChar the replacement character + */ +LIB61850_INTERNAL char* +StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char oldChar, char newChar); + LIB61850_INTERNAL char* StringUtils_copySubString(char* startPos, char* endPos); diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c index abf9a04c..1608f964 100644 --- a/src/common/string_utilities.c +++ b/src/common/string_utilities.c @@ -1,7 +1,7 @@ /* * string_utilities.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -65,18 +65,39 @@ StringUtils_copyStringToBuffer(const char* string, char* buffer) return buffer; } +char* +StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char oldChar, char newChar) +{ + int i = 0; + + while (true) + { + if (str[i] == oldChar) + buffer[i] = newChar; + else + buffer[i] = str[i]; + + if (str[i] == 0) + break; + + i++; + } + + return buffer; +} + char* StringUtils_createStringFromBuffer(const uint8_t* buf, int size) { - char* newStr = (char*) GLOBAL_MALLOC(size + 1); + char* newStr = (char*) GLOBAL_MALLOC(size + 1); - if (newStr) { - memcpy(newStr, buf, size); - newStr[size] = 0; - } + if (newStr) { + memcpy(newStr, buf, size); + newStr[size] = 0; + } - return newStr; + return newStr; } char* @@ -408,11 +429,11 @@ getCharWeight(int c) { static bool initialized = false; static char lookupTable[LT_MAX_CHARS + 1]; + static const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789"; if (!initialized) { int ltIndex; int weight = 1; - const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789"; for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) { if (strchr(charOrder, ltIndex)) continue; diff --git a/src/goose/goose_publisher.c b/src/goose/goose_publisher.c index 1abf067d..d1ec7e20 100644 --- a/src/goose/goose_publisher.c +++ b/src/goose/goose_publisher.c @@ -1,7 +1,7 @@ /* * goose_publisher.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -65,18 +65,19 @@ GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, boo { GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher)); - if (self) { - - if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag)) { + if (self) + { + if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag)) + { self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); GoosePublisher_reset(self); } - else { + else + { GoosePublisher_destroy(self); self = NULL; } - } return self; @@ -91,20 +92,21 @@ GoosePublisher_create(CommParameters* parameters, const char* interfaceID) void GoosePublisher_destroy(GoosePublisher self) { - if (self) { + if (self) + { if (self->ethernetSocket) { Ethernet_destroySocket(self->ethernetSocket); } MmsValue_delete(self->timestamp); - if (self->goID != NULL) + if (self->goID) GLOBAL_FREEMEM(self->goID); - if (self->goCBRef != NULL) + if (self->goCBRef) GLOBAL_FREEMEM(self->goCBRef); - if (self->dataSetRef != NULL) + if (self->dataSetRef) GLOBAL_FREEMEM(self->dataSetRef); if (self->buffer) @@ -117,18 +119,27 @@ GoosePublisher_destroy(GoosePublisher self) void GoosePublisher_setGoID(GoosePublisher self, char* goID) { + if (self->goID) + GLOBAL_FREEMEM(self->goID); + self->goID = StringUtils_copyString(goID); } void GoosePublisher_setGoCbRef(GoosePublisher self, char* goCbRef) { + if (self->goCBRef) + GLOBAL_FREEMEM(self->goCBRef); + self->goCBRef = StringUtils_copyString(goCbRef); } void GoosePublisher_setDataSetRef(GoosePublisher self, char* dataSetRef) { + if (self->dataSetRef) + GLOBAL_FREEMEM(self->dataSetRef); + self->dataSetRef = StringUtils_copyString(dataSetRef); } @@ -197,7 +208,7 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* { uint8_t srcAddr[6]; - if (interfaceID != NULL) + if (interfaceID) Ethernet_getInterfaceMACAddress(interfaceID, srcAddr); else Ethernet_getInterfaceMACAddress(CONFIG_ETHERNET_INTERFACE_ID, srcAddr); @@ -209,71 +220,84 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* uint16_t vlanId; uint16_t appId; - if (parameters == NULL) { - dstAddr = defaultDstAddr; - priority = CONFIG_GOOSE_DEFAULT_PRIORITY; - vlanId = CONFIG_GOOSE_DEFAULT_VLAN_ID; - appId = CONFIG_GOOSE_DEFAULT_APPID; - } - else { + if (parameters) + { dstAddr = parameters->dstAddress; priority = parameters->vlanPriority; vlanId = parameters->vlanId; appId = parameters->appId; } + else + { + dstAddr = defaultDstAddr; + priority = CONFIG_GOOSE_DEFAULT_PRIORITY; + vlanId = CONFIG_GOOSE_DEFAULT_VLAN_ID; + appId = CONFIG_GOOSE_DEFAULT_APPID; + } - if (interfaceID != NULL) + if (interfaceID) self->ethernetSocket = Ethernet_createSocket(interfaceID, dstAddr); else self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr); - if (self->ethernetSocket) { + if (self->ethernetSocket) + { self->buffer = (uint8_t*) GLOBAL_MALLOC(GOOSE_MAX_MESSAGE_SIZE); - memcpy(self->buffer, dstAddr, 6); - memcpy(self->buffer + 6, srcAddr, 6); + if (self->buffer) + { + memcpy(self->buffer, dstAddr, 6); + memcpy(self->buffer + 6, srcAddr, 6); - int bufPos = 12; + int bufPos = 12; - if (useVlanTags) { - /* Priority tag - IEEE 802.1Q */ - self->buffer[bufPos++] = 0x81; - self->buffer[bufPos++] = 0x00; + if (useVlanTags) + { + /* Priority tag - IEEE 802.1Q */ + self->buffer[bufPos++] = 0x81; + self->buffer[bufPos++] = 0x00; - uint8_t tci1 = priority << 5; - tci1 += vlanId / 256; + uint8_t tci1 = priority << 5; + tci1 += vlanId / 256; - uint8_t tci2 = vlanId % 256; + uint8_t tci2 = vlanId % 256; - self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ - self->buffer[bufPos++] = tci2; /* VLAN-ID */ - } + self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ + self->buffer[bufPos++] = tci2; /* VLAN-ID */ + } - /* EtherType GOOSE */ - self->buffer[bufPos++] = 0x88; - self->buffer[bufPos++] = 0xB8; + /* EtherType GOOSE */ + self->buffer[bufPos++] = 0x88; + self->buffer[bufPos++] = 0xB8; - /* APPID */ - self->buffer[bufPos++] = appId / 256; - self->buffer[bufPos++] = appId % 256; + /* APPID */ + self->buffer[bufPos++] = appId / 256; + self->buffer[bufPos++] = appId % 256; - self->lengthField = bufPos; + self->lengthField = bufPos; - /* Length */ - self->buffer[bufPos++] = 0x00; - self->buffer[bufPos++] = 0x08; + /* Length */ + self->buffer[bufPos++] = 0x00; + self->buffer[bufPos++] = 0x08; - /* Reserved1 */ - self->buffer[bufPos++] = 0x00; - self->buffer[bufPos++] = 0x00; + /* Reserved1 */ + self->buffer[bufPos++] = 0x00; + self->buffer[bufPos++] = 0x00; - /* Reserved2 */ - self->buffer[bufPos++] = 0x00; - self->buffer[bufPos++] = 0x00; + /* Reserved2 */ + self->buffer[bufPos++] = 0x00; + self->buffer[bufPos++] = 0x00; - self->payloadStart = bufPos; + self->payloadStart = bufPos; - return true; + return true; + } + else + { + if (DEBUG_GOOSE_PUBLISHER) + printf("GOOSE_PUBLISHER: Failed to allocate buffer\n"); + return false; + } } else { return false; @@ -281,8 +305,8 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* } static int32_t -createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffer, size_t maxPayloadSize) { - +createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffer, size_t maxPayloadSize) +{ /* Step 1 - calculate length fields */ uint32_t goosePduLength = 0; @@ -294,7 +318,7 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe goosePduLength += BerEncoder_determineEncodedStringSize(self->dataSetRef); - if (self->goID != NULL) + if (self->goID) goosePduLength += BerEncoder_determineEncodedStringSize(self->goID); else goosePduLength += BerEncoder_determineEncodedStringSize(self->goCBRef); @@ -317,13 +341,16 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe LinkedList element = LinkedList_getNext(dataSetValues); - while (element != NULL) { + while (element) + { MmsValue* dataSetEntry = (MmsValue*) element->data; - if (dataSetEntry) { + if (dataSetEntry) + { dataSetSize += MmsValue_encodeMmsData(dataSetEntry, NULL, 0, false); } - else { + else + { /* TODO encode MMS NULL */ if (DEBUG_GOOSE_PUBLISHER) printf("GOOSE_PUBLISHER: NULL value in data set!\n"); @@ -358,7 +385,7 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe bufPos = BerEncoder_encodeStringWithTag(0x82, self->dataSetRef, buffer, bufPos); /* Encode goID */ - if (self->goID != NULL) + if (self->goID) bufPos = BerEncoder_encodeStringWithTag(0x83, self->goID, buffer, bufPos); else bufPos = BerEncoder_encodeStringWithTag(0x83, self->goCBRef, buffer, bufPos); @@ -390,7 +417,8 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe /* Encode data set entries */ element = LinkedList_getNext(dataSetValues); - while (element != NULL) { + while (element) + { MmsValue* dataSetEntry = (MmsValue*) element->data; if (dataSetEntry) { @@ -444,7 +472,7 @@ GoosePublisher_publishAndDump(GoosePublisher self, LinkedList dataSet, char *msg int rc = GoosePublisher_publish(self, dataSet); if (rc == 0) - { + { int copied = self->payloadStart + self->payloadLength; if (bufSize < copied) diff --git a/src/goose/goose_publisher.h b/src/goose/goose_publisher.h index 77eb2766..0d5e6417 100644 --- a/src/goose/goose_publisher.h +++ b/src/goose/goose_publisher.h @@ -170,7 +170,7 @@ GoosePublisher_setStNum(GoosePublisher self, uint32_t stNum); * NOTE: Only for testing! The sequence number is increase manually whenever \ref GoosePublisher_publish is called. * * \param self GoosePublisher instance - * \param stNum the state number of the next GOOSE message to send + * \param sqNum the sequence number of the next GOOSE message to send */ LIB61850_API void GoosePublisher_setSqNum(GoosePublisher self, uint32_t sqNum); diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c index 63fe9710..1c6abac2 100644 --- a/src/goose/goose_receiver.c +++ b/src/goose/goose_receiver.c @@ -1,7 +1,7 @@ /* * goose_receiver.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -63,7 +63,8 @@ GooseReceiver_createEx(uint8_t* buffer) { GooseReceiver self = (GooseReceiver) GLOBAL_MALLOC(sizeof(struct sGooseReceiver)); - if (self != NULL) { + if (self != NULL) + { self->running = false; self->stop = false; self->interfaceId = NULL; @@ -124,9 +125,20 @@ static void createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength) { value->value.visibleString.buf = (char*) GLOBAL_MALLOC(elementLength + 1); - memcpy(value->value.visibleString.buf, bufferSrc, elementLength); - value->value.visibleString.buf[elementLength] = 0; - value->value.visibleString.size = elementLength; + + if (value->value.visibleString.buf) + { + memcpy(value->value.visibleString.buf, bufferSrc, elementLength); + value->value.visibleString.buf[elementLength] = 0; + value->value.visibleString.size = elementLength; + } + else + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: failed to allocate memory for visible string\n"); + + value->value.visibleString.size = 0; + } } static GooseParseError @@ -141,24 +153,28 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) GooseParseError pe = GOOSE_PARSE_ERROR_NO_ERROR; uint8_t tag; - while (bufPos < allDataLength) { + while (bufPos < allDataLength) + { tag = buffer[bufPos++]; - if (elementIndex > maxIndex) { + if (elementIndex > maxIndex) + { pe = GOOSE_PARSE_ERROR_OVERFLOW; break; /* from while */ } MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex); - if (value == NULL) { + if (value == NULL) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: type mismatch (element %i not found)\n", elementIndex); return GOOSE_PARSE_ERROR_TYPE_MISMATCH; } bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); - if (bufPos < 0) { + if (bufPos < 0) + { pe = GOOSE_PARSE_ERROR_TAGDECODE; break; /* from while */ } @@ -173,7 +189,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case 0xa1: /* array */ if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n"); - if (MmsValue_getType(value) == MMS_ARRAY) { + if (MmsValue_getType(value) == MMS_ARRAY) + { if (parseAllData(buffer + bufPos, elementLength, value) != GOOSE_PARSE_ERROR_NO_ERROR) pe = GOOSE_PARSE_ERROR_SUBLEVEL; } @@ -185,7 +202,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case 0xa2: /* structure */ if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n"); - if (MmsValue_getType(value) == MMS_STRUCTURE) { + if (MmsValue_getType(value) == MMS_STRUCTURE) + { if (parseAllData(buffer + bufPos, elementLength, value) != GOOSE_PARSE_ERROR_NO_ERROR) pe = GOOSE_PARSE_ERROR_SUBLEVEL; } @@ -208,18 +226,23 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x84: /* BIT STRING */ - if (MmsValue_getType(value) == MMS_BIT_STRING) { + if (MmsValue_getType(value) == MMS_BIT_STRING) + { int padding = buffer[bufPos]; - if (padding > 7) { + if (padding > 7) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n"); pe = GOOSE_PARSE_ERROR_INVALID_PADDING; } - else { + else + { int bitStringLength = (8 * (elementLength - 1)) - padding; - if (bitStringLength == value->value.bitString.size) { + + if (bitStringLength == value->value.bitString.size) + { memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1); } @@ -234,8 +257,10 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x85: /* integer */ - if (MmsValue_getType(value) == MMS_INTEGER) { - if (elementLength <= value->value.integer->maxSize) { + if (MmsValue_getType(value) == MMS_INTEGER) + { + if (elementLength <= value->value.integer->maxSize) + { value->value.integer->size = elementLength; memcpy(value->value.integer->octets, buffer + bufPos, elementLength); } @@ -249,8 +274,10 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x86: /* unsigned integer */ - if (MmsValue_getType(value) == MMS_UNSIGNED) { - if (elementLength <= value->value.integer->maxSize) { + if (MmsValue_getType(value) == MMS_UNSIGNED) + { + if (elementLength <= value->value.integer->maxSize) + { value->value.integer->size = elementLength; memcpy(value->value.integer->octets, buffer + bufPos, elementLength); } @@ -264,7 +291,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x87: /* Float */ - if (MmsValue_getType(value) == MMS_FLOAT) { + if (MmsValue_getType(value) == MMS_FLOAT) + { if (elementLength == 9) { MmsValue_setDouble(value, BerDecoder_decodeDouble(buffer, bufPos)); } @@ -281,15 +309,19 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x89: /* octet string */ - if (MmsValue_getType(value) == MMS_OCTET_STRING) { - if (elementLength <= abs(value->value.octetString.maxSize)) { + if (MmsValue_getType(value) == MMS_OCTET_STRING) + { + if (elementLength <= abs(value->value.octetString.maxSize)) + { value->value.octetString.size = elementLength; memcpy(value->value.octetString.buf, buffer + bufPos, elementLength); } - else { + else + { uint8_t* newBuf = (uint8_t*)GLOBAL_MALLOC(elementLength); - if (newBuf) { + if (newBuf) + { memcpy(newBuf, buffer + bufPos, elementLength); uint8_t* oldBuf = value->value.octetString.buf; @@ -300,7 +332,6 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) GLOBAL_FREEMEM(oldBuf); } - } } else { @@ -309,14 +340,17 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x8a: /* visible string */ - if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { - - if (value->value.visibleString.buf != NULL) { - if ((int32_t) value->value.visibleString.size >= elementLength) { + if (MmsValue_getType(value) == MMS_VISIBLE_STRING) + { + if (value->value.visibleString.buf != NULL) + { + if ((int32_t) value->value.visibleString.size >= elementLength) + { memcpy(value->value.visibleString.buf, buffer + bufPos, elementLength); value->value.visibleString.buf[elementLength] = 0; } - else { + else + { GLOBAL_FREEMEM(value->value.visibleString.buf); createNewStringFromBufferElement(value, buffer + bufPos, elementLength); @@ -324,7 +358,6 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } else createNewStringFromBufferElement(value, buffer + bufPos, elementLength); - } else { pe = GOOSE_PARSE_ERROR_TYPE_MISMATCH; @@ -332,7 +365,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x8c: /* binary time */ - if (MmsValue_getType(value) == MMS_BINARY_TIME) { + if (MmsValue_getType(value) == MMS_BINARY_TIME) + { if ((elementLength == 4) || (elementLength == 6)) { memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength); } @@ -343,7 +377,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x91: /* Utctime */ - if (elementLength == 8) { + if (elementLength == 8) + { if (MmsValue_getType(value) == MMS_UTC_TIME) { MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); } @@ -370,13 +405,15 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) elementIndex++; } - if (elementIndex <= maxIndex) { + if (elementIndex <= maxIndex) + { if (pe == GOOSE_PARSE_ERROR_NO_ERROR) { pe = GOOSE_PARSE_ERROR_UNDERFLOW; } } - if (DEBUG_GOOSE_SUBSCRIBER) { + if (DEBUG_GOOSE_SUBSCRIBER) + { switch (pe) { case GOOSE_PARSE_ERROR_UNKNOWN_TAG: printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag); @@ -419,11 +456,14 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt MmsValue* dataSetValues = NULL; - while (bufPos < allDataLength) { + while (bufPos < allDataLength) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); - if (bufPos < 0) { + + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -474,11 +514,14 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt elementIndex = 0; bufPos = 0; - while (bufPos < allDataLength) { + while (bufPos < allDataLength) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); - if (bufPos < 0) { + + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -528,22 +571,26 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt case 0x84: /* BIT STRING */ { - if (elementLength > 1) { + if (elementLength > 1) + { int padding = buffer[bufPos]; int rawBitLength = (elementLength - 1) * 8; - if (padding > 7) { + if (padding > 7) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n"); goto exit_with_error; } - else { + else + { value = MmsValue_newBitString(rawBitLength - padding); memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1); } } - else { + else + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: invalid bit-string\n"); @@ -553,13 +600,15 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt break; case 0x85: /* integer */ - if (elementLength > 8) { - if (DEBUG_GOOSE_SUBSCRIBER) - printf("GOOSE_SUBSCRIBER: unsupported integer size(%i)\n", elementLength); + if (elementLength > 8) + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: unsupported integer size(%i)\n", elementLength); - goto exit_with_error; + goto exit_with_error; } - else { + else + { value = MmsValue_newInteger(elementLength * 8); memcpy(value->value.integer->octets, buffer + bufPos, elementLength); value->value.integer->size = elementLength; @@ -568,13 +617,15 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt break; case 0x86: /* unsigned integer */ - if (elementLength > 8) { + if (elementLength > 8) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: unsupported unsigned size(%i)\n", elementLength); goto exit_with_error; } - else { + else + { value = MmsValue_newUnsigned(elementLength * 8); memcpy(value->value.integer->octets, buffer + bufPos, elementLength); value->value.integer->size = elementLength; @@ -610,13 +661,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt break; case 0x91: /* Utctime */ - if (elementLength == 8) { + if (elementLength == 8) + { value = MmsValue_newUtcTime(0); MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); } else - if (DEBUG_GOOSE_SUBSCRIBER) - printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); + } break; default: @@ -627,7 +681,8 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt bufPos += elementLength; - if (value != NULL) { + if (value != NULL) + { MmsValue_setElement(dataSetValues, elementIndex, value); elementIndex++; } @@ -662,10 +717,12 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) uint32_t numberOfDatSetEntries = 0; - if (buffer[bufPos++] == 0x61) { + if (buffer[bufPos++] == 0x61) + { int gooseLength; bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -673,12 +730,14 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) int gooseEnd = bufPos + gooseLength; - while (bufPos < gooseEnd) { + while (bufPos < gooseEnd) + { int elementLength; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -696,16 +755,19 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) { LinkedList element = LinkedList_getNext(self->subscriberList); - while (element != NULL) { + while (element) + { GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element); if (subscriber->isObserver) { - if (elementLength > 129) { + if (elementLength > 129) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef too long!\n"); } - else { + else + { memcpy(subscriber->goCBRef, buffer + bufPos, elementLength); subscriber->goCBRef[elementLength] = 0; } @@ -713,8 +775,10 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) matchingSubscriber = subscriber; break; } - else if (subscriber->goCBRefLen == elementLength) { - if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) { + else if (subscriber->goCBRefLen == elementLength) + { + if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n"); matchingSubscriber = subscriber; @@ -744,12 +808,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found dataSet\n"); { - if (matchingSubscriber) { - if (elementLength > 129) { + if (matchingSubscriber) + { + if (elementLength > 129) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: datSet too long!\n"); } - else { + else + { memcpy(matchingSubscriber->datSet, buffer + bufPos, elementLength); matchingSubscriber->datSet[elementLength] = 0; } @@ -761,12 +828,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found goId\n"); { - if (matchingSubscriber) { - if (elementLength > 129) { + if (matchingSubscriber) + { + if (elementLength > 129) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: goId too long!\n"); } - else { + else + { memcpy(matchingSubscriber->goId, buffer + bufPos, elementLength); matchingSubscriber->goId[elementLength] = 0; } @@ -832,15 +902,17 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) bufPos += elementLength; } - if (matchingSubscriber != NULL) { - + if (matchingSubscriber != NULL) + { matchingSubscriber->timeAllowedToLive = timeAllowedToLive; matchingSubscriber->ndsCom = ndsCom; matchingSubscriber->simulation = simulation; - if (matchingSubscriber->dataSetValuesSelfAllocated) { + if (matchingSubscriber->dataSetValuesSelfAllocated) + { /* when confRev changed replaced old data set */ - if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) { + if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) + { MmsValue_delete(matchingSubscriber->dataSetValues); matchingSubscriber->dataSetValues = NULL; } @@ -850,14 +922,16 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (timestampBufPos) MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos); - else { + else + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: GOOSE message has no time stamp\n"); MmsValue_setUtcTime(matchingSubscriber->timestamp, 0); } - if (matchingSubscriber->isObserver && matchingSubscriber->dataSetValues != NULL) { + if (matchingSubscriber->isObserver && matchingSubscriber->dataSetValues != NULL) + { MmsValue_delete(matchingSubscriber->dataSetValues); matchingSubscriber->dataSetValues = NULL; } @@ -866,7 +940,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (matchingSubscriber->dataSetValues == NULL) matchingSubscriber->dataSetValues = parseAllDataUnknownValue(matchingSubscriber, dataSetBufferAddress, dataSetBufferLength, false); - else { + else + { GooseParseError parseError = parseAllData(dataSetBufferAddress, dataSetBufferLength, matchingSubscriber->dataSetValues); if (parseError != GOOSE_PARSE_ERROR_NO_ERROR) { @@ -876,7 +951,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) matchingSubscriber->parseError = parseError; } - if (matchingSubscriber->stNum == stNum) { + if (matchingSubscriber->stNum == stNum) + { if (matchingSubscriber->sqNum >= sqNum) { isValid = false; } @@ -920,13 +996,18 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) uint8_t priority = 0; uint16_t vlanId = 0; bool vlanSet = false; + /* check for VLAN tag */ - if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) { + if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) + { priority = buffer[bufPos + 2] & 0xF8 >> 5; vlanId = ((buffer[bufPos + 2] & 0x07) << 8) + buffer[bufPos + 3]; vlanSet = true; bufPos += 4; /* skip VLAN tag */ headerLength += 4; + + if (numbytes < (22 + 4)) + return; } /* check for GOOSE Ethertype */ @@ -956,13 +1037,22 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) int apduLength = length - 8; - if (numbytes < length + headerLength) { + if (apduLength < 0) + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Invalid length field\n"); + return; + } + + if (numbytes < length + headerLength) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Invalid PDU size\n"); return; } - if (DEBUG_GOOSE_SUBSCRIBER) { + if (DEBUG_GOOSE_SUBSCRIBER) + { printf("GOOSE_SUBSCRIBER: GOOSE message:\nGOOSE_SUBSCRIBER: ----------------\n"); printf("GOOSE_SUBSCRIBER: DST-MAC: %02x:%02x:%02x:%02x:%02X:%02X\n", dstMac[0], dstMac[1], dstMac[2], dstMac[3], dstMac[4], dstMac[5]); @@ -974,7 +1064,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) /* check if there is an interested subscriber */ LinkedList element = LinkedList_getNext(self->subscriberList); - while (element != NULL) { + while (element) + { GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element); if (subscriber->isObserver) @@ -990,7 +1081,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) } if (((subscriber->appId == -1) || (subscriber->appId == appId)) && - (!subscriber->dstMacSet || (memcmp(subscriber->dstMac, dstMac,6) == 0))) { + (!subscriber->dstMacSet || (memcmp(subscriber->dstMac, dstMac,6) == 0))) + { subscriberFound = true; break; } @@ -1014,9 +1106,12 @@ gooseReceiverLoop(void *threadParameter) EthernetHandleSet handleSet = EthernetHandleSet_new(); EthernetHandleSet_addSocket(handleSet, self->ethSocket); - if (self->running) { + bool running = true; - while (self->running) { + if (running) + { + while (running) + { switch (EthernetHandleSet_waitReady(handleSet, 100)) { case -1: @@ -1030,6 +1125,8 @@ gooseReceiverLoop(void *threadParameter) } if (self->stop) break; + + running = self->running; } GooseReceiver_stopThreadless(self); @@ -1046,7 +1143,8 @@ void GooseReceiver_start(GooseReceiver self) { #if (CONFIG_MMS_THREADLESS_STACK == 0) - if (GooseReceiver_startThreadless(self)) { + if (GooseReceiver_startThreadless(self)) + { self->thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, false); if (self->thread != NULL) { @@ -1086,7 +1184,8 @@ GooseReceiver_stop(GooseReceiver self) void GooseReceiver_destroy(GooseReceiver self) { - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK == 0) if ((self->thread != NULL) && (GooseReceiver_isRunning(self))) GooseReceiver_stop(self); @@ -1114,7 +1213,8 @@ GooseReceiver_startThreadless(GooseReceiver self) else self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL); - if (self->ethSocket != NULL) { + if (self->ethSocket != NULL) + { Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE); /* set multicast addresses for subscribers */ @@ -1122,14 +1222,17 @@ GooseReceiver_startThreadless(GooseReceiver self) LinkedList element = LinkedList_getNext(self->subscriberList); - while (element != NULL) { + while (element != NULL) + { GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element); - if (subscriber->dstMacSet == false) { + if (subscriber->dstMacSet == false) + { /* no destination MAC address defined -> we have to switch to all multicast mode */ Ethernet_setMode(self->ethSocket, ETHERNET_SOCKET_MODE_ALL_MULTICAST); } - else { + else + { Ethernet_addMulticastAddress(self->ethSocket, subscriber->dstMac); } @@ -1159,7 +1262,8 @@ GooseReceiver_tick(GooseReceiver self) { int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH); - if (packetSize > 0) { + if (packetSize > 0) + { parseGooseMessage(self, self->buffer, packetSize); return true; } diff --git a/src/goose/goose_subscriber.c b/src/goose/goose_subscriber.c index 64227127..019c2b7e 100644 --- a/src/goose/goose_subscriber.c +++ b/src/goose/goose_subscriber.c @@ -1,7 +1,7 @@ /* * goose_subscriber.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -40,7 +40,8 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues) { GooseSubscriber self = (GooseSubscriber) GLOBAL_CALLOC(1, sizeof(struct sGooseSubscriber)); - if (self) { + if (self) + { StringUtils_copyStringMax(self->goCBRef, 130, goCbRef); self->goCBRefLen = strlen(goCbRef); @@ -97,7 +98,8 @@ GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId) void GooseSubscriber_destroy(GooseSubscriber self) { - if (self) { + if (self) + { MmsValue_delete(self->timestamp); if (self->dataSetValuesSelfAllocated) @@ -120,19 +122,19 @@ GooseSubscriber_getAppId(GooseSubscriber self) return self->appId; } -char * +char* GooseSubscriber_getGoId(GooseSubscriber self) { return self->goId; } -char * +char* GooseSubscriber_getGoCbRef(GooseSubscriber self) { return self->goCBRef; } -char * +char* GooseSubscriber_getDataSet(GooseSubscriber self) { return self->datSet; diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c index af12d26a..321532e5 100644 --- a/src/iec61850/client/client_report.c +++ b/src/iec61850/client/client_report.c @@ -3,7 +3,7 @@ * * Client implementation for IEC 61850 reporting. * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -283,14 +283,27 @@ lookupReportHandler(IedConnection self, const char* rcbReference) return NULL; } +static void +uninstallReportHandler(IedConnection self, const char* rcbReference) +{ + ClientReport report = lookupReportHandler(self, rcbReference); + + if (report != NULL) { + LinkedList_remove(self->enabledReports, report); + ClientReport_destroy(report); + } +} + void IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId, ReportCallbackFunction handler, void* handlerParameter) { + Semaphore_wait(self->reportHandlerMutex); + ClientReport report = lookupReportHandler(self, rcbReference); if (report != NULL) { - IedConnection_uninstallReportHandler(self, rcbReference); + uninstallReportHandler(self, rcbReference); if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: Removed existing report callback handler for %s\n", rcbReference); @@ -306,8 +319,8 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference, else report->rptId = NULL; - Semaphore_wait(self->reportHandlerMutex); LinkedList_add(self->enabledReports, report); + Semaphore_post(self->reportHandlerMutex); if (DEBUG_IED_CLIENT) @@ -319,12 +332,7 @@ IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReferenc { Semaphore_wait(self->reportHandlerMutex); - ClientReport report = lookupReportHandler(self, rcbReference); - - if (report != NULL) { - LinkedList_remove(self->enabledReports, report); - ClientReport_destroy(report); - } + uninstallReportHandler(self, rcbReference); Semaphore_post(self->reportHandlerMutex); } @@ -367,6 +375,8 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c void iedConnection_handleReport(IedConnection self, MmsValue* value) { + Semaphore_wait(self->reportHandlerMutex); + MmsValue* rptIdValue = MmsValue_getElement(value, 0); if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING)) { @@ -769,15 +779,14 @@ iedConnection_handleReport(IedConnection self, MmsValue* value) matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED; } } - - Semaphore_wait(self->reportHandlerMutex); if (matchingReport->callback != NULL) matchingReport->callback(matchingReport->callbackParameter, matchingReport); +exit_function: + Semaphore_post(self->reportHandlerMutex); -exit_function: return; } diff --git a/src/iec61850/client/client_report_control.c b/src/iec61850/client/client_report_control.c index 6affd50e..65967cdb 100644 --- a/src/iec61850/client/client_report_control.c +++ b/src/iec61850/client/client_report_control.c @@ -528,39 +528,44 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GetRCBValuesHandler handler = (IedConnection_GetRCBValuesHandler) call->callback; ClientReportControlBlock updateRcb = (ClientReportControlBlock) call->specificParameter; char* rcbReference = (char*) call->specificParameter2.pointer; - - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), NULL); } - else { - - if (value == NULL) { + else + { + if (value == NULL) + { handler(invokeId, call->callbackParameter, IED_ERROR_OBJECT_DOES_NOT_EXIST, NULL); } - else { - if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { + else + { + if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) + { if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: getRCBValues returned data-access-error!\n"); handler(invokeId, call->callbackParameter, iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)), NULL); } - else { - + else + { ClientReportControlBlock returnRcb = updateRcb; if (returnRcb == NULL) returnRcb = ClientReportControlBlock_create(rcbReference); - if (clientReportControlBlock_updateValues(returnRcb, value)) { + if (clientReportControlBlock_updateValues(returnRcb, value)) + { handler(invokeId, call->callbackParameter, IED_ERROR_OK, returnRcb); } - else { + else + { if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n"); @@ -569,19 +574,18 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV if (updateRcb == NULL) ClientReportControlBlock_destroy(returnRcb); } - } MmsValue_delete(value); } - } GLOBAL_FREEMEM(rcbReference); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -598,7 +602,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* domainName = MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId); - if (domainName == NULL) { + if (domainName == NULL) + { *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; return 0; } @@ -608,7 +613,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -627,7 +633,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { GLOBAL_FREEMEM(call->specificParameter2.pointer); iedConnection_releaseOutstandingCall(self, call); return 0; diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index bc8e9ff6..23ccf6ff 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1,7 +1,7 @@ /* * ied_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -33,7 +33,6 @@ #define DEFAULT_CONNECTION_TIMEOUT 10000 #define DATA_SET_MAX_NAME_LENGTH 64 /* is 32 according to standard! */ -#define OUTSTANDING_CALLS 12 typedef struct sICLogicalDevice { @@ -47,12 +46,107 @@ struct sClientDataSet MmsValue* dataSetValues; /* MmsValue instance of type MMS_ARRAY */ }; -struct sFileDirectoryEntry { +struct sFileDirectoryEntry +{ char* fileName; uint32_t fileSize; uint64_t lastModified; }; +const char* +IedClientError_toString(IedClientError err) +{ + switch (err) + { + case IED_ERROR_OK: + return "ok"; + + case IED_ERROR_NOT_CONNECTED: + return "not-connected"; + + case IED_ERROR_ALREADY_CONNECTED: + return "already-connected"; + + case IED_ERROR_CONNECTION_LOST: + return "connection-lost"; + + case IED_ERROR_SERVICE_NOT_SUPPORTED: + return "service-not-supported"; + + case IED_ERROR_CONNECTION_REJECTED: + return "connection-rejected"; + + case IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED: + return "outstanding-call-limit-reached"; + + case IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT: + return "invalid-argument"; + + case IED_ERROR_ENABLE_REPORT_FAILED_DATASET_MISMATCH: + return "enable-report-failed-due-to-dataset-mismatch"; + + case IED_ERROR_OBJECT_REFERENCE_INVALID: + return "object-reference-invalid"; + + case IED_ERROR_UNEXPECTED_VALUE_RECEIVED: + return "unexpected-value-received"; + + case IED_ERROR_TIMEOUT: + return "timeout"; + + case IED_ERROR_ACCESS_DENIED: + return "access-denied"; + + case IED_ERROR_OBJECT_DOES_NOT_EXIST: + return "object-does-not-exist"; + + case IED_ERROR_OBJECT_EXISTS: + return "object-exists"; + + case IED_ERROR_OBJECT_ACCESS_UNSUPPORTED: + return "object-access-unsupported"; + + case IED_ERROR_TYPE_INCONSISTENT: + return "type-inconsistent"; + + case IED_ERROR_TEMPORARILY_UNAVAILABLE: + return "temporary-unavailable"; + + case IED_ERROR_OBJECT_UNDEFINED: + return "object-undefined"; + + case IED_ERROR_INVALID_ADDRESS: + return "invalid-address"; + + case IED_ERROR_HARDWARE_FAULT: + return "hardware-fault"; + + case IED_ERROR_TYPE_UNSUPPORTED: + return "type-unsupported"; + + case IED_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT: + return "object-attribute-inconsistent"; + + case IED_ERROR_OBJECT_VALUE_INVALID: + return "object-value-invalid"; + + case IED_ERROR_OBJECT_INVALIDATED: + return "object-invalidated"; + + case IED_ERROR_MALFORMED_MESSAGE: + return "malformed-message"; + + case IED_ERROR_OBJECT_CONSTRAINT_CONFLICT: + return "object-constraint-conflict"; + + case IED_ERROR_SERVICE_NOT_IMPLEMENTED: + return "service-not-implemented"; + + default: + return "unknown-error"; + } +} + IedClientError iedConnection_mapMmsErrorToIedError(MmsError mmsError) { @@ -108,6 +202,9 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError) case MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE: return IED_ERROR_TEMPORARILY_UNAVAILABLE; + case MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT: + return IED_ERROR_OBJECT_CONSTRAINT_CONFLICT; + default: return IED_ERROR_UNKNOWN; } @@ -174,8 +271,10 @@ iedConnection_allocateOutstandingCall(IedConnection self) int i = 0; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].used == false) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].used == false) + { self->outstandingCalls[i].used = true; call = &(self->outstandingCalls[i]); break; @@ -206,8 +305,10 @@ iedConnection_lookupOutstandingCall(IedConnection self, uint32_t invokeId) int i = 0; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) + { call = &(self->outstandingCalls[i]); break; } @@ -615,7 +716,8 @@ createNewConnectionObject(TLSConfiguration tlsConfig, bool useThreads) self->reportHandlerMutex = Semaphore_create(1); self->outstandingCallsLock = Semaphore_create(1); - self->outstandingCalls = (IedConnectionOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sIedConnectionOutstandingCall)); + self->maxOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->outstandingCalls = (IedConnectionOutstandingCall) GLOBAL_CALLOC(CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED, sizeof(struct sIedConnectionOutstandingCall)); self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; @@ -660,6 +762,29 @@ IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, in IsoConnectionParameters_setLocalTcpParameters(isoP, localIpAddress, localPort); } +void +IedConnection_setMaxOutstandingCalls(IedConnection self, int calling, int called) +{ + if (calling < 1) + calling = 1; + + if (called < 1) + called = 1; + + if (self->outstandingCalls) + { + GLOBAL_FREEMEM(self->outstandingCalls); + } + + self->maxOutstandingCalled = called; + self->outstandingCalls = (IedConnectionOutstandingCall)GLOBAL_CALLOC(called, sizeof(struct sIedConnectionOutstandingCall)); + + if (self->connection) + { + MmsConnnection_setMaxOutstandingCalls(self->connection, calling, called); + } +} + void IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs) { @@ -669,7 +794,8 @@ IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs) void IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs) { - if (self->connection) { + if (self->connection) + { MmsConnection_setRequestTimeout(self->connection, timeoutInMs); } } @@ -852,7 +978,7 @@ IedConnection_destroy(IedConnection self) GLOBAL_FREEMEM(self->outstandingCalls); - LinkedList_destroyStatic(self->clientControls); + LinkedList_destroyDeep(self->clientControls, (LinkedListValueDeleteFunction)ControlObjectClient_destroy); Semaphore_destroy(self->clientControlsLock); Semaphore_destroy(self->outstandingCallsLock); @@ -1024,7 +1150,8 @@ IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, MmsConnection_getDomainNamesAsync(self->connection, &(call->invokeId), &err, continueAfter, result, getNameListHandler, self); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(err); iedConnection_releaseOutstandingCall(self, call); @@ -1056,7 +1183,8 @@ IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* MmsConnection_getDomainVariableNamesAsync(self->connection, &(call->invokeId), &err, ldName, continueAfter, result, getNameListHandler, self); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(err); iedConnection_releaseOutstandingCall(self, call); @@ -1088,7 +1216,8 @@ IedConnection_getLogicalDeviceDataSetsAsync(IedConnection self, IedClientError* MmsConnection_getDomainVariableListNamesAsync(self->connection, &(call->invokeId), &err, ldName, continueAfter, result, getNameListHandler, self); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(err); iedConnection_releaseOutstandingCall(self, call); @@ -1109,15 +1238,16 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_ReadObjectHandler handler = (IedConnection_ReadObjectHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), value); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -1138,14 +1268,16 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c domainId = MmsMapping_getMmsDomainFromObjectReference(objRef, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objRef, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -1158,15 +1290,18 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); - if (secondBrace) { + if (secondBrace) + { char* endPtr; int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1185,8 +1320,8 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c else MmsConnection_readVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, readObjectHandlerInternal, self); - if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) { - + if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) + { if (err != MMS_ERROR_NONE) { *error = iedConnection_mapMmsErrorToIedError(err); } @@ -1199,7 +1334,6 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c return call->invokeId; } - MmsValue* IedConnection_readObject(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc) @@ -1214,7 +1348,8 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char* domainId = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objectReference, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -1224,15 +1359,18 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char* /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); - if (secondBrace) { + if (secondBrace) + { char* endPtr; int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1266,10 +1404,12 @@ IedConnection_readBooleanValue(IedConnection self, IedClientError* error, const bool retVal = false; - if (value != NULL) { + if (value != NULL) + { if (MmsValue_getType(value) == MMS_BOOLEAN) retVal = MmsValue_getBoolean(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1289,10 +1429,12 @@ IedConnection_readFloatValue(IedConnection self, IedClientError* error, const ch float retVal = 0.f; - if (value != NULL) { + if (value != NULL) + { if (MmsValue_getType(value) == MMS_FLOAT) retVal = MmsValue_toFloat(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1312,10 +1454,12 @@ IedConnection_readStringValue(IedConnection self, IedClientError* error, const c char* retVal = NULL; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_VISIBLE_STRING) || (MmsValue_getType(value) == MMS_STRING)) retVal = StringUtils_copyString(MmsValue_toString(value)); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1335,10 +1479,12 @@ IedConnection_readInt32Value(IedConnection self, IedClientError* error, const ch int32_t retVal = 0; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_INTEGER) || (MmsValue_getType(value) == MMS_UNSIGNED)) retVal = MmsValue_toInt32(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1358,10 +1504,12 @@ IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, con uint32_t retVal = 0; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_INTEGER) || (MmsValue_getType(value) == MMS_UNSIGNED)) retVal = MmsValue_toUint32(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1381,10 +1529,12 @@ IedConnection_readInt64Value(IedConnection self, IedClientError* error, const ch int64_t retVal = 0; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_INTEGER) || (MmsValue_getType(value) == MMS_UNSIGNED)) retVal = MmsValue_toInt64(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1405,15 +1555,17 @@ IedConnection_readTimestampValue(IedConnection self, IedClientError* error, cons Timestamp* retVal = timeStamp; - if (value != NULL) { - if (MmsValue_getType(value) == MMS_UTC_TIME) { - + if (value != NULL) + { + if (MmsValue_getType(value) == MMS_UTC_TIME) + { if (retVal == NULL) retVal = (Timestamp*) GLOBAL_MALLOC(sizeof(Timestamp)); memcpy(retVal->val, value->value.utcTime, 8); } - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1434,12 +1586,13 @@ IedConnection_readQualityValue(IedConnection self, IedClientError* error, const Quality quality = QUALITY_VALIDITY_GOOD; - if (value != NULL) { - + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_BIT_STRING) && (MmsValue_getBitStringSize(value) == 13)) { quality = Quality_fromMmsValue(value); } - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1466,7 +1619,8 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char* domainId = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objectReference, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return; } @@ -1476,15 +1630,18 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char* /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); - if (secondBrace) { + if (secondBrace) + { char* endPtr; int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1502,7 +1659,8 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char* else *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; } - else { + else + { MmsConnection_writeVariable(self->connection, &mmsError, domainId, itemId, value); *error = iedConnection_mapMmsErrorToIedError(mmsError); @@ -1516,8 +1674,8 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAc IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback; IedClientError iedError = iedConnection_mapMmsErrorToIedError(err); @@ -1529,7 +1687,8 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAc iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -1550,14 +1709,16 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const domainId = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objectReference, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -1571,7 +1732,8 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); if (secondBrace) { @@ -1579,7 +1741,8 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1598,13 +1761,15 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const else *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; } - else { + else + { MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, value, writeVariableHandler, self); *error = iedConnection_mapMmsErrorToIedError(err); } - if (*error != IED_ERROR_OK) { + if (*error != IED_ERROR_OK) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -1645,7 +1810,6 @@ IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const c IedConnection_writeObject(self, error, objectReference, fc, &mmsValue); } - void IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, uint32_t value) @@ -1714,9 +1878,10 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error LinkedList logicalDeviceNames = MmsConnection_getDomainNames(self->connection, &mmsError); - if (logicalDeviceNames != NULL) { - - if (self->logicalDevices != NULL) { + if (logicalDeviceNames) + { + if (self->logicalDevices) + { LinkedList_destroyDeep(self->logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy); self->logicalDevices = NULL; } @@ -1725,20 +1890,23 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error LinkedList logicalDevices = LinkedList_create(); - while (logicalDevice != NULL) { + while (logicalDevice) + { char* name = (char*) logicalDevice->data; LinkedList variables = MmsConnection_getDomainVariableNames(self->connection, &mmsError, name); - if (variables != NULL) { + if (variables) + { ICLogicalDevice* icLogicalDevice = ICLogicalDevice_create(name); ICLogicalDevice_setVariableList(icLogicalDevice, variables); LinkedList_add(logicalDevices, icLogicalDevice); } - else { + else + { if (error) *error = iedConnection_mapMmsErrorToIedError(mmsError); break; @@ -1747,10 +1915,12 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error logicalDevice = LinkedList_getNext(logicalDevice); } - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { LinkedList_destroyDeep(logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy); } - else { + else + { self->logicalDevices = logicalDevices; } @@ -1765,19 +1935,22 @@ IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error) { *error = IED_ERROR_OK; - if (self->logicalDevices == NULL) { + if (self->logicalDevices == NULL) + { IedConnection_getDeviceModelFromServer(self, error); if (*error != IED_ERROR_OK) return NULL; } - if (self->logicalDevices != NULL) { + if (self->logicalDevices) + { LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices); LinkedList logicalDeviceList = LinkedList_create(); - while (logicalDevice != NULL) { + while (logicalDevice) + { ICLogicalDevice* icLogicalDevice = (ICLogicalDevice*) logicalDevice->data; char* logicalDeviceName = StringUtils_copyString(icLogicalDevice->name); @@ -1787,11 +1960,16 @@ IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error) logicalDevice = LinkedList_getNext(logicalDevice); } - *error = IED_ERROR_OK; + if (error) + *error = IED_ERROR_OK; + return logicalDeviceList; } - else { - *error = IED_ERROR_UNKNOWN; + else + { + if (error) + *error = IED_ERROR_UNKNOWN; + return NULL; } } @@ -1819,19 +1997,22 @@ IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const bool moreFollows = false; - do { + do + { moreFollows = MmsConnection_getFileDirectory(self->connection, &mmsError, directoryName, continueAfter, mmsFileDirectoryHandler, fileNames); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); return NULL; } - if (moreFollows) { + if (moreFollows) + { FileDirectoryEntry lastDirectoryEntry = (FileDirectoryEntry) LinkedList_getData(LinkedList_getLastElement(fileNames)); @@ -1856,7 +2037,8 @@ IedConnection_getFileDirectoryEx(IedConnection self, IedClientError* error, cons bool moreFollowsInternal = MmsConnection_getFileDirectory(self->connection, &mmsError, directoryName, continueAfter, mmsFileDirectoryHandler, fileNames); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); @@ -1877,9 +2059,10 @@ fileDirectoryHandlerEx(uint32_t invokeId, void* parameter, MmsError err, char* f IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - - if (call->specificParameter2.getFileDirectory.cont) { + if (call) + { + if (call->specificParameter2.getFileDirectory.cont) + { IedConnection_FileDirectoryEntryHandler handler = (IedConnection_FileDirectoryEntryHandler) call->callback; call->specificParameter2.getFileDirectory.cont = @@ -1889,7 +2072,8 @@ fileDirectoryHandlerEx(uint32_t invokeId, void* parameter, MmsError err, char* f if (filename == NULL) iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -1903,7 +2087,8 @@ IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error, IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -1917,7 +2102,8 @@ IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error, *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -1957,7 +2143,8 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil int32_t frsmId = MmsConnection_fileOpen(self->connection, &mmsError, fileName, 0, &fileSize, NULL); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return 0; } @@ -1968,17 +2155,20 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil clientFileReadHandler.retVal = true; clientFileReadHandler.byteReceived = 0; - while (true) { + while (true) + { bool moreFollows = MmsConnection_fileRead(self->connection, &mmsError, frsmId, mmsFileReadHandler, &clientFileReadHandler); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return 0; } - if (clientFileReadHandler.retVal == false) { + if (clientFileReadHandler.retVal == false) + { *error = IED_ERROR_UNKNOWN; break; } @@ -1999,7 +2189,8 @@ mmsConnectionFileCloseHandler (uint32_t invokeId, void* parameter, MmsError mmsE { (void)success; - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: failed to close file error: %i (mms-error: %i)\n", iedConnection_mapMmsErrorToIedError(mmsError), mmsError); } @@ -2008,10 +2199,12 @@ mmsConnectionFileCloseHandler (uint32_t invokeId, void* parameter, MmsError mmsE IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { + if (call) + { iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2025,46 +2218,53 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(call->specificParameter2.getFileInfo.originalInvokeId, call->callbackParameter, err, invokeId, NULL, 0, false); - if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) { + if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) + { /* close file */ MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self); if (mmsError != MMS_ERROR_NONE) iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: getFile timeout -> stop download\n"); iedConnection_releaseOutstandingCall(self, call); } } - else { + else + { bool cont = handler(call->specificParameter2.getFileInfo.originalInvokeId, call->callbackParameter, IED_ERROR_OK, invokeId, buffer, byteReceived, moreFollows); - if ((moreFollows == false) || (cont == false)) { + if ((moreFollows == false) || (cont == false)) + { /* close file */ MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self); if (mmsError != MMS_ERROR_NONE) iedConnection_releaseOutstandingCall(self, call); } - else { + else + { /* send next read request */ MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileReadHandler, self); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); @@ -2072,16 +2272,16 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr /* close file */ MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); } - } } } - } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2097,24 +2297,27 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; call->specificParameter2.getFileInfo.originalInvokeId = invokeId; - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { call->specificParameter2.getFileInfo.originalInvokeId = invokeId; MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileReadHandler, self); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); @@ -2126,9 +2329,9 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr iedConnection_releaseOutstandingCall(self, call); } } - } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2143,7 +2346,8 @@ IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -2155,7 +2359,8 @@ IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -2197,14 +2402,16 @@ deleteFileAndSetFileHandler (uint32_t invokeId, void* parameter, MmsError mmsErr IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError)); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2218,7 +2425,8 @@ IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -2230,7 +2438,8 @@ IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -2270,7 +2479,8 @@ IedConnection_deleteFileAsync(IedConnection self, IedClientError* error, const c *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -2302,15 +2512,18 @@ IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* erro LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices); - while (logicalDevice != NULL) { + while (logicalDevice) + { ICLogicalDevice* device = (ICLogicalDevice*) logicalDevice->data; - if (strcmp(device->name, logicalDeviceName) == 0) { + if (strcmp(device->name, logicalDeviceName) == 0) + { LinkedList logicalNodeNames = LinkedList_create(); LinkedList variable = LinkedList_getNext(device->variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; if (strchr(variableName, '$') == NULL) @@ -2335,7 +2548,8 @@ addToStringSet(LinkedList set, char* string) { LinkedList element = set; - while (LinkedList_getNext(element) != NULL) { + while (LinkedList_getNext(element) != NULL) + { if (strcmp((char*) LinkedList_getNext(element)->data, string) == 0) return false; @@ -2351,21 +2565,25 @@ addVariablesWithFc(char* fc, char* lnName, LinkedList variables, LinkedList lnDi { LinkedList variable = LinkedList_getNext(variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos != NULL) + { if (memcmp(fcPos + 1, fc, 2) != 0) goto next_element; int lnNameLen = (int)(fcPos - variableName); - if (strncmp(variableName, lnName, lnNameLen) == 0) { + if (strncmp(variableName, lnName, lnNameLen) == 0) + { char* fcEndPos = strchr(fcPos + 1, '$'); - if (fcEndPos != NULL) { + if (fcEndPos != NULL) + { char* nameEndPos = strchr(fcEndPos + 1, '$'); if (nameEndPos == NULL) @@ -2390,7 +2608,8 @@ getLogicalNodeDirectoryLogs(IedConnection self, IedClientError* error, const cha LinkedList journals = MmsConnection_getDomainJournals(mmsCon, &mmsError, logicalDeviceName); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } @@ -2399,17 +2618,19 @@ getLogicalNodeDirectoryLogs(IedConnection self, IedClientError* error, const cha LinkedList journal = LinkedList_getNext(journals); - while (journal != NULL) { - + while (journal) + { char* journalName = (char*) LinkedList_getData(journal); char* logName = strchr(journalName, '$'); - if (logName != NULL) { + if (logName) + { logName[0] = 0; logName += 1; - if (strcmp(journalName, logicalNodeName) == 0) { + if (strcmp(journalName, logicalNodeName) == 0) + { char* log = StringUtils_copyString(logName); LinkedList_add(logs, (void*) log); } @@ -2433,7 +2654,8 @@ getLogicalNodeDirectoryDataSets(IedConnection self, IedClientError* error, const LinkedList dataSets = MmsConnection_getDomainVariableListNames(mmsCon, &mmsError, logicalDeviceName); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } @@ -2442,16 +2664,19 @@ getLogicalNodeDirectoryDataSets(IedConnection self, IedClientError* error, const LinkedList dataSet = LinkedList_getNext(dataSets); - while (dataSet != NULL) { + while (dataSet) + { char* dataSetName = (char*) LinkedList_getData(dataSet); char* lnDataSetName = strchr(dataSetName, '$'); - if (lnDataSetName != NULL) { + if (lnDataSetName) + { lnDataSetName[0] = 0; lnDataSetName += 1; - if (strcmp(dataSetName, logicalNodeName) == 0) { + if (strcmp(dataSetName, logicalNodeName) == 0) + { char* lnDataSet = StringUtils_copyString(lnDataSetName); LinkedList_add(lnDataSets, (void*) lnDataSet); } @@ -2471,7 +2696,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(logicalNodeReference) > 129) { + if (strlen(logicalNodeReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2482,7 +2708,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, char* ldSep = strchr(lnRefCopy, '/'); - if (ldSep == NULL) { + if (ldSep == NULL) + { *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; return NULL; } @@ -2511,10 +2738,12 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2522,25 +2751,28 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } LinkedList lnDirectory = LinkedList_create(); - switch (acsiClass) { - + switch (acsiClass) + { case ACSI_CLASS_DATA_OBJECT: { LinkedList variable = LinkedList_getNext(ld->variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { if (memcmp(fcPos + 1, "RP", 2) == 0) goto next_element; @@ -2552,13 +2784,16 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, int lnNameLen = (int)(fcPos - variableName); - if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) { + if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) + { char* fcEndPos = strchr(fcPos + 1, '$'); - if (fcEndPos != NULL) { + if (fcEndPos) + { char* nameEndPos = strchr(fcEndPos + 1, '$'); - if (nameEndPos == NULL) { + if (nameEndPos == NULL) + { char* dataObjectName = StringUtils_copyString(fcEndPos + 1); if (!addToStringSet(lnDirectory, dataObjectName)) @@ -2579,7 +2814,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, { LinkedList variable = LinkedList_getNext(ld->variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; if (strcmp(variableName, "LLN0$SP$SGCB") == 0) @@ -2622,7 +2858,8 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(logicalNodeReference) > 129) { + if (strlen(logicalNodeReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2639,7 +2876,8 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, char* ldSep = strchr(lnRefCopy, '/'); - if (ldSep == NULL) { + if (ldSep == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2656,10 +2894,12 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2667,7 +2907,8 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2679,15 +2920,18 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, LinkedList lnDirectory = LinkedList_create(); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { int lnNameLen = (int)(fcPos - variableName); - if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) { + if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) + { LinkedList_add(lnDirectory, StringUtils_copyString(fcPos + 1)); } } @@ -2705,7 +2949,8 @@ getDataDirectory(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(dataReference) > 129) { + if (strlen(dataReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2730,7 +2975,8 @@ getDataDirectory(IedConnection self, IedClientError* error, char* logicalNodeNameEnd = strchr(logicalNodeName, '.'); - if (logicalNodeNameEnd == NULL) { + if (logicalNodeNameEnd == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2741,7 +2987,8 @@ getDataDirectory(IedConnection self, IedClientError* error, int dataNamePartLen = (int)strlen(dataNamePart); - if (dataNamePartLen < 1) { + if (dataNamePartLen < 1) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2754,10 +3001,12 @@ getDataDirectory(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2765,7 +3014,8 @@ getDataDirectory(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2774,18 +3024,20 @@ getDataDirectory(IedConnection self, IedClientError* error, LinkedList dataDirectory = LinkedList_create(); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { int lnNameLen = (int)(fcPos - variableName); - if (logicalNodeNameLen == lnNameLen) { - - if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) { - + if (logicalNodeNameLen == lnNameLen) + { + if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) + { /* ok we are in the correct logical node */ /* skip FC */ @@ -2801,10 +3053,10 @@ getDataDirectory(IedConnection self, IedClientError* error, if (remainingLen <= dataNamePartLen) goto next_variable; - if (remainingPart[dataNamePartLen] == '$') { - - if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) { - + if (remainingPart[dataNamePartLen] == '$') + { + if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) + { char* subElementName = remainingPart + dataNamePartLen + 1; char* subElementNameSep = strchr(subElementName, '$'); @@ -2814,7 +3066,8 @@ getDataDirectory(IedConnection self, IedClientError* error, char* elementName; - if (withFc) { + if (withFc) + { int elementNameLen = (int)strlen(subElementName); elementName = (char*) GLOBAL_MALLOC(elementNameLen + 5); @@ -2843,7 +3096,6 @@ getDataDirectory(IedConnection self, IedClientError* error, *error = IED_ERROR_OK; return dataDirectory; - } LinkedList @@ -2864,14 +3116,16 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(dataReference) > 129) { + if (strlen(dataReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } char* fcString = FunctionalConstraint_toString(fc); - if (fcString == NULL) { + if (fcString == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2896,7 +3150,8 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, char* logicalNodeNameEnd = strchr(logicalNodeName, '.'); - if (logicalNodeNameEnd == NULL) { + if (logicalNodeNameEnd == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2907,7 +3162,8 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, int dataNamePartLen = (int)strlen(dataNamePart); - if (dataNamePartLen < 1) { + if (dataNamePartLen < 1) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2920,10 +3176,12 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2931,7 +3189,8 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2940,18 +3199,20 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, LinkedList dataDirectory = LinkedList_create(); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { int lnNameLen = (int)(fcPos - variableName); - if (logicalNodeNameLen == lnNameLen) { - - if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) { - + if (logicalNodeNameLen == lnNameLen) + { + if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) + { /* ok we are in the correct logical node */ /* skip FC */ @@ -2970,10 +3231,10 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, if (remainingLen <= dataNamePartLen) goto next_variable; - if (remainingPart[dataNamePartLen] == '$') { - - if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) { - + if (remainingPart[dataNamePartLen] == '$') + { + if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) + { char* subElementName = remainingPart + dataNamePartLen + 1; char* subElementNameSep = strchr(subElementName, '$'); @@ -3002,7 +3263,6 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, *error = IED_ERROR_OK; return dataDirectory; - } @@ -3023,9 +3283,10 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha const char* itemId; bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3033,17 +3294,20 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } int domainIdLength = (int)strlen(domainId); - if ((strlen(dataSetReference) - domainIdLength - 1) > 32) { + if ((strlen(dataSetReference) - domainIdLength - 1) > 32) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3053,7 +3317,8 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3064,12 +3329,13 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha LinkedList dataSetElement = LinkedList_getNext(dataSetElements); - while (dataSetElement != NULL) { - + while (dataSetElement) + { MmsVariableAccessSpecification* dataSetEntry = MmsMapping_ObjectReferenceToVariableAccessSpec((char*) dataSetElement->data); - if (dataSetEntry == NULL) { + if (dataSetEntry == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto cleanup_list; } @@ -3107,8 +3373,10 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha int dataSetReferenceLength = (int)strlen(dataSetReference); - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3116,16 +3384,18 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha else StringUtils_copyStringMax(itemId, DATA_SET_MAX_NAME_LENGTH + 1, dataSetReference); } - else { - - if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) { + else + { + if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdString = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3135,8 +3405,10 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha StringUtils_replace(itemId, '.', '$'); } } - else { - if (dataSetReferenceLength > 33) { + else + { + if (dataSetReferenceLength > 33) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3166,13 +3438,14 @@ deleteNamedVariableListHandler(uint32_t invokeId, void* parameter, MmsError mmsE IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback; IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { if (success == false) err = IED_ERROR_ACCESS_DENIED; } @@ -3181,7 +3454,8 @@ deleteNamedVariableListHandler(uint32_t invokeId, void* parameter, MmsError mmsE iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3200,9 +3474,10 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons int dataSetReferenceLength = (int)strlen(dataSetReference); - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') - || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3210,17 +3485,18 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons else StringUtils_copyStringMax(itemId, DATA_SET_MAX_NAME_LENGTH + 1, dataSetReference); } - else { - - if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, - domainId) == NULL) { + else + { + if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } const char *itemIdString = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3230,8 +3506,10 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons StringUtils_replace(itemId, '.', '$'); } } - else { - if (dataSetReferenceLength > 33) { + else + { + if (dataSetReferenceLength > 33) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3243,7 +3521,8 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons MmsError mmsError; - if ((domainId == NULL) || (itemId[0] == 0)) { + if ((domainId == NULL) || (itemId[0] == 0)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3259,14 +3538,17 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons call->callbackParameter = parameter; call->invokeId = 0; - if (isAssociationSpecific) { + if (isAssociationSpecific) + { MmsConnection_deleteAssociationSpecificNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, itemId, deleteNamedVariableListHandler, self); } - else { + else + { MmsConnection_deleteNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, deleteNamedVariableListHandler, self); } - if (*error != IED_ERROR_OK) { + if (*error != IED_ERROR_OK) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -3281,13 +3563,14 @@ createDataSetAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback; IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { if (success == false) err = IED_ERROR_ACCESS_DENIED; } @@ -3296,7 +3579,8 @@ createDataSetAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3310,9 +3594,10 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; - goto exit_function; + goto exit_function; } call->callback = handler; @@ -3326,9 +3611,10 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons const char* itemId; bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3336,17 +3622,20 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } int domainIdLength = (int)strlen(domainId); - if ((strlen(dataSetReference) - domainIdLength - 1) > 32) { + if ((strlen(dataSetReference) - domainIdLength - 1) > 32) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3356,7 +3645,8 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3365,12 +3655,13 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons LinkedList dataSetElement = LinkedList_getNext(dataSetElements); - while (dataSetElement != NULL) { - + while (dataSetElement) + { MmsVariableAccessSpecification* dataSetEntry = MmsMapping_ObjectReferenceToVariableAccessSpec((char*) dataSetElement->data); - if (dataSetEntry == NULL) { + if (dataSetEntry == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto cleanup_list; } @@ -3380,11 +3671,13 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons dataSetElement = LinkedList_getNext(dataSetElement); } - if (isAssociationSpecific) { + if (isAssociationSpecific) + { MmsConnection_defineNamedVariableListAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, dataSetEntries, createDataSetAsyncHandler, self); } - else { + else + { MmsConnection_defineNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self); } @@ -3397,8 +3690,10 @@ cleanup_list: exit_function: - if (*error != IED_ERROR_OK) { - iedConnection_releaseOutstandingCall(self, call); + if (*error != IED_ERROR_OK) + { + if (call) + iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -3422,8 +3717,10 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3431,17 +3728,20 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRef = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3451,7 +3751,8 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con itemId = itemIdRefInBuffer; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3467,13 +3768,14 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con entries = MmsConnection_readNamedVariableListDirectory(self->connection, &mmsError, domainId, itemId, &deletable); - if (mmsError == MMS_ERROR_NONE) { - + if (mmsError == MMS_ERROR_NONE) + { LinkedList entry = LinkedList_getNext(entries); dataSetMembers = LinkedList_create(); - while (entry) { + while (entry) + { MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(entry); char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); @@ -3504,17 +3806,20 @@ getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mms IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { + if (call) + { LinkedList dataSetMembers = NULL; if (mmsError != MMS_ERROR_NONE) err = iedConnection_mapMmsErrorToIedError(mmsError); - if (specs) { + if (specs) + { dataSetMembers = LinkedList_create(); LinkedList specElem = LinkedList_getNext(specs); - while (specElem) { + while (specElem) + { MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(specElem); char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); @@ -3545,7 +3850,8 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -3562,8 +3868,10 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3571,17 +3879,20 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRef = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3591,7 +3902,8 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error itemId = itemIdRefInBuffer; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3606,12 +3918,14 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error exit_function: - if (*error != IED_ERROR_OK) { + if (*error != IED_ERROR_OK) + { iedConnection_releaseOutstandingCall(self, call); return 0; } - else { + else + { return call->invokeId; } } @@ -3628,9 +3942,10 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3638,17 +3953,20 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3659,7 +3977,8 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3675,18 +3994,21 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const dataSetVal = MmsConnection_readNamedVariableListValues(self->connection, &mmsError, domainId, itemId, true); - if (dataSetVal == NULL) { + if (dataSetVal == NULL) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); goto exit_function; } else *error = IED_ERROR_OK; - if (dataSet == NULL) { + if (dataSet == NULL) + { dataSet = ClientDataSet_create(dataSetReference); ClientDataSet_setDataSetValues(dataSet, dataSetVal); } - else { + else + { MmsValue* dataSetValues = ClientDataSet_getValues(dataSet); MmsValue_update(dataSetValues, dataSetVal); MmsValue_delete(dataSetVal); @@ -3703,32 +4025,38 @@ getDataSetHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_ReadDataSetHandler handler = (IedConnection_ReadDataSetHandler) call->callback; ClientDataSet dataSet = (ClientDataSet) call->specificParameter; char* dataSetReference = (char*) call->specificParameter2.pointer; - if (value != NULL) { - - if (dataSet == NULL) { + if (value) + { + if (dataSet == NULL) + { dataSet = ClientDataSet_create(dataSetReference); - ClientDataSet_setDataSetValues(dataSet, value); - GLOBAL_FREEMEM(dataSetReference); + ClientDataSet_setDataSetValues(dataSet, MmsValue_clone(value)); } - else { + else + { MmsValue* dataSetValues = ClientDataSet_getValues(dataSet); MmsValue_update(dataSetValues, value); - MmsValue_delete(value); } + + MmsValue_delete(value); } + if (dataSetReference) + GLOBAL_FREEMEM(dataSetReference); + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), dataSet); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3746,9 +4074,10 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3756,17 +4085,20 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3777,14 +4109,16 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -3809,8 +4143,8 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { - + if (err != MMS_ERROR_NONE) + { GLOBAL_FREEMEM(call->specificParameter2.pointer); iedConnection_releaseOutstandingCall(self, call); @@ -3833,9 +4167,10 @@ IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, cons bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3843,17 +4178,20 @@ IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, cons else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3864,7 +4202,8 @@ IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, cons itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3886,15 +4225,16 @@ writeDataSetHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, Li IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_WriteDataSetHandler handler = (IedConnection_WriteDataSetHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), accessResults); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3912,9 +4252,10 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3922,17 +4263,20 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3943,14 +4287,16 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -3964,7 +4310,8 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; @@ -3986,8 +4333,8 @@ IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const ch char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName) + { logName[0] = 0; logName++; @@ -4003,18 +4350,19 @@ IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const ch MmsValue_delete(startTimeMms); MmsValue_delete(endTimeMms); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } else return journalEntries; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } - } static void @@ -4024,15 +4372,16 @@ readJournalHandler(uint32_t invokeId, void* parameter, MmsError err, LinkedList IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_QueryLogHandler handler = (IedConnection_QueryLogHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), journalEntries, moreFollows); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -4049,14 +4398,15 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName != NULL) + { logName[0] = 0; logName++; IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -4080,18 +4430,19 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } return call->invokeId; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } - } uint32_t @@ -4105,14 +4456,15 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName) + { logName[0] = 0; logName++; IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -4132,14 +4484,16 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } return call->invokeId; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -4158,8 +4512,8 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName) + { logName[0] = 0; logName++; @@ -4171,14 +4525,16 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha MmsValue_delete(timeStampMms); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } else return journalEntries; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -4252,4 +4608,3 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self) { return self->lastModified; } - diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index fa9a13c0..2ba1d3f7 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -768,7 +768,6 @@ MmsMapping_ObjectReferenceToVariableAccessSpec(char* objectReference) return accessSpec; } - static int getNumberOfDigits(int value) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index c3a174fe..5946bd00 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1,7 +1,7 @@ /* * iec61850_client.h * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -162,6 +162,9 @@ typedef enum { /** Received an invalid response message from the server */ IED_ERROR_MALFORMED_MESSAGE = 34, + /** Service was not executed because required resource is still in use */ + IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35, + /** Service not implemented */ IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98, @@ -169,6 +172,14 @@ typedef enum { IED_ERROR_UNKNOWN = 99 } IedClientError; +/** + * \brief Convert error value to string + * + * \return string constant representing the error + */ +LIB61850_API const char* +IedClientError_toString(IedClientError err); + /************************************************** * Connection creation and destruction **************************************************/ @@ -255,6 +266,16 @@ IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, in LIB61850_API void IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs); +/** + * \brief Set the maximum number outstanding calls allowed for this connection + * + * \param self the connection object + * \param calling the maximum outstanding calls allowed by the caller (client) + * \param called the maximum outstanding calls allowed by the called endpoint (server) + */ +LIB61850_API void +IedConnection_setMaxOutstandingCalls(IedConnection self, int calling, int called); + /** * \brief set the request timeout in ms * @@ -1151,15 +1172,6 @@ typedef int ReasonForInclusion; /** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */ #define IEC61850_REASON_UNKNOWN 32 -#define REASON_NOT_INCLUDED IEC61850_REASON_NOT_INCLUDED -#define REASON_DATA_CHANGE IEC61850_REASON_DATA_CHANGE -#define REASON_QUALITY_CHANGE IEC61850_REASON_QUALITY_CHANGE -#define REASON_DATA_UPDATE IEC61850_REASON_DATA_UPDATE -#define REASON_INTEGRITY IEC61850_REASON_INTEGRITY -#define REASON_GI IEC61850_REASON_GI -#define REASON_UNKNOWN IEC61850_REASON_UNKNOWN - - /* Element encoding mask values for ClientReportControlBlock */ /** include the report ID into the setRCB request */ @@ -1256,9 +1268,11 @@ typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report); * Otherwise the internal data structures storing the received data set values will not be updated * correctly. * - * When replacing a report handler you only have to call this function. There is no separate call to + * \note Replacing a report handler you only have to call this function. There is no separate call to * IedConnection_uninstallReportHandler() required. * + * \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock. + * * \param self the connection object * \param rcbReference object reference of the report control block * \param rptId a string that identifies the report. If the rptId is not available then the @@ -1273,6 +1287,8 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference, /** * \brief uninstall a report handler function for the specified report control block (RCB) * + * \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock. + * * \param self the connection object * \param rcbReference object reference of the report control block */ @@ -2467,20 +2483,6 @@ IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool LIB61850_API LinkedList /**/ IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, const char* logicalDeviceName); -typedef enum { - ACSI_CLASS_DATA_OBJECT, - ACSI_CLASS_DATA_SET, - ACSI_CLASS_BRCB, - ACSI_CLASS_URCB, - ACSI_CLASS_LCB, - ACSI_CLASS_LOG, - ACSI_CLASS_SGCB, - ACSI_CLASS_GoCB, - ACSI_CLASS_GsCB, - ACSI_CLASS_MSVCB, - ACSI_CLASS_USVCB -} ACSIClass; - /** * \brief returns a list of all MMS variables that are children of the given logical node * diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 9a364656..53aa6e86 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -55,6 +55,21 @@ typedef struct { uint8_t dstAddress[6]; } PhyComAddress; +/** IEC 61850 ACSI classes */ +typedef enum { + ACSI_CLASS_DATA_OBJECT, + ACSI_CLASS_DATA_SET, + ACSI_CLASS_BRCB, + ACSI_CLASS_URCB, + ACSI_CLASS_LCB, + ACSI_CLASS_LOG, + ACSI_CLASS_SGCB, + ACSI_CLASS_GoCB, + ACSI_CLASS_GsCB, + ACSI_CLASS_MSVCB, + ACSI_CLASS_USVCB +} ACSIClass; + /** * \brief Control model (represented by "ctlModel" attribute) */ diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 3db3235e..6dc69524 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -3,7 +3,7 @@ * * IEC 61850 server API for libiec61850. * - * Copyright 2013-2023 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -815,7 +815,9 @@ LIB61850_API void IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter); /** - * \brief Ignore all requests from clients + * \brief Ignore all requests from clients (for testing purposes) + * + * NOTE: This function will block all client requests on MMS layer * * \param self the instance of IedServer to configure. * \param enable when true all requests from clients will be ignored @@ -1447,7 +1449,7 @@ LIB61850_API DataObject* ControlAction_getControlObject(ControlAction self); /** - * \brief Gets the time of the control, if it's a timeActivatedControl, returns 0, if it's not. + * \brief Gets the time of the control (attribute "operTm"), if it's a timeActivatedControl, returns 0, if it's not. * * \param self the control action instance * @@ -1456,6 +1458,16 @@ ControlAction_getControlObject(ControlAction self); LIB61850_API uint64_t ControlAction_getControlTime(ControlAction self); +/** + * \brief Gets the time (attribute "T") of the last received control action (Oper or Select) + * + * \param self the control action instance + * + * \return the time of the last received control action + */ +LIB61850_API Timestamp* +ControlAction_getT(ControlAction self); + /** * \brief Control model callback to perform the static tests (optional). * @@ -1845,6 +1857,19 @@ LIB61850_API void IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter); +/** + * \brief Install a WriteAccessHandler for all data attributes of a data object with a specific FC + * + * \param self the instance of IedServer to operate on. + * \param dataObject the data object to monitor + * \param fc the functional constraint to monitor + * \param handler the callback function that is invoked if a client tries to write to + * the monitored data attribute. + * \param parameter a user provided parameter that is passed to the WriteAccessHandler when called. +*/ +LIB61850_API void +IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter); + typedef enum { ACCESS_POLICY_ALLOW, ACCESS_POLICY_DENY @@ -1892,120 +1917,119 @@ typedef MmsDataAccessError LIB61850_API void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); +typedef enum { + DATASET_CREATE, + DATASET_DELETE, + DATASET_READ, + DATASET_WRITE, + DATASET_GET_DIRECTORY +} IedServer_DataSetOperation; + /** - * \brief Callback that is called in case of RCB access to give the user the opportunity to block or allow the operation + * \brief Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory) * * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE * * \param parameter user provided parameter - * \param rcb affected report control block * \param connection client connection that is involved - * \param operation one of the following operation event types: RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER + * \param operation one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY + * + * \return true to allow operation, false to deny operation */ typedef bool -(*IedServer_RCBAccessHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation); +(*IedServer_DataSetAccessHandler) (void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef); /** - * \brief Set a handler to control read and write access to report control blocks (RCBs) - * - * \param self the instance of IedServer to operate on. - * \param handler the event handler to be used + * \brief Set a handler to control access to a dataset (create, delete, read, write, list directory) + * + * \param handler the callback handler to be used * \param parameter a user provided parameter that is passed to the handler. */ LIB61850_API void -IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter); +IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter); typedef enum { - LCB_EVENT_GET_PARAMETER, - LCB_EVENT_SET_PARAMETER -} IedServer_LCBEventType; + DIRECTORY_CAT_LD_LIST, + DIRECTORY_CAT_DATA_LIST, + DIRECTORY_CAT_DATASET_LIST, + DIRECTORY_CAT_LOG_LIST +} IedServer_DirectoryCategory; -/** - * \brief Callback that is called in case of LCB access to give the user the opportunity to block or allow the operation - * - * - * \param parameter user provided parameter - * \param lcb affected log control block - * \param connection client connection that is involved - * \param operation one of the following operation event types: LCB_EVENT_GET_PARAMETER, LCB_EVENT_SET_PARAMETER - */ typedef bool -(*IedServer_LCBAccessHandler) (void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation); +(*IedServer_DirectoryAccessHandler) (void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice); -/** - * \brief Set a handler to control read and write access to log control blocks (LCBs) - * - * \param self the instance of IedServer to operate on. - * \param handler the event handler to be used - * \param parameter a user provided parameter that is passed to the handler. - */ LIB61850_API void -IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter); +IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter); /** - * \brief Callback that is called when the client is trying to read log data - * + * \brief Callback that is called when a client is invoking a list objects service + * + * This callback can be used to control the list object access to specific objects and is called for each object that are subject to a client request. + * * \param parameter user provided parameter - * \param logRef object reference of the log * \param connection client connection that is involved - * - * \return true to allow read log data, false to deny + * \param acsiClass the ACSI class of the object + * \param ld the logical device of the object + * \param ln the logical node of the object + * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + * \param subObjectName the name of a sub element of an object or NULL + * \param fc the functional constraint of the object of IEC61850_FC_NONE when the object has no FC. + * + * \return true to include the object in the service response, otherwise false */ typedef bool -(*IedServer_LogAccessHandler) (void* parameter, const char* logRef, ClientConnection connection); +(*IedServer_ListObjectsAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc); /** - * \brief Set a handler control access to a log (read log data) - * + * \brief Set a handler to control which objects are return by the list objects services + * * \param handler the callback handler to be used * \param parameter a user provided parameter that is passed to the handler. */ LIB61850_API void -IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter); +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter); typedef enum { - DATASET_CREATE, - DATASET_DELETE, - DATASET_READ, - DATASET_WRITE, - DATASET_GET_DIRECTORY -} IedServer_DataSetOperation; + IEC61850_CB_ACCESS_TYPE_READ, + IEC61850_CB_ACCESS_TYPE_WRITE +} IedServer_ControlBlockAccessType; /** - * \brief Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory) - * - * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE - * + * \brief Callback that is called when a client is invoking a read or write service to a control block or log + * + * This callback can be used to control the read and write access to control blocks and logs (SGCB, LCBs, URCBs, BRCBs, GoCBs, SVCBs, logs) + * * \param parameter user provided parameter * \param connection client connection that is involved - * \param operation one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY - * - * \return true to allow operation, false to deny operation + * \param acsiClass the ACSI class of the object + * \param ld the logical device of the object + * \param ln the logical node of the object + * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + * \param subObjectName the name of a sub element of an object or NULL + * \param accessType access type (read=IEC61850_CB_ACCESS_TYPE_READ or write=IEC61850_CB_ACCESS_TYPE_WRITE) + * + * \return true to include the object in the service response, otherwise false */ typedef bool -(*IedServer_DataSetAccessHandler) (void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef); +(*IedServer_ControlBlockAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType); /** - * \brief Set a handler to control access to a dataset (create, delete, read, write, list directory) - * + * \brief Set a handler to control read and write access to control blocks and logs + * * \param handler the callback handler to be used * \param parameter a user provided parameter that is passed to the handler. */ LIB61850_API void -IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter); - -typedef enum { - DIRECTORY_CAT_LD_LIST, - DIRECTORY_CAT_DATA_LIST, - DIRECTORY_CAT_DATASET_LIST, - DIRECTORY_CAT_LOG_LIST -} IedServer_DirectoryCategory; - -typedef bool -(*IedServer_DirectoryAccessHandler) (void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice); +IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter); +/** + * \brief Temporarily ignore read requests (for testing purposes) + * + * \param self the instance of IedServer to operate on. + * \param ignore true to ignore read requests, false to handle read requests. +*/ LIB61850_API void -IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter); +IedServer_ignoreReadAccess(IedServer self, bool ignore); /**@}*/ diff --git a/src/iec61850/inc_private/control.h b/src/iec61850/inc_private/control.h index 66cb6706..adf56731 100644 --- a/src/iec61850/inc_private/control.h +++ b/src/iec61850/inc_private/control.h @@ -1,7 +1,7 @@ /* * control.h * - * Copyright 2013-2019 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -90,6 +90,8 @@ struct sControlObject MmsValue* origin; MmsValue* timestamp; + Timestamp T; + MmsValue* ctlNumSt; MmsValue* originSt; diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h index e571ca8e..52467747 100644 --- a/src/iec61850/inc_private/ied_connection_private.h +++ b/src/iec61850/inc_private/ied_connection_private.h @@ -1,7 +1,7 @@ /* * ied_connection_private.h * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -34,14 +34,16 @@ typedef struct sIedConnectionOutstandingCall* IedConnectionOutstandingCall; -struct sIedConnectionOutstandingCall { +struct sIedConnectionOutstandingCall +{ bool used; uint32_t invokeId; void* callback; void* callbackParameter; void* specificParameter; /* function/service specific parameter */ - union { + union + { void* pointer; struct { uint32_t originalInvokeId; @@ -69,6 +71,7 @@ struct sIedConnection Semaphore outstandingCallsLock; IedConnectionOutstandingCall outstandingCalls; + int maxOutstandingCalled; IedConnectionClosedHandler connectionCloseHandler; void* connectionClosedParameter; @@ -81,7 +84,8 @@ struct sIedConnection uint8_t timeQuality; }; -struct sClientReportControlBlock { +struct sClientReportControlBlock +{ char* objectReference; bool isBuffered; diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index d5555659..8dc5fede 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -81,10 +81,11 @@ struct sIedServer uint8_t timeQuality; /* user settable time quality for internally updated times */ + bool ignoreReadAccess; /* when true don't answer read request (for test purposes) */ + bool running; }; - LIB61850_INTERNAL IEC61850_ServiceError private_IedServer_convertMmsDataAccessErrorToServiceError(MmsDataAccessError mmsError); diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h index 82ef712b..79c3f246 100644 --- a/src/iec61850/inc_private/logging.h +++ b/src/iec61850/inc_private/logging.h @@ -124,7 +124,7 @@ LIB61850_INTERNAL MmsValue* LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError -LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection); #endif /* LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_ */ diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index 63ec5742..340a2d81 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -149,7 +149,7 @@ LIB61850_INTERNAL void MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* paramter); LIB61850_INTERNAL MmsDataAccessError -Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection); LIB61850_INTERNAL MmsValue* diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 51714f11..1830b87c 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -338,20 +338,17 @@ struct sMmsMapping { IedServer_RCBEventHandler rcbEventHandler; void* rcbEventHandlerParameter; - IedServer_RCBAccessHandler rcbAccessHandler; - void* rcbAccessHandlerParameter; - - IedServer_LCBAccessHandler lcbAccessHandler; - void* lcbAccessHandlerParameter; - - IedServer_LogAccessHandler logAccessHandler; - void* logAccessHandlerParameter; - IedServer_DataSetAccessHandler dataSetAccessHandler; void* dataSetAccessHandlerParameter; IedServer_DirectoryAccessHandler directoryAccessHandler; void* directoryAccessHandlerParameter; + + IedServer_ListObjectsAccessHandler listObjectsAccessHandler; + void* listObjectsAccessHandlerParameter; + + IedServer_ControlBlockAccessHandler controlBlockAccessHandler; + void* controlBlockAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/inc_private/mms_sv.h b/src/iec61850/inc_private/mms_sv.h index c150a641..a0bb0ded 100644 --- a/src/iec61850/inc_private/mms_sv.h +++ b/src/iec61850/inc_private/mms_sv.h @@ -38,10 +38,10 @@ LIBIEC61850_SV_createSVControlBlocks(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int svCount, bool unicast); LIB61850_INTERNAL MmsValue* -LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig , MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError -LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection); LIB61850_INTERNAL void diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index 558343c2..eddeb2d1 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -126,7 +126,7 @@ LIB61850_INTERNAL void ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, bool modelLocked); LIB61850_INTERNAL MmsValue* -ReportControl_getRCBValue(ReportControl* rc, char* elementName); +ReportControl_getRCBValue(ReportControl* rc, const char* elementName); LIB61850_INTERNAL MmsVariableSpecification* Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, @@ -137,7 +137,7 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int reportsCount); LIB61850_INTERNAL MmsDataAccessError -Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, +Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, const char* elementName, MmsValue* value, MmsServerConnection connection); LIB61850_INTERNAL bool diff --git a/src/iec61850/server/impl/client_connection.c b/src/iec61850/server/impl/client_connection.c index dbca770a..6eac160d 100644 --- a/src/iec61850/server/impl/client_connection.c +++ b/src/iec61850/server/impl/client_connection.c @@ -1,7 +1,7 @@ /* * client_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,8 +32,8 @@ #include "libiec61850_platform_includes.h" -struct sClientConnection { - +struct sClientConnection +{ #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore tasksCountMutex; #endif @@ -47,7 +47,8 @@ private_ClientConnection_create(void* serverConnectionHandle) { ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection)); - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) self->tasksCountMutex = Semaphore_create(1); #endif @@ -62,7 +63,8 @@ private_ClientConnection_create(void* serverConnectionHandle) void private_ClientConnection_destroy(ClientConnection self) { - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_destroy(self->tasksCountMutex); #endif @@ -123,7 +125,6 @@ private_ClientConnection_getServerConnectionHandle(ClientConnection self) return self->serverConnectionHandle; } - const char* ClientConnection_getPeerAddress(ClientConnection self) { diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 27d40624..2acecedd 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -540,17 +540,18 @@ updateDataSetsWithCachedValues(IedServer self) } } - IedServer IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration) { IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer)); - if (self) { + if (self) + { self->model = dataModel; self->running = false; self->localIpAddress = NULL; + self->ignoreReadAccess = false; #if (CONFIG_IEC61850_EDITION_1 == 1) self->edition = IEC_61850_EDITION_1; @@ -561,7 +562,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) self->logServiceEnabled = true; - if (serverConfiguration) { + if (serverConfiguration) + { self->logServiceEnabled = serverConfiguration->enableLogService; self->edition = serverConfiguration->edition; } @@ -574,7 +576,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ #if (CONFIG_IEC61850_REPORT_SERVICE == 1) - if (serverConfiguration) { + if (serverConfiguration) + { self->reportBufferSizeBRCBs = serverConfiguration->reportBufferSize; self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs; self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB; @@ -582,7 +585,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes; self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable; } - else { + else + { self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->enableOwnerForRCB = false; @@ -602,11 +606,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #endif #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (serverConfiguration) { + if (serverConfiguration) + { self->enableEditSG = serverConfiguration->enableEditSG; self->hasSGCBResvTms = serverConfiguration->enableResvTmsForSGCB; } - else { + else + { self->enableEditSG = true; self->hasSGCBResvTms = true; } @@ -614,14 +620,15 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio self->mmsMapping = MmsMapping_create(dataModel, self); - if (self->mmsMapping) { - + if (self->mmsMapping) + { self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration); #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (serverConfiguration) { + if (serverConfiguration) + { MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService); MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService); MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets); @@ -668,7 +675,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio IedServer_setTimeQuality(self, true, false, false, 10); } - else { + else + { IedServer_destroy(self); self = NULL; } @@ -696,27 +704,6 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, self->mmsMapping->rcbEventHandlerParameter = parameter; } -void -IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter) -{ - self->mmsMapping->rcbAccessHandler = handler; - self->mmsMapping->rcbAccessHandlerParameter = parameter; -} - -void -IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter) -{ - self->mmsMapping->lcbAccessHandler = handler; - self->mmsMapping->lcbAccessHandlerParameter = parameter; -} - -void -IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter) -{ - self->mmsMapping->logAccessHandler = handler; - self->mmsMapping->logAccessHandlerParameter = parameter; -} - void IedServer_destroy(IedServer self) { @@ -1700,6 +1687,38 @@ IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* da } } +void +IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter) +{ + if (dataObject == NULL) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: IedServer_handlerWriteAccessForDataObject - dataObject == NULL!\n"); + } + else + { + ModelNode* childElement = dataObject->firstChild; + + while (childElement) + { + if (childElement->modelType == DataAttributeModelType) + { + DataAttribute* dataAttribute = (DataAttribute*) childElement; + + if (dataAttribute->fc == fc) + { + IedServer_handleWriteAccessForComplexAttribute(self, dataAttribute, handler, parameter); + } + } + else if (childElement->modelType == DataObjectModelType) + { + IedServer_handleWriteAccessForDataObject(self, (DataObject*) childElement, fc, handler, parameter); + } + + childElement = childElement->sibling; + } + } +} + void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter) { @@ -1967,3 +1986,23 @@ IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHan self->mmsMapping->directoryAccessHandler = handler; self->mmsMapping->directoryAccessHandlerParameter = parameter; } + +void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter) +{ + self->mmsMapping->listObjectsAccessHandler = handler; + self->mmsMapping->listObjectsAccessHandlerParameter = parameter; +} + +void +IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter) +{ + self->mmsMapping->controlBlockAccessHandler = handler; + self->mmsMapping->controlBlockAccessHandlerParameter = parameter; +} + +void +IedServer_ignoreReadAccess(IedServer self, bool ignore) +{ + self->ignoreReadAccess = ignore; +} diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index de2b6f89..07ef4324 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -74,7 +74,8 @@ ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsM static MmsValue* getOperParameterCtlNum(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 7) return MmsValue_getElement(operParameters, 3); else if (MmsValue_getArraySize(operParameters) == 6) @@ -87,7 +88,8 @@ getOperParameterCtlNum(MmsValue* operParameters) static MmsValue* getCancelParameterCtlNum(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 6) return MmsValue_getElement(operParameters, 3); else if (MmsValue_getArraySize(operParameters) == 5) @@ -100,7 +102,8 @@ getCancelParameterCtlNum(MmsValue* operParameters) static MmsValue* getCancelParameterOrigin(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 6) return MmsValue_getElement(operParameters, 2); else if (MmsValue_getArraySize(operParameters) == 5) @@ -166,6 +169,7 @@ getCancelParameterTest(MmsValue* operParameters) return NULL; } +/* access the MmsValue of Oper.T or SBOw.T */ static MmsValue* getOperParameterTime(MmsValue* operParameters) { @@ -179,7 +183,7 @@ getOperParameterTime(MmsValue* operParameters) timeParameter = MmsValue_getElement(operParameters, 3); } - if (timeParameter != NULL) + if (timeParameter) if ((MmsValue_getType(timeParameter) == MMS_UTC_TIME) || (MmsValue_getType(timeParameter) == MMS_BINARY_TIME)) return timeParameter; @@ -211,11 +215,12 @@ getCancelParameterTime(MmsValue* operParameters) static void copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject, IEC61850_ServiceType serviceType) { - if (controlObject->ctlVal) { - + if (controlObject->ctlVal) + { ControlTrkInstance trkInst = NULL; - switch (controlObject->cdc) { + switch (controlObject->cdc) + { case CST_SPCTRK: trkInst = self->spcTrk; break; @@ -247,7 +252,8 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject break; } - if (trkInst) { + if (trkInst) + { if (trkInst->ctlVal) MmsValue_update(trkInst->ctlVal->mmsValue, controlObject->ctlVal); @@ -265,16 +271,20 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject MmsValue* operVal = NULL; - if (serviceType == IEC61850_SERVICE_TYPE_SELECT_WITH_VALUES) { + if (serviceType == IEC61850_SERVICE_TYPE_SELECT_WITH_VALUES) + { if (controlObject->sbow) operVal = controlObject->sbow; } - else if (serviceType == IEC61850_SERVICE_TYPE_OPERATE) { + else if (serviceType == IEC61850_SERVICE_TYPE_OPERATE) + { if (controlObject->oper) operVal = controlObject->oper; } - else if (serviceType == IEC61850_SERVICE_TYPE_CANCEL) { - if (controlObject->cancel) { + else if (serviceType == IEC61850_SERVICE_TYPE_CANCEL) + { + if (controlObject->cancel) + { operVal = controlObject->cancel; if (trkInst->Test) { MmsValue_update(trkInst->Test->mmsValue, getCancelParameterTest(operVal)); @@ -286,8 +296,8 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject } } - if (operVal) { - + if (operVal) + { if (trkInst->Test) { MmsValue_update(trkInst->Test->mmsValue, getOperParameterTest(operVal)); } @@ -300,7 +310,6 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject MmsValue_update(trkInst->T->mmsValue, getOperParameterTime(operVal)); } } - } } } @@ -310,8 +319,8 @@ convertCheckHandlerResultToServiceError(CheckHandlerResult controlHandlerResult) { IEC61850_ServiceError serviceError; - switch (controlHandlerResult) { - + switch (controlHandlerResult) + { case CONTROL_HARDWARE_FAULT: serviceError = IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT; break; @@ -352,7 +361,8 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject ServiceTrkInstance trkInst = NULL; - if (controlObject->ctlVal) { + if (controlObject->ctlVal) + { switch(controlObject->cdc) { case CST_SPCTRK: @@ -389,7 +399,8 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject } } - if (trkInst) { + if (trkInst) + { if (trkInst->serviceType) MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); @@ -460,7 +471,8 @@ getState(ControlObject* self) static void setStSeld(ControlObject* self, bool value) { - if (self->stSeld) { + if (self->stSeld) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->pendingEventsLock); #endif @@ -479,7 +491,8 @@ setStSeld(ControlObject* self, bool value) static void updateSboTimeoutValue(ControlObject* self) { - if (self->sboTimeout != NULL) { + if (self->sboTimeout != NULL) + { uint32_t sboTimeoutVal = MmsValue_toInt32(self->sboTimeout); if (DEBUG_IED_SERVER) @@ -506,7 +519,8 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne updateNextControlTimeout(mmsMapping, selectTime); - if (self->selectStateChangedHandler) { + if (self->selectStateChangedHandler) + { self->selectStateChangedHandler((ControlAction) self, self->selectStateChangedHandlerParameter, true, @@ -517,7 +531,8 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne static void unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping) { - if (getState(self) != STATE_UNSELECTED) { + if (getState(self) != STATE_UNSELECTED) + { setState(self, STATE_UNSELECTED); setStSeld(self, false); @@ -525,7 +540,8 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* /* trigger timeout check in next cycle to update the next timeout value */ mmsMapping->nextControlTimeout = 0; - if (self->selectStateChangedHandler) { + if (self->selectStateChangedHandler) + { self->selectStateChangedHandler((ControlAction) self, self->selectStateChangedHandlerParameter, false, @@ -540,11 +556,14 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* static void checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMapping) { - if ((self->ctlModel == 2) || (self->ctlModel == 4)) { - - if (getState(self) == STATE_READY) { - if (self->selectTimeout > 0) { - if (currentTime > (self->selectTime + self->selectTimeout)) { + if ((self->ctlModel == 2) || (self->ctlModel == 4)) + { + if (getState(self) == STATE_READY) + { + if (self->selectTimeout > 0) + { + if (currentTime > (self->selectTime + self->selectTimeout)) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: select-timeout (timeout-val = %u) for control %s/%s.%s\n", self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name); @@ -562,7 +581,8 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMap static void setOpRcvd(ControlObject* self, bool value) { - if (self->opRcvd) { + if (self->opRcvd) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->pendingEventsLock); #endif @@ -581,13 +601,16 @@ setOpRcvd(ControlObject* self, bool value) static void setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) { - if (self->opOk) { + if (self->opOk) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->pendingEventsLock); #endif - if (value) { - if (self->tOpOk) { + if (value) + { + if (self->tOpOk) + { MmsValue* timestamp = self->tOpOk->mmsValue; MmsValue_setUtcTimeMsEx(timestamp, currentTimeInMs, self->iedServer->timeQuality); @@ -607,7 +630,8 @@ setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) static bool isSboClassOperateOnce(ControlObject* self) { - if (self->sboClass != NULL) { + if (self->sboClass != NULL) + { if (MmsValue_toInt32(self->sboClass) == 1) return false; else @@ -620,8 +644,8 @@ isSboClassOperateOnce(ControlObject* self) static MmsValue* getOperParameterOperTime(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { - + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 7) return MmsValue_getElement(operParameters, 1); } @@ -659,12 +683,14 @@ exitControlTask(ControlObject* self) static void abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason, MmsMapping* mmsMapping) { - if ((self->ctlModel == 2) || (self->ctlModel == 4)) { - - if (unconditional) { + if ((self->ctlModel == 2) || (self->ctlModel == 4)) + { + if (unconditional) + { unselectObject(self, reason, mmsMapping); } - else { + else + { if (isSboClassOperateOnce(self)) unselectObject(self, reason, mmsMapping); else @@ -694,7 +720,8 @@ operateControl(ControlObject* self, MmsValue* value, uint64_t currentTime, bool static void resetAddCause(ControlObject* self) { - if (self) { + if (self) + { self->addCauseValue = ADD_CAUSE_UNKNOWN; MmsValue_setInt32(self->addCause, self->addCauseValue); @@ -710,8 +737,8 @@ executeStateMachine: state = getState(controlObject); - switch (state) { - + switch (state) + { case STATE_WAIT_FOR_SELECT: { controlObject->isSelect = 1; @@ -724,12 +751,14 @@ executeStateMachine: controlObject->isSelect = 0; - if (checkHandlerResult != CONTROL_WAITING_FOR_SELECT) { - - if (controlObject->ctlModel == 2) { + if (checkHandlerResult != CONTROL_WAITING_FOR_SELECT) + { + if (controlObject->ctlModel == 2) + { LinkedList values = LinkedList_create(); - if (checkHandlerResult == CONTROL_ACCEPTED) { + if (checkHandlerResult == CONTROL_ACCEPTED) + { LinkedList_add(values, controlObject->sbo); selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self); @@ -738,7 +767,8 @@ executeStateMachine: updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else { + else + { LinkedList_add(values, &emptyString); setState(controlObject, STATE_UNSELECTED); @@ -753,9 +783,10 @@ executeStateMachine: LinkedList_destroyStatic(values); } - else if (controlObject->ctlModel == 4) { - if (checkHandlerResult == CONTROL_ACCEPTED) { - + else if (controlObject->ctlModel == 4) + { + if (checkHandlerResult == CONTROL_ACCEPTED) + { selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self); if (controlObject->ctlNumSt) @@ -773,8 +804,8 @@ executeStateMachine: if (DEBUG_IED_SERVER) printf("IED_SERVER: SBOw - selected successful\n"); } - else { - + else + { setState(controlObject, STATE_UNSELECTED); ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "SBOw", @@ -793,13 +824,15 @@ executeStateMachine: printf("IED_SERVER: SBOw - select rejected by application!\n"); } } - else { + else + { /* ERROR: invalid internal state! */ setState(controlObject, STATE_WAIT_FOR_SELECT); } } - else { + else + { updateNextControlTimeout(self, Hal_getTimeInMs() + 100); } @@ -818,19 +851,23 @@ executeStateMachine: controlObject->errorValue = CONTROL_ERROR_NO_ERROR; controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK; - if (controlObject->waitForExecutionHandler != NULL) { + if (controlObject->waitForExecutionHandler != NULL) + { dynamicCheckResult = controlObject->waitForExecutionHandler((ControlAction) controlObject, controlObject->waitForExecutionHandlerParameter, controlObject->ctlVal, controlObject->testMode, controlObject->synchroCheck); } - if (dynamicCheckResult == CONTROL_RESULT_FAILED) { - if ((controlObject->errorValue != CONTROL_ERROR_NO_ERROR) || (controlObject->addCauseValue != ADD_CAUSE_UNKNOWN)) { + if (dynamicCheckResult == CONTROL_RESULT_FAILED) + { + if ((controlObject->errorValue != CONTROL_ERROR_NO_ERROR) || (controlObject->addCauseValue != ADD_CAUSE_UNKNOWN)) + { ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper", controlObject->errorValue, controlObject->addCauseValue, controlObject->ctlNum, controlObject->origin, false); } - if (!isTimeActivatedControl) { + if (!isTimeActivatedControl) + { MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED, true); @@ -838,7 +875,8 @@ executeStateMachine: updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_OPERATE, IEC61850_SERVICE_ERROR_ACCESS_NOT_ALLOWED_IN_CURRENT_STATE); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else { + else + { #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_TIME_ACTIVATED_OPERATE, IEC61850_SERVICE_ERROR_ACCESS_NOT_ALLOWED_IN_CURRENT_STATE); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ @@ -849,8 +887,10 @@ executeStateMachine: abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self); exitControlTask(controlObject); } - else if (dynamicCheckResult == CONTROL_RESULT_OK) { - if (isTimeActivatedControl) { + else if (dynamicCheckResult == CONTROL_RESULT_OK) + { + if (isTimeActivatedControl) + { ControlObject_sendCommandTerminationPositive(controlObject); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -862,7 +902,8 @@ executeStateMachine: MmsValue_setUtcTime(operTm, 0); MmsValue_setUtcTimeQuality(operTm, self->iedServer->timeQuality); } - else { + else + { MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, DATA_ACCESS_ERROR_SUCCESS, true); @@ -877,7 +918,8 @@ executeStateMachine: goto executeStateMachine; } - else { + else + { updateNextControlTimeout(self, Hal_getTimeInMs() + 10); } } @@ -889,11 +931,12 @@ executeStateMachine: ControlHandlerResult result = operateControl(controlObject, controlObject->ctlVal, currentTime, controlObject->testMode); - if (result != CONTROL_RESULT_WAITING) { - - if (result == CONTROL_RESULT_OK) { - - if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) { + if (result != CONTROL_RESULT_WAITING) + { + if (result == CONTROL_RESULT_OK) + { + if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) + { ControlObject_sendCommandTerminationPositive(controlObject); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -903,9 +946,10 @@ executeStateMachine: abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED, self); } - else { - - if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) { + else + { + if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: operate failed!\n"); @@ -925,7 +969,8 @@ executeStateMachine: resetAddCause(controlObject); } - else { + else + { updateNextControlTimeout(self, currentTimeInMs + 10); } } @@ -949,7 +994,8 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* self->stateLock = Semaphore_create(1); self->pendingEventsLock = Semaphore_create(1); - if ((self->stateLock == NULL) || (self->pendingEventsLock == NULL)) { + if ((self->stateLock == NULL) || (self->pendingEventsLock == NULL)) + { ControlObject_destroy(self); self = NULL; goto exit_function; @@ -958,7 +1004,8 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* self->name = StringUtils_copyString(name); - if (self->name == NULL) { + if (self->name == NULL) + { ControlObject_destroy(self); self = NULL; goto exit_function; @@ -970,20 +1017,24 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* MmsVariableSpecification* ctlValSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "ctlVal", NULL); - if (ctlValSpec) { + if (ctlValSpec) + { self->ctlVal = MmsValue_newDefaultValue(ctlValSpec); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: control object %s/%s.%s has no ctlVal element!\n", domain->domainName, lnName, name); } MmsVariableSpecification* originSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "origin", NULL); - if (originSpec) { + if (originSpec) + { self->origin = MmsValue_newDefaultValue(originSpec); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: control object %s/%s.%s has no origin element!\n", domain->domainName, lnName, name); } @@ -1015,7 +1066,8 @@ ControlObject_initialize(ControlObject* self) MmsValue* ctlModel = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlModelName); - if (ctlModel == NULL) { + if (ctlModel == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: No control model found for variable %s\n", ctlModelName); } @@ -1028,7 +1080,8 @@ ControlObject_initialize(ControlObject* self) self->ctlNumSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlNumName); - if (self->ctlNumSt == NULL) { + if (self->ctlNumSt == NULL) + { /* for APC */ ctlNumName = StringUtils_createStringInBuffer(strBuf, 130, 4, self->lnName, "$MX$", self->name, "$ctlNum"); @@ -1039,7 +1092,8 @@ ControlObject_initialize(ControlObject* self) self->originSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, originName); - if (self->originSt == NULL) { + if (self->originSt == NULL) + { /* for APC */ originName = StringUtils_createStringInBuffer(strBuf, 130, 4, self->lnName, "$MX$", self->name, "$origin"); @@ -1053,7 +1107,8 @@ ControlObject_initialize(ControlObject* self) updateSboTimeoutValue(self); - if (self->sbo) { + if (self->sbo) + { char* controlObjectReference = StringUtils_createStringInBuffer(strBuf, 130, 5, self->mmsDomain->domainName, "/", self->lnName, "$CO$", self->name); @@ -1154,7 +1209,8 @@ ControlObject_initialize(ControlObject* self) self->stSeld = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, stSeldName); - if ((self->stSeld) && (self->stSeld->type != IEC61850_BOOLEAN)) { + if ((self->stSeld) && (self->stSeld->type != IEC61850_BOOLEAN)) + { self->stSeld = NULL; if (DEBUG_IED_SERVER) @@ -1165,7 +1221,8 @@ ControlObject_initialize(ControlObject* self) self->opRcvd = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, opRcvdName); - if ((self->opRcvd) && (self->opRcvd->type != IEC61850_BOOLEAN)) { + if ((self->opRcvd) && (self->opRcvd->type != IEC61850_BOOLEAN)) + { self->opRcvd = NULL; if (DEBUG_IED_SERVER) @@ -1176,7 +1233,8 @@ ControlObject_initialize(ControlObject* self) self->opOk = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, opOkName); - if ((self->opOk) && (self->opOk->type != IEC61850_BOOLEAN)) { + if ((self->opOk) && (self->opOk->type != IEC61850_BOOLEAN)) + { self->opOk = NULL; if (DEBUG_IED_SERVER) @@ -1187,7 +1245,8 @@ ControlObject_initialize(ControlObject* self) self->tOpOk = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, tOpOkName); - if ((self->tOpOk) && (self->tOpOk->type != IEC61850_TIMESTAMP)) { + if ((self->tOpOk) && (self->tOpOk->type != IEC61850_TIMESTAMP)) + { self->tOpOk = NULL; if (DEBUG_IED_SERVER) @@ -1197,7 +1256,8 @@ ControlObject_initialize(ControlObject* self) self->error = MmsValue_newIntegerFromInt32(0); self->addCause = MmsValue_newIntegerFromInt32(0); - if (ctlModel != NULL) { + if (ctlModel != NULL) + { int ctlModelVal = MmsValue_toInt32(ctlModel); if (DEBUG_IED_SERVER) @@ -1225,44 +1285,50 @@ ControlObject_handlePendingEvents(ControlObject* self) Semaphore_wait(self->pendingEventsLock); #endif - if (self->pendingEvents > 0) { - - if (self->pendingEvents & PENDING_EVENT_SELECTED) { + if (self->pendingEvents > 0) + { + if (self->pendingEvents & PENDING_EVENT_SELECTED) + { if (self->stSeld) IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, true); self->pendingEvents &= ~(PENDING_EVENT_SELECTED); } - if (self->pendingEvents & PENDING_EVENT_UNSELECTED) { + if (self->pendingEvents & PENDING_EVENT_UNSELECTED) + { if (self->stSeld) IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, false); self->pendingEvents &= ~(PENDING_EVENT_UNSELECTED); } - if (self->pendingEvents & PENDING_EVENT_OP_RCVD_TRUE) { + if (self->pendingEvents & PENDING_EVENT_OP_RCVD_TRUE) + { if (self->opRcvd) IedServer_updateBooleanAttributeValue(self->iedServer, self->opRcvd, true); self->pendingEvents &= ~(PENDING_EVENT_OP_RCVD_TRUE); } - if (self->pendingEvents & PENDING_EVENT_OP_RCVD_FALSE) { + if (self->pendingEvents & PENDING_EVENT_OP_RCVD_FALSE) + { if (self->opRcvd) IedServer_updateBooleanAttributeValue(self->iedServer, self->opRcvd, false); self->pendingEvents &= ~(PENDING_EVENT_OP_RCVD_FALSE); } - if (self->pendingEvents & PENDING_EVENT_OP_OK_TRUE) { + if (self->pendingEvents & PENDING_EVENT_OP_OK_TRUE) + { if (self->opOk) IedServer_updateBooleanAttributeValue(self->iedServer, self->opOk, true); self->pendingEvents &= ~(PENDING_EVENT_OP_OK_TRUE); } - if (self->pendingEvents & PENDING_EVENT_OP_OK_FALSE) { + if (self->pendingEvents & PENDING_EVENT_OP_OK_FALSE) + { if (self->opOk) IedServer_updateBooleanAttributeValue(self->iedServer, self->opOk, false); @@ -1278,7 +1344,8 @@ ControlObject_handlePendingEvents(ControlObject* self) void ControlObject_destroy(ControlObject* self) { - if (self) { + if (self) + { if (self->mmsValue) MmsValue_delete(self->mmsValue); @@ -1357,7 +1424,8 @@ ControlObject_getMmsValue(ControlObject* self) bool ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping) { - if (self->mmsConnection == connection) { + if (self->mmsConnection == connection) + { abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED, mmsMapping); return true; } @@ -1415,29 +1483,32 @@ ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataOb void Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) { - if (currentTimeInMs >= self->nextControlTimeout) { - + if (currentTimeInMs >= self->nextControlTimeout) + { /* invalidate nextControlTimeout */ self->nextControlTimeout = (uint64_t) 0xFFFFFFFFFFFFFFFFLLU; LinkedList element = LinkedList_getNext(self->controlObjects); - while (element != NULL) { + while (element != NULL) + { ControlObject* controlObject = (ControlObject*) element->data; - if (controlObject->state != STATE_UNSELECTED) { - - if ((controlObject->ctlModel == 1) || (controlObject->ctlModel == 3)) { - if (controlObject->state == STATE_READY) { + if (controlObject->state != STATE_UNSELECTED) + { + if ((controlObject->ctlModel == 1) || (controlObject->ctlModel == 3)) + { + if (controlObject->state == STATE_READY) + { element = LinkedList_getNext(element); continue; } } - if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) { - - if (controlObject->operateTime <= currentTimeInMs) { - + if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) + { + if (controlObject->operateTime <= currentTimeInMs) + { /* enter state Perform Test */ setOpRcvd(controlObject, true); @@ -1448,7 +1519,9 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) CheckHandlerResult checkResult = CONTROL_ACCEPTED; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ controlObject->errorValue = CONTROL_ERROR_NO_ERROR; controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_INTERLOCKING; @@ -1458,8 +1531,8 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) controlObject->interlockCheck); } - if (checkResult == CONTROL_ACCEPTED) { - + if (checkResult == CONTROL_ACCEPTED) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: time activated operate: command accepted\n"); @@ -1468,8 +1541,8 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) executeControlTask(self, controlObject, currentTimeInMs); } - else { - + else + { ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper", controlObject->errorValue, controlObject->addCauseValue, controlObject->ctlNum, controlObject->origin, false); @@ -1512,12 +1585,16 @@ Control_lookupControlObject(MmsMapping* self, MmsDomain* domain, char* lnName, c { LinkedList element = LinkedList_getNext(self->controlObjects); - while (element != NULL) { + while (element != NULL) + { ControlObject* controlObject = (ControlObject*) element->data; - if (ControlObject_getDomain(controlObject) == domain) { - if (strcmp(ControlObject_getLNName(controlObject), lnName) == 0) { - if (strcmp(ControlObject_getName(controlObject), objectName) == 0) { + if (ControlObject_getDomain(controlObject) == domain) + { + if (strcmp(ControlObject_getLNName(controlObject), lnName) == 0) + { + if (strcmp(ControlObject_getName(controlObject), objectName) == 0) + { return controlObject; } } @@ -1532,8 +1609,10 @@ Control_lookupControlObject(MmsMapping* self, MmsDomain* domain, char* lnName, c static MmsValue* getCtlVal(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { - if (MmsValue_getArraySize(operParameters) > 5) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { + if (MmsValue_getArraySize(operParameters) > 5) + { return MmsValue_getElement(operParameters, 0); } } @@ -1678,7 +1757,8 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connect StringUtils_createStringInBuffer(ctlObj, 130, 7, MmsDomain_getName(self->mmsDomain), "/", self->lnName, "$CO$", self->name, "$", ctlVariable); - if (DEBUG_IED_SERVER) { + if (DEBUG_IED_SERVER) + { printf("IED_SERVER: sendLastApplError:\n"); printf("IED_SERVER: control object: %s\n", ctlObj); printf("IED_SERVER: ctlNum: %u\n", MmsValue_toUint32(ctlNum)); @@ -1728,7 +1808,8 @@ doesElementEquals(char* element, char* name) { int i = 0; - while (name[i] != 0) { + while (name[i] != 0) + { if (element[i] == 0) return false; @@ -1766,9 +1847,6 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia char* lnName = variableId; - if (lnName == NULL) - return NULL; - char* objectName = MmsMapping_getNextNameElement(separator + 1); if (objectName == NULL) @@ -1776,13 +1854,14 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) { - + if (varName != NULL) + { bool foundVar = false; char* nextVarName = varName; - do { + do + { if (doesElementEquals(varName, "Oper") || doesElementEquals(varName, "SBO") || doesElementEquals(varName, "SBOw") || @@ -1795,10 +1874,10 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia nextVarName = MmsMapping_getNextNameElement(varName); - if (nextVarName != NULL) + if (nextVarName) varName = nextVarName; - } while (nextVarName != NULL); + } while (nextVarName); if (foundVar == false) varName = NULL; @@ -1809,24 +1888,28 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia ControlObject* controlObject = Control_lookupControlObject(self, domain, lnName, objectName); - if (controlObject != NULL) { - - if (varName != NULL) { + if (controlObject != NULL) + { + if (varName != NULL) + { if (strcmp(varName, "Oper") == 0) value = controlObject->oper; else if (strcmp(varName, "SBOw") == 0) value = controlObject->sbow; - else if (strcmp(varName, "SBO") == 0) { - if (controlObject->ctlModel == 2) { - + else if (strcmp(varName, "SBO") == 0) + { + if (controlObject->ctlModel == 2) + { uint64_t currentTime = Hal_getTimeInMs(); value = &emptyString; - if (isDirectAccess == true) { + if (isDirectAccess == true) + { checkSelectTimeout(controlObject, currentTime, self); - if (getState(controlObject) == STATE_UNSELECTED) { + if (getState(controlObject) == STATE_UNSELECTED) + { CheckHandlerResult checkResult = CONTROL_ACCEPTED; /* opRcvd must not be set here! */ @@ -1834,7 +1917,9 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia controlObject->addCauseValue = ADD_CAUSE_UNKNOWN; controlObject->mmsConnection = connection; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ controlObject->isSelect = 1; @@ -1844,7 +1929,8 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia controlObject->isSelect = 0; } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { selectObject(controlObject, currentTime, connection, self); value = controlObject->sbo; @@ -1852,13 +1938,15 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else if (checkResult == CONTROL_WAITING_FOR_SELECT) { + else if (checkResult == CONTROL_WAITING_FOR_SELECT) + { controlObject->mmsConnection = connection; controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection); setState(controlObject, STATE_WAIT_FOR_SELECT); value = &delayedResponse; } - else { + else + { #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, convertCheckHandlerResultToServiceError(checkResult)); @@ -1866,9 +1954,9 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia } } } - } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: select not applicable for control model %u\n", controlObject->ctlModel); @@ -1878,7 +1966,8 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia else if (strcmp(varName, "Cancel") == 0) value = controlObject->cancel; - else { + else + { value = MmsValue_getSubElement(ControlObject_getMmsValue(controlObject), ControlObject_getTypeSpec(controlObject), varName); } @@ -1887,7 +1976,8 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia value = ControlObject_getMmsValue(controlObject); } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Control object not found %s/%s.%s\n", domain->domainName, lnName, objectName); } @@ -1926,7 +2016,7 @@ checkValidityOfOriginParameter(MmsValue* origin) } MmsDataAccessError -Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { MmsDataAccessError indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -1961,13 +2051,14 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) { - + if (varName != NULL) + { bool foundVar = false; char* nextVarName = varName; - do { + do + { if (doesElementEquals(varName, "Oper") || doesElementEquals(varName, "SBO") || doesElementEquals(varName, "SBOw") || @@ -1992,31 +2083,36 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari if (DEBUG_IED_SERVER) printf("IED_SERVER: write access control: objectName: (%s) varName: (%s)\n", objectName, varName); - if (varName == NULL) { + if (varName == NULL) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } controlObject = Control_lookupControlObject(self, domain, lnName, objectName); - if (controlObject == NULL) { + if (controlObject == NULL) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } - if (controlObject->ctlModel == CONTROL_MODEL_STATUS_ONLY) { + if (controlObject->ctlModel == CONTROL_MODEL_STATUS_ONLY) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } - if (strcmp(varName, "SBOw") == 0) { /* select with value */ - + if (strcmp(varName, "SBOw") == 0) /* select with value */ + { serviceType = IEC61850_SERVICE_TYPE_SELECT_WITH_VALUES; - if (controlObject->ctlModel == 4) { - - if (controlObject->sbow) { - if (MmsValue_update(controlObject->sbow, value) == false) { + if (controlObject->ctlModel == 4) + { + if (controlObject->sbow) + { + if (MmsValue_update(controlObject->sbow, value) == false) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: SBOw - type mismatch\n"); } @@ -2024,14 +2120,16 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* ctlVal = getCtlVal(value); - if (ctlVal != NULL) { - + if (ctlVal != NULL) + { MmsValue* ctlNum = getOperParameterCtlNum(value); MmsValue* origin = getOperParameterOrigin(value); MmsValue* check = getOperParameterCheck(value); MmsValue* test = getOperParameterTest(value); + MmsValue* t = getOperParameterTime(value); - if (checkValidityOfOriginParameter(origin) == false) { + if (checkValidityOfOriginParameter(origin) == false) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR, @@ -2043,15 +2141,21 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } + if (t) + { + Timestamp_fromMmsValue(&(controlObject->T), t); + } + int state = getState(controlObject); uint64_t currentTime = Hal_getTimeInMs(); checkSelectTimeout(controlObject, currentTime, self); - if (state != STATE_UNSELECTED) { - - if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) { + if (state != STATE_UNSELECTED) + { + if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "SBOw", @@ -2063,7 +2167,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - else { + else + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR, @@ -2075,8 +2180,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } } - else { - + else + { CheckHandlerResult checkResult = CONTROL_ACCEPTED; /* opRcvd must not be set here! */ @@ -2091,7 +2196,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari controlObject->testMode = testCondition; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ controlObject->isSelect = 1; @@ -2103,7 +2210,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari controlObject->isSelect = 0; } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { selectObject(controlObject, currentTime, connection, self); indication = DATA_ACCESS_ERROR_SUCCESS; @@ -2111,7 +2219,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari if (DEBUG_IED_SERVER) printf("IED_SERVER: SBOw - selected successful\n"); } - else if (checkResult == CONTROL_WAITING_FOR_SELECT) { + else if (checkResult == CONTROL_WAITING_FOR_SELECT) + { controlObject->mmsConnection = connection; controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection); @@ -2125,7 +2234,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari indication = DATA_ACCESS_ERROR_NO_RESPONSE; } - else { + else + { indication = (MmsDataAccessError) checkResult; ControlObject_sendLastApplError(controlObject, connection, "SBOw", controlObject->errorValue, @@ -2138,17 +2248,19 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari } } } - else { + else + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } } - else { + else + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } } - else if (strcmp(varName, "Oper") == 0) { - + else if (strcmp(varName, "Oper") == 0) + { serviceType = IEC61850_SERVICE_TYPE_OPERATE; MmsValue* ctlVal = getCtlVal(value); @@ -2159,19 +2271,24 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* timeParameter = getOperParameterTime(value); if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL) - || (timeParameter == NULL)) { + || (timeParameter == NULL)) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } - if (checkValidityOfOriginParameter(origin) == false) { + Timestamp_fromMmsValue(&(controlObject->T), timeParameter); + + if (checkValidityOfOriginParameter(origin) == false) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, ctlNum, origin, true); - if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + { unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self); } @@ -2184,7 +2301,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari int state = getState(controlObject); - if (state == STATE_WAIT_FOR_ACTIVATION_TIME) { + if (state == STATE_WAIT_FOR_ACTIVATION_TIME) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Oper", @@ -2193,15 +2311,17 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - else if (state == STATE_READY) { - + else if (state == STATE_READY) + { bool interlockCheck = MmsValue_getBitStringBit(check, 1); bool synchroCheck = MmsValue_getBitStringBit(check, 0); bool testCondition = MmsValue_getBoolean(test); - if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { - if (controlObject->mmsConnection != connection) { + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + { + if (controlObject->mmsConnection != connection) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper - operate from wrong client connection!\n"); @@ -2212,7 +2332,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - if (controlObject->ctlModel == 4) { /* select-before-operate with enhanced security */ + if (controlObject->ctlModel == 4) /* select-before-operate with enhanced security */ + { if ((MmsValue_equals(ctlVal, controlObject->ctlVal) && MmsValue_equals(origin, controlObject->origin) && MmsValue_equals(ctlNum, controlObject->ctlNum) && @@ -2240,24 +2361,29 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* operTm = getOperParameterOperTime(value); - if (operTm != NULL) { + if (operTm != NULL) + { controlObject->operateTime = MmsValue_getUtcTimeInMs(operTm); - if (controlObject->operateTime > currentTime) { + if (controlObject->operateTime > currentTime) + { controlObject->timeActivatedOperate = true; controlObject->synchroCheck = synchroCheck; controlObject->interlockCheck = interlockCheck; controlObject->mmsConnection = connection; CheckHandlerResult checkResult = CONTROL_ACCEPTED; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ checkResult = controlObject->checkHandler((ControlAction) controlObject, controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck); } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { initiateControlTask(controlObject); setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME); @@ -2269,19 +2395,21 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari indication = DATA_ACCESS_ERROR_SUCCESS; } - else { + else + { indication = (MmsDataAccessError) checkResult; } } } - else{ + else + { controlObject->operateTime = 0; } MmsValue_update(controlObject->oper, value); - if (controlObject->timeActivatedOperate == false) { - + if (controlObject->timeActivatedOperate == false) + { CheckHandlerResult checkResult = CONTROL_ACCEPTED; /* enter state Perform Test */ @@ -2291,13 +2419,16 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari controlObject->addCauseValue = ADD_CAUSE_UNKNOWN; controlObject->mmsConnection = connection; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ checkResult = controlObject->checkHandler((ControlAction) controlObject, controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck); } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { indication = DATA_ACCESS_ERROR_NO_RESPONSE; controlObject->mmsConnection = connection; @@ -2313,7 +2444,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari updateNextControlTimeout(self, currentTime); } - else { + else + { indication = (MmsDataAccessError) checkResult; /* leave state Perform Test */ @@ -2321,7 +2453,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self); - if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) { + if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) + { ControlObject_sendLastApplError(controlObject, connection, "Oper", controlObject->errorValue, controlObject->addCauseValue, ctlNum, origin, true); @@ -2330,7 +2463,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari } } - else if (state == STATE_UNSELECTED) { + else if (state == STATE_UNSELECTED) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper failed - control not selected!\n"); @@ -2343,7 +2477,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - else if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) { + else if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper failed - control already being executed!\n"); @@ -2356,8 +2491,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } } - else if (strcmp(varName, "Cancel") == 0) { - + else if (strcmp(varName, "Cancel") == 0) + { serviceType = IEC61850_SERVICE_TYPE_CANCEL; int state = getState(controlObject); @@ -2368,14 +2503,16 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* ctlNum = getCancelParameterCtlNum(value); MmsValue* origin = getCancelParameterOrigin(value); - if ((ctlNum == NULL) || (origin == NULL)) { + if ((ctlNum == NULL) || (origin == NULL)) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; if (DEBUG_IED_SERVER) printf("IED_SERVER: Invalid cancel message!\n"); goto free_and_return; } - if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) { + if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Cancel", @@ -2385,26 +2522,32 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { - if (state != STATE_UNSELECTED) { - if (controlObject->mmsConnection == connection) { + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + { + if (state != STATE_UNSELECTED) + { + if (controlObject->mmsConnection == connection) + { indication = DATA_ACCESS_ERROR_SUCCESS; unselectObject(controlObject, SELECT_STATE_REASON_CANCELED, self); goto free_and_return; } - else { + else + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Cancel", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true); } } - else { + else + { indication = DATA_ACCESS_ERROR_SUCCESS; } } - if (controlObject->timeActivatedOperate) { + if (controlObject->timeActivatedOperate) + { controlObject->timeActivatedOperate = false; abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED, self); @@ -2417,14 +2560,18 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari free_and_return: #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) - if (controlObject) { - if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) { - if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) { + if (controlObject) + { + if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) + { + if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) + { updateGenericTrackingObjectValues(self, controlObject, serviceType, private_IedServer_convertMmsDataAccessErrorToServiceError(indication)); } } - else { + else + { updateGenericTrackingObjectValues(self, controlObject, serviceType, serviceError); } } @@ -2456,7 +2603,8 @@ ControlAction_getOrCat(ControlAction self) { ControlObject* controlObject = (ControlObject*) self; - if (controlObject->origin) { + if (controlObject->origin) + { MmsValue* orCat = MmsValue_getElement(controlObject->origin, 0); if (orCat) { @@ -2472,10 +2620,12 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize) { ControlObject* controlObject = (ControlObject*) self; - if (controlObject->origin) { + if (controlObject->origin) + { MmsValue* orIdent = MmsValue_getElement(controlObject->origin, 1); - if (orIdent) { + if (orIdent) + { if (MmsValue_getType(orIdent) == MMS_OCTET_STRING) { *orIdentSize = MmsValue_getOctetStringSize(orIdent); return MmsValue_getOctetStringBuffer(orIdent); @@ -2549,5 +2699,12 @@ ControlAction_getControlTime(ControlAction self) return controlObject->operateTime; } -#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ +Timestamp* +ControlAction_getT(ControlAction self) +{ + ControlObject* controlObject = (ControlObject*) self; + return &(controlObject->T); +} + +#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index c55a1ff4..59b0ba40 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -395,7 +395,6 @@ updateLogStatusInLCB(LogControl* self) } } - static void freeDynamicDataSet(LogControl* self) { @@ -483,7 +482,7 @@ copyLCBValuesToTrackingObject(MmsMapping* self, LogControl* logControl) #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ MmsDataAccessError -LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { (void)connection; @@ -524,13 +523,28 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma } else { - if (self->lcbAccessHandler) - { + if (self->controlBlockAccessHandler) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_SET_PARAMETER) == false) { - retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->logControlBlock->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (retVal != DATA_ACCESS_ERROR_SUCCESS) { goto exit_function; } } @@ -747,14 +761,21 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, { bool allowAccess = true; - if (self->lcbAccessHandler) - { + if (self->controlBlockAccessHandler) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_GET_PARAMETER) == false) { - allowAccess = false; + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); - value = &objectAccessDenied; + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->logControlBlock->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + allowAccess = false; + + value = &objectAccessDenied; + } + } } } diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 2d76f39c..1435df2d 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1,7 +1,7 @@ /* * mms_mapping.c * - * Copyright 2013-2023 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -1335,8 +1335,8 @@ checkForServiceTrackingVariables(MmsMapping* self, LogicalNode* logicalNode) { ModelNode* modelNode = logicalNode->firstChild; - while (modelNode) { - + while (modelNode) + { if (!strcmp(modelNode->name, "SpcTrk") || !strcmp(modelNode->name, "DpcTrk") || !strcmp(modelNode->name, "IncTrk") || !strcmp(modelNode->name, "EncTrk1") || !strcmp(modelNode->name, "ApcFTrk") || !strcmp(modelNode->name, "ApcIntTrk") || @@ -2344,8 +2344,8 @@ lookupGCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName) #endif static MmsDataAccessError -writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, - MmsValue* value) +writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, + MmsValue* value, MmsServerConnection connection) { char variableId[130]; @@ -2377,6 +2377,20 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable if (mmsGCB == NULL) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + /* check if write access to GoCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(mmsGCB); + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_GoCB, ld, ln, MmsGooseControlBlock_getName(mmsGCB), varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + if (strcmp(varName, "GoEna") == 0) { if (MmsValue_getType(value) != MMS_BOOLEAN) return DATA_ACCESS_ERROR_TYPE_INCONSISTENT; @@ -2485,43 +2499,6 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ -#if 0 -static MmsValue* -checkIfValueBelongsToModelNode(DataAttribute* dataAttribute, MmsValue* value, MmsValue* newValue) -{ - if (dataAttribute->mmsValue == value) - return newValue; - - DataAttribute* child = (DataAttribute*) dataAttribute->firstChild; - - while (child != NULL) { - MmsValue* tmpValue = checkIfValueBelongsToModelNode(child, value, newValue); - - if (tmpValue != NULL) - return tmpValue; - else - child = (DataAttribute*) child->sibling; - } - - if (MmsValue_getType(value) == MMS_STRUCTURE) { - int elementCount = MmsValue_getArraySize(value); - - int i; - for (i = 0; i < elementCount; i++) { - MmsValue* childValue = MmsValue_getElement(value, i); - MmsValue* childNewValue = MmsValue_getElement(newValue, i); - - MmsValue* tmpValue = checkIfValueBelongsToModelNode(dataAttribute, childValue, childNewValue); - - if (tmpValue != NULL) - return tmpValue; - } - } - - return NULL; -} -#endif - static FunctionalConstraint getFunctionalConstraintForWritableNode(char* separator) { @@ -2591,16 +2568,28 @@ getAccessPolicyForFC(MmsMapping* self, FunctionalConstraint fc) static MmsDataAccessError mmsWriteHandler(void* parameter, MmsDomain* domain, - char* variableId, MmsValue* value, MmsServerConnection connection) + const char* variableId, int arrayIdx, const char* componentId, MmsValue* value, MmsServerConnection connection) { MmsMapping* self = (MmsMapping*) parameter; if (DEBUG_IED_SERVER) - printf("IED_SERVER: Write requested %s\n", variableId); + { + if (arrayIdx != -1) { + if (componentId) { + printf("IED_SERVER: Write requested %s(%i).%s\n", variableId, arrayIdx, componentId); + } + else { + printf("IED_SERVER: Write requested %s(%i)\n", variableId, arrayIdx); + } + } + else { + printf("IED_SERVER: Write requested %s\n", variableId); + } + } /* Access control based on functional constraint */ - char* separator = strchr(variableId, '$'); + char* separator = (char*)strchr(variableId, '$'); if (separator == NULL) return DATA_ACCESS_ERROR_INVALID_ADDRESS; @@ -2619,7 +2608,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, /* Goose control block - GO */ if (isGooseControlBlock(separator)) - return writeAccessGooseControlBlock(self, domain, variableId, value); + return writeAccessGooseControlBlock(self, domain, variableId, value, connection); #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ @@ -2677,7 +2666,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if (rcNameLen == variableIdLen) { if (strncmp(variableId, rc->name, variableIdLen) == 0) { - char* elementName = variableId + rcNameLen + 1; + const char* elementName = variableId + rcNameLen + 1; return Reporting_RCBWriteAccessHandler(self, rc, elementName, value, connection); } @@ -2699,8 +2688,48 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if (nextSep != NULL) { nextSep = strchr(nextSep + 1, '$'); + if (nextSep == NULL) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + } + char* nameId = nextSep + 1; + /* check access permissions */ + if (self->controlBlockAccessHandler) + { + MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + char lnName[65]; + strncpy(lnName, variableId, 64); + lnName[64] = 0; + lnName[lnNameLength] = 0; + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, "SGCB", nameId, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (retVal != DATA_ACCESS_ERROR_SUCCESS) { + return retVal; + } + } + if (strcmp(nameId, "ActSG") == 0) { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; @@ -2709,6 +2738,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, uint32_t val = MmsValue_toUint32(value); if ((val > 0) && (val <= sg->sgcb->numOfSGs)) { + if (val != sg->sgcb->actSG) { if (sg->actSgChangedHandler) { @@ -2891,13 +2921,19 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ /* writable data model elements - SP, SV, CF, DC, BL */ - if (fc != IEC61850_FC_NONE) { + if (fc != IEC61850_FC_NONE) + { MmsValue* cachedValue; - cachedValue = MmsServer_getValueFromCache(self->mmsServer, domain, variableId); - - if (cachedValue) { + if (arrayIdx != -1) { + cachedValue = MmsServer_getValueFromCacheEx2(self->mmsServer, domain, variableId, arrayIdx, componentId); + } + else { + cachedValue = MmsServer_getValueFromCache(self->mmsServer, domain, variableId); + } + if (cachedValue) + { if (!MmsValue_equalTypes(cachedValue, value)) { return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; } @@ -2910,7 +2946,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, printf("IED_SERVER: write to %s policy:%i\n", variableId, nodeAccessPolicy); #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (isFunctionalConstraint("SE", separator)) { + if (isFunctionalConstraint("SE", separator)) + { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); if (sg != NULL) { @@ -2927,12 +2964,13 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, /* Call write access handlers */ LinkedList writeHandlerListElement = LinkedList_getNext(self->attributeAccessHandlers); - while (writeHandlerListElement != NULL) { + while (writeHandlerListElement) + { AttributeAccessHandler* accessHandler = (AttributeAccessHandler*) writeHandlerListElement->data; DataAttribute* dataAttribute = accessHandler->attribute; - if (dataAttribute->mmsValue == cachedValue) { - + if (dataAttribute->mmsValue == cachedValue) + { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -2940,7 +2978,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, accessHandler->handler(dataAttribute, value, clientConnection, accessHandler->parameter); - if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) { + if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) + { handlerFound = true; if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE) @@ -2956,14 +2995,14 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, } /* DENY access if no handler is found and default policy is DENY */ - if (!handlerFound) { - + if (!handlerFound) + { if (nodeAccessPolicy == ACCESS_POLICY_DENY) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - } - if (updateValue) { + if (updateValue) + { DataAttribute* da = IedModel_lookupDataAttributeByMmsValue(self->model, cachedValue); if (da) @@ -3026,7 +3065,7 @@ MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) static MmsValue* -readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -3050,13 +3089,28 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; MmsGooseControlBlock mmsGCB = lookupGCB(self, domain, lnName, objectName); - if (mmsGCB != NULL) { - if (varName != NULL) { + if (mmsGCB) { + + /* check if read access to GoCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(mmsGCB); + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_GoCB, ld, ln, MmsGooseControlBlock_getName(mmsGCB), varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + return &objectAccessDenied; + } + } + + if (varName) { value = MmsValue_getSubElement(MmsGooseControlBlock_getMmsValues(mmsGCB), MmsGooseControlBlock_getVariableSpecification(mmsGCB), varName); } @@ -3070,7 +3124,6 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ - static MmsValue* mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) { @@ -3096,11 +3149,10 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo } #endif - #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) /* GOOSE control blocks - GO */ if (isGooseControlBlock(separator)) { - retValue = readAccessGooseControlBlock(self, domain, variableId); + retValue = readAccessGooseControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3108,7 +3160,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) /* Sampled Value control blocks - MS/US */ if (isSampledValueControlBlock(separator)) { - retValue = LIBIEC61850_SV_readAccessSampledValueControlBlock(self, domain, variableId); + retValue = LIBIEC61850_SV_readAccessSampledValueControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3123,8 +3175,8 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_REPORT_SERVICE == 1) /* Report control blocks - BR, RP */ - if (isReportControlBlock(separator)) { - + if (isReportControlBlock(separator)) + { LinkedList reportControls = self->reportControls; LinkedList nextElement = reportControls; @@ -3145,11 +3197,12 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo else variableIdLen = strlen(variableId); - while ((nextElement = LinkedList_getNext(nextElement)) != NULL) { + while ((nextElement = LinkedList_getNext(nextElement)) != NULL) + { ReportControl* rc = (ReportControl*) nextElement->data; - if (rc->domain == domain) { - + if (rc->domain == domain) + { int parentLNNameStrLen = strlen(rc->parentLN->name); if (parentLNNameStrLen != lnNameLength) @@ -3158,7 +3211,8 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo if (memcmp(rc->parentLN->name, variableId, parentLNNameStrLen) != 0) continue; - if (strlen(rc->name) == variableIdLen) { + if (strlen(rc->name) == variableIdLen) + { if (strncmp(variableId, rc->name, variableIdLen) == 0) { char* elementName = MmsMapping_getNextNameElement(reportName); @@ -3314,6 +3368,257 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE } } +static bool +mmsListObjectsAccessHandler(void* parameter, MmsGetNameListType listType, MmsDomain* domain, char* variableId, MmsServerConnection connection) +{ + MmsMapping* self = (MmsMapping*) parameter; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsListObjectsAccessHandler: Requested %s\n", variableId); + + bool allowAccess = true; + + if (listType == MMS_GETNAMELIST_DATASETS) + { + if (self->listObjectsAccessHandler) { + + char str[65]; + + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + LogicalNode* ln = NULL; + + char* objectName = variableId; + + char* separator = strchr(variableId, '$'); + + if (separator) { + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + objectName = separator + 1; + } + } + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_SET, ld, ln, objectName, NULL, IEC61850_FC_NONE); + } + + return allowAccess; + } + else if (listType == MMS_GETNAMELIST_JOURNALS) + { + if (self->listObjectsAccessHandler) { + char str[65]; + + char* ldName = MmsDomain_getName(domain); + + char* objectName = variableId; + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + LogicalNode* ln = NULL; + + char* separator = strchr(variableId, '$'); + + if (separator) { + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + objectName = separator + 1; + } + } + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_LOG, ld, ln, objectName, NULL, IEC61850_FC_NONE); + } + + return allowAccess; + } + + if (self->listObjectsAccessHandler) + { + char* separator = strchr(variableId, '$'); + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + if (ld) + { + FunctionalConstraint fc = IEC61850_FC_NONE; + + if (separator) { + fc = FunctionalConstraint_fromString(separator + 1); + + if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || + fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || + fc == IEC61850_FC_LG || fc == IEC61850_FC_GO) + { + char* subObjectName = NULL; + + char str[65]; + char subObjectBuf[65]; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + char* doStart = strchr(separator + 1, '$'); + + if (doStart != NULL) { + + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else { + doEnd--; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + + subObjectName = StringUtils_copyStringToBufferAndReplace(doEnd + 2, subObjectBuf, '$', '.'); + } + } + } + + ACSIClass acsiClass = ACSI_CLASS_USVCB; + + switch (fc) + { + case IEC61850_FC_BR: + acsiClass = ACSI_CLASS_BRCB; + break; + + case IEC61850_FC_RP: + acsiClass = ACSI_CLASS_URCB; + break; + + case IEC61850_FC_GO: + acsiClass = ACSI_CLASS_GoCB; + break; + + case IEC61850_FC_LG: + acsiClass = ACSI_CLASS_LCB; + break; + + case IEC61850_FC_MS: + acsiClass = ACSI_CLASS_MSVCB; + break; + + default: + break; + } + + if (self->listObjectsAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, acsiClass, ld, ln, str, subObjectName, fc); + } + + goto exit_function; + } + else + { + char str[65]; + char* subObjectName = NULL; + char subObjectBuf[65]; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln != NULL) + { + char* doStart = strchr(separator + 1, '$'); + + if (doStart != NULL) { + + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else { + doEnd--; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + + subObjectName = StringUtils_copyStringToBufferAndReplace(doEnd + 2, subObjectBuf, '$', '.'); + } + + if (fc == IEC61850_FC_SP) { + if (!strcmp(str, "SGCB")) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, subObjectName, fc); + + goto exit_function; + } + } + + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + + if (dobj != NULL) { + + if (dobj->modelType == DataObjectModelType) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + if (self->listObjectsAccessHandler) { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, dobj->name, subObjectName, fc); + } + } + } + } + else { + /* no data object but with FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + if (self->listObjectsAccessHandler) { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, NULL, NULL, fc); + } + } + } + } + } + else { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln) { + /* only LN, no FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->listObjectsAccessHandler) { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, NULL, NULL, fc); + } + } + } + } + else { + /* internal error ? - we should not end up here! */ + } + } + +exit_function: + return allowAccess; +} + static MmsDataAccessError mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) { @@ -3324,15 +3629,26 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS if (DEBUG_IED_SERVER) printf("IED_SERVER: mmsReadAccessHandler: Requested %s\n", variableId); + if (self->iedServer->ignoreReadAccess) + { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsReadAccessHandler - ignore request\n"); + + return DATA_ACCESS_ERROR_NO_RESPONSE; + } + char* separator = strchr(variableId, '$'); #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (separator) { - if (isFunctionalConstraint("SE", separator)) { + if (separator) + { + if (isFunctionalConstraint("SE", separator)) + { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); - if (sg != NULL) { + if (sg != NULL) + { if (sg->sgcb->editSG == 0) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } @@ -3350,13 +3666,12 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS LogicalDevice* ld = IedModel_getDevice(self->model, ldName); - if (ld != NULL) { - - char str[65]; - - FunctionalConstraint fc; + if (ld != NULL) + { + FunctionalConstraint fc = IEC61850_FC_NONE; - if (separator != NULL) { + if (separator != NULL) + { fc = FunctionalConstraint_fromString(separator + 1); if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || @@ -3367,6 +3682,8 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } else { + char str[65]; + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); @@ -3375,30 +3692,45 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS { char* doStart = strchr(separator + 1, '$'); - if (doStart != NULL) { - + if (doStart != NULL) + { char* doEnd = strchr(doStart + 1, '$'); - if (doEnd == NULL) { + if (doEnd == NULL) + { StringUtils_copyStringToBuffer(doStart + 1, str); } - else { + else + { doEnd--; StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); } - if (fc == IEC61850_FC_SP) { + if (fc == IEC61850_FC_SP) + { if (!strcmp(str, "SGCB")) + { + if (self->controlBlockAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, "", IEC61850_CB_ACCESS_TYPE_READ) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + return DATA_ACCESS_ERROR_SUCCESS; + } } ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); - if (dobj != NULL) { - - if (dobj->modelType == DataObjectModelType) { - + if (dobj != NULL) + { + if (dobj->modelType == DataObjectModelType) + { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -3407,7 +3739,8 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } } } - else { + else + { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -3417,6 +3750,18 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } } } + else + { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln != NULL) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + return self->readAccessHandler(ld, ln, NULL, fc, clientConnection, + self->readAccessHandlerParameter); + } + } } return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; @@ -3643,17 +3988,29 @@ mmsReadJournalHandler(void* parameter, MmsDomain* domain, const char* logName, M MmsMapping* self = (MmsMapping*)parameter; - if (self->logAccessHandler) { - char logReference[130]; - logReference[0] = 0; + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - StringUtils_appendString(logReference, 130, MmsDomain_getName(domain)); - StringUtils_appendString(logReference, 130, "/"); - StringUtils_appendString(logReference, 130, logName); + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + LogicalNode* ln = NULL; + + char str[65]; - allowAccess = self->logAccessHandler(self->logAccessHandlerParameter, logReference, clientConnection); + StringUtils_copyStringMax(str, 65, logName); + + char* name = str; + + char* separator = strchr(str, '$'); + + if (separator) { + name = separator + 1; + *separator = 0; + + ln = LogicalDevice_getLogicalNode(ld, str); + } + + allowAccess = self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LOG, ld, ln, name, NULL, IEC61850_CB_ACCESS_TYPE_READ); } return allowAccess; @@ -3666,6 +4023,7 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installReadHandler(self->mmsServer, mmsReadHandler, (void*) self); MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); + MmsServer_installListAccessHandler(self->mmsServer, mmsListObjectsAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); MmsServer_installGetNameListHandler(self->mmsServer, mmsGetNameListHandler, (void*) self); @@ -3869,10 +4227,12 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) bool modelLocked = self->isModelLocked; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { ReportControl* rc = (ReportControl*) element->data; - if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) { + if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) + { int index; switch (flag) { diff --git a/src/iec61850/server/mms_mapping/mms_sv.c b/src/iec61850/server/mms_mapping/mms_sv.c index a03652f1..3fbd0d2b 100644 --- a/src/iec61850/server/mms_mapping/mms_sv.c +++ b/src/iec61850/server/mms_mapping/mms_sv.c @@ -1,7 +1,7 @@ /* * mms_sv.c * - * Copyright 2015-2022 Michael Zillgith + * Copyright 2015-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,6 +32,8 @@ #include "mms_sv.h" #include "mms_mapping_internal.h" +#include "ied_server_private.h" +#include "mms_value_internal.h" struct sMmsSampledValueControlBlock { SVControlBlock* svcb; @@ -50,11 +52,12 @@ struct sMmsSampledValueControlBlock { MmsValue* svEnaValue; MmsValue* resvValue; - SVCBEventHandler eventHandler; void* eventHandlerParameter; }; +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + MmsSampledValueControlBlock MmsSampledValueControlBlock_create() { @@ -63,7 +66,6 @@ MmsSampledValueControlBlock_create() return self; } - void MmsSampledValueControlBlock_destroy(MmsSampledValueControlBlock self) { @@ -105,7 +107,8 @@ MmsSampledValueControlBlock_enable(MmsSampledValueControlBlock self) if (DEBUG_IED_SERVER) printf("IED_SERVER: enable SVCB %s\n", self->svcb->name); - self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); + if (self->eventHandler) + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); } static void @@ -117,7 +120,8 @@ MmsSampledValueControlBlock_disable(MmsSampledValueControlBlock self) if (DEBUG_IED_SERVER) printf("IED_SERVER: disable SVCB %s\n", self->svcb->name); - self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); + if (self->eventHandler) + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); } static bool @@ -127,7 +131,7 @@ MmsSampledValueControlBlock_isEnabled(MmsSampledValueControlBlock self) } MmsDataAccessError -LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { char variableId[130]; @@ -150,7 +154,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; else return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -160,7 +164,28 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch if (mmsSVCB == NULL) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - if (mmsSVCB->reservedByClient != NULL) { + /* check if write access to SVCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (mmsSVCB->svcb->isUnicast) + acsiClass = ACSI_CLASS_USVCB; + else + acsiClass = ACSI_CLASS_MSVCB; + + LogicalNode* ln = mmsSVCB->logicalNode; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, mmsSVCB->svcb->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + if (mmsSVCB->reservedByClient) { if (mmsSVCB->reservedByClient != connection) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } @@ -209,7 +234,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch } MmsValue* -LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -233,13 +258,35 @@ LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* d char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; MmsSampledValueControlBlock mmsSVCB = lookupSVCB(self, domain, lnName, objectName); - if (mmsSVCB != NULL) { - if (varName != NULL) { + if (mmsSVCB) { + + /* check if read access to SVCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (mmsSVCB->svcb->isUnicast) + acsiClass = ACSI_CLASS_USVCB; + else + acsiClass = ACSI_CLASS_MSVCB; + + LogicalNode* ln = mmsSVCB->logicalNode; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, mmsSVCB->svcb->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + return &objectAccessDenied; + } + } + + if (varName) { value = MmsValue_getSubElement(mmsSVCB->mmsValue, mmsSVCB->mmsType, varName); } else { diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 9cbbb4f2..0bb683f1 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1,7 +1,7 @@ /* * reporting.c * - * Copyright 2013-2023 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -185,7 +185,8 @@ purgeBuf(ReportControl* rc) static void deleteDataSetValuesShadowBuffer(ReportControl* self) { - if (self->bufferedDataSetValues != NULL) { + if (self->bufferedDataSetValues != NULL) + { assert(self->dataSet != NULL); int dataSetSize = DataSet_getSize(self->dataSet); @@ -252,7 +253,7 @@ ReportControl_destroy(ReportControl* self) } MmsValue* -ReportControl_getRCBValue(ReportControl* rc, char* elementName) +ReportControl_getRCBValue(ReportControl* rc, const char* elementName) { if (rc->buffered) { if (strcmp(elementName, "RptID") == 0) @@ -460,7 +461,7 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) } static void -updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsValue* newValue) +updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, const char* name, MmsValue* newValue) { if (rc->buffered) { if (self->brcbTrk) { @@ -724,9 +725,23 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, success = true; dataSetValue = NULL; - if (rc->buffered) { - rc->isBuffering = false; - purgeBuf(rc); + if (rc->dataSet) { + if (rc->buffered) { + rc->isBuffering = false; + purgeBuf(rc); + } + + /* delete pending events */ + deleteDataSetValuesShadowBuffer(rc); + + if (isUsedDataSetDynamic) { + if (rc->dataSet) { + MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); + } + } + + /* release used data set */ + rc->dataSet = NULL; } } else @@ -739,7 +754,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, /* check if old and new data sets are the same */ if (rc->dataSet && dataSetValue) { - const char* dataSetLdName = rc->dataSet->logicalDeviceName; const char* dataSetName = rc->dataSet->name; const char* newDataSetName = MmsValue_toString(dataSetValue); @@ -790,7 +804,8 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } } - if (dataSetValue) { + if (dataSetValue) + { const char* dataSetName = MmsValue_toString(dataSetValue); DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName); @@ -1777,12 +1792,24 @@ ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerCon ClientConnection clientConnection = NULL; - if (mmsMapping->rcbAccessHandler || mmsMapping->rcbEventHandler) { + if (mmsMapping->controlBlockAccessHandler || mmsMapping->rcbEventHandler) { clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); } - if (mmsMapping->rcbAccessHandler) { - if (mmsMapping->rcbAccessHandler(mmsMapping->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER) == false) { + if (mmsMapping->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (rc->rcb->buffered) + acsiClass = ACSI_CLASS_BRCB; + else + acsiClass = ACSI_CLASS_URCB; + + LogicalNode* ln = rc->rcb->parent; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + if (mmsMapping->controlBlockAccessHandler(mmsMapping->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->rcb->name, elementName, IEC61850_CB_ACCESS_TYPE_READ) == false) { accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; accessAllowed = false; } @@ -1880,7 +1907,7 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection) } MmsDataAccessError -Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, +Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, const char* elementName, MmsValue* value, MmsServerConnection connection) { MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; @@ -1891,8 +1918,20 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); /* check if write access to RCB is allowed on this connection */ - if (self->rcbAccessHandler) { - if (self->rcbAccessHandler(self->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER) == false) { + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (rc->rcb->buffered) + acsiClass = ACSI_CLASS_BRCB; + else + acsiClass = ACSI_CLASS_URCB; + + LogicalNode* ln = rc->rcb->parent; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->rcb->name, elementName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function_only_tracking; @@ -2873,13 +2912,14 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ int dataBlockSize = 0; - if (isIntegrity || isGI) { - + if (isIntegrity || isGI) + { DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas; int i; - for (i = 0; i < inclusionBitStringSize; i++) { + for (i = 0; i < inclusionBitStringSize; i++) + { /* don't need reason for inclusion in GI or integrity report */ int encodedSize; @@ -2887,7 +2927,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ if (dataSetEntry->value) { encodedSize = MmsValue_encodeMmsData(dataSetEntry->value, NULL, 0, false); } - else { + else + { MmsValue _errVal; _errVal.type = MMS_DATA_ACCESS_ERROR; _errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; @@ -2902,17 +2943,19 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ bufferEntrySize += MemoryAllocator_getAlignedSize(sizeof(int) + dataBlockSize); /* add aligned_size(LEN + DATA) */ } - else { /* other trigger reason */ + else + { + /* other trigger reason */ bufferEntrySize += inclusionFieldSize; int reasonForInclusionSize = 0; int i; - for (i = 0; i < inclusionBitStringSize; i++) { - - if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { - + for (i = 0; i < inclusionBitStringSize; i++) + { + if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) + { reasonForInclusionSize++; assert(reportControl->bufferedDataSetValues[i] != NULL); @@ -2947,13 +2990,15 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ if (DEBUG_IED_SERVER) printf("IED_SERVER: number of reports in report buffer: %i\n", buffer->reportsCount); - if (buffer->lastEnqueuedReport == NULL) { /* buffer is empty - we start at the beginning of the memory block */ + if (buffer->lastEnqueuedReport == NULL) + { + /* buffer is empty - we start at the beginning of the memory block */ entryBufPos = buffer->memoryBlock; buffer->oldestReport = (ReportBufferEntry*) entryBufPos; buffer->nextToTransmit = (ReportBufferEntry*) entryBufPos; } - else { - + else + { assert(buffer->lastEnqueuedReport != NULL); assert(buffer->oldestReport != NULL); @@ -2965,12 +3010,16 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ if (DEBUG_IED_SERVER) printf ("IED_SERVER: Last buffer offset: %i\n", (int) ((uint8_t*) buffer->lastEnqueuedReport - buffer->memoryBlock)); - if (buffer->lastEnqueuedReport == buffer->oldestReport) { /* --> buffer->reportsCount == 1 */ + if (buffer->lastEnqueuedReport == buffer->oldestReport) + { + /* --> buffer->reportsCount == 1 */ assert(buffer->reportsCount == 1); entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); - if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */ + if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) + { + /* buffer overflow */ entryBufPos = buffer->memoryBlock; #if (DEBUG_IED_SERVER == 1) @@ -2984,7 +3033,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ buffer->oldestReport->next = NULL; buffer->nextToTransmit = NULL; } - else { + else + { if (buffer->nextToTransmit == buffer->oldestReport) buffer->nextToTransmit = buffer->lastEnqueuedReport; @@ -2993,17 +3043,22 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ } } - else if (buffer->lastEnqueuedReport > buffer->oldestReport) { + else if (buffer->lastEnqueuedReport > buffer->oldestReport) + { entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); - if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */ + if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) + { + /* buffer overflow */ entryBufPos = buffer->memoryBlock; /* remove old reports until enough space for new entry is available */ - while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) { + while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) + { assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -3018,7 +3073,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ buffer->reportsCount--; - if (buffer->oldestReport == NULL) { + if (buffer->oldestReport == NULL) + { buffer->oldestReport = (ReportBufferEntry*) entryBufPos; buffer->oldestReport->next = NULL; break; @@ -3028,14 +3084,18 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ buffer->lastEnqueuedReport->next = (ReportBufferEntry*) entryBufPos; } - else if (buffer->lastEnqueuedReport < buffer->oldestReport) { + else if (buffer->lastEnqueuedReport < buffer->oldestReport) + { entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); - if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */ + if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) + { + /* buffer overflow */ entryBufPos = buffer->memoryBlock; /* remove older reports in upper buffer part */ - while ((uint8_t*) buffer->oldestReport > buffer->memoryBlock) { + while ((uint8_t*) buffer->oldestReport > buffer->memoryBlock) + { assert(buffer->oldestReport != NULL); if (buffer->nextToTransmit == buffer->oldestReport) { @@ -3055,13 +3115,15 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ } /* remove older reports in lower buffer part that will be overwritten by new report */ - while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) { + while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) + { if (buffer->oldestReport == NULL) break; assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -3077,15 +3139,17 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ buffer->reportsCount--; } } - else { - while (((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) && ((uint8_t*) buffer->oldestReport != buffer->memoryBlock)) { - + else + { + while (((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) && ((uint8_t*) buffer->oldestReport != buffer->memoryBlock)) + { if (buffer->oldestReport == NULL) break; assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -3116,7 +3180,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ entry->timeOfEntry = timeOfEntry; - if (isBuffered) { + if (isBuffered) + { /* ENTRY_ID is set to system time in ms! */ uint64_t entryId = timeOfEntry; @@ -3135,7 +3200,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ printf(" at pos %p\n", entryStartPos); #endif - if (reportControl->enabled == false) { + if (reportControl->enabled == false) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(reportControl->rcbValuesLock); #endif @@ -3162,7 +3228,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ entryBufPos += MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry)); - if (isIntegrity || isGI) { + if (isIntegrity || isGI) + { DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas; /* encode LEN */ @@ -3172,12 +3239,13 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ /* encode DATA */ int i; - for (i = 0; i < inclusionBitStringSize; i++) { - + for (i = 0; i < inclusionBitStringSize; i++) + { if (dataSetEntry->value) { entryBufPos += MmsValue_encodeMmsData(dataSetEntry->value, entryBufPos, 0, true); } - else { + else + { MmsValue _errVal; _errVal.type = MMS_DATA_ACCESS_ERROR; _errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; @@ -3187,9 +3255,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ dataSetEntry = dataSetEntry->sibling; } - } - else { + else + { /* encode inclusion bit string */ inclusionFieldStatic.value.bitString.buf = entryBufPos; memset(entryBufPos, 0, inclusionFieldSize); @@ -3202,10 +3270,10 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ /* encode DATA */ int i; - for (i = 0; i < inclusionBitStringSize; i++) { - - if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { - + for (i = 0; i < inclusionBitStringSize; i++) + { + if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) + { /* update inclusion bit string for report entry */ MmsValue_setBitStringBit(inclusionField, i, true); @@ -3213,13 +3281,14 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ entryBufPos += MmsValue_encodeMmsData(reportControl->bufferedDataSetValues[i], entryBufPos, 0, true); } - } /* encode REASON */ - for (i = 0; i < inclusionBitStringSize; i++) { + for (i = 0; i < inclusionBitStringSize; i++) + { - if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { + if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) + { *entryBufPos = (uint8_t) reportControl->inclusionFlags[i]; entryBufPos ++; } @@ -3248,10 +3317,12 @@ exit_function: Semaphore_post(buffer->lock); #endif - if (reportControl->server) { + if (reportControl->server) + { MmsMapping* mmsMapping = reportControl->server->mmsMapping; - if (mmsMapping->rcbEventHandler) { + if (mmsMapping->rcbEventHandler) + { if (overflow) { mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, reportControl->rcb, NULL, RCB_EVENT_OVERFLOW, NULL, DATA_ACCESS_ERROR_SUCCESS); } @@ -3942,11 +4013,12 @@ Reporting_activateBufferedReports(MmsMapping* self) static void processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) { - if ((rc->enabled) || (rc->isBuffering)) { - - if (rc->triggerOps & TRG_OPT_GI) { - if (rc->gi) { - + if ((rc->enabled) || (rc->isBuffering)) + { + if (rc->triggerOps & TRG_OPT_GI) + { + if (rc->gi) + { /* send current events in event buffer before GI report */ if (rc->triggered) { rc->triggered = false; @@ -3961,12 +4033,12 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) } } - if (rc->triggerOps & TRG_OPT_INTEGRITY) { - - if (rc->intgPd > 0) { - - if (currentTimeInMs >= rc->nextIntgReportTime) { - + if (rc->triggerOps & TRG_OPT_INTEGRITY) + { + if (rc->intgPd > 0) + { + if (currentTimeInMs >= rc->nextIntgReportTime) + { /* send current events in event buffer before integrity report */ if (rc->triggered) { enqueueReport(rc, false, false, currentTimeInMs); @@ -3981,8 +4053,8 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) } /* check for system time change effects */ - if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) { - + if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) + { if (rc->server->syncIntegrityReportTimes) { rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd); } @@ -3995,9 +4067,11 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) rc->triggered = false; } - else { + else + { /* check for system time change effects */ - if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) { + if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) + { if (rc->server->syncIntegrityReportTimes) { rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd); } @@ -4009,9 +4083,10 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) } } - if (rc->triggered) { - if (currentTimeInMs >= rc->reportTime) { - + if (rc->triggered) + { + if (currentTimeInMs >= rc->reportTime) + { enqueueReport(rc, false, false, currentTimeInMs); rc->triggered = false; @@ -4027,11 +4102,12 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) Semaphore_wait(self->isModelLockedMutex); #endif - if (self->isModelLocked == false) { - + if (self->isModelLocked == false) + { LinkedList element = self->reportControls; - while ((element = LinkedList_getNext(element)) != NULL ) { + while ((element = LinkedList_getNext(element)) != NULL ) + { ReportControl* rc = (ReportControl*) element->data; ReportControl_lockNotify(rc); @@ -4055,11 +4131,12 @@ Reporting_sendReports(MmsMapping* self, MmsServerConnection connection) { LinkedList element = LinkedList_getNext(self->reportControls); - while (element) { + while (element) + { ReportControl* rc = (ReportControl*) LinkedList_getData(element); - if (rc->clientConnection == connection) { - + if (rc->clientConnection == connection) + { ReportControl_lockNotify(rc); if (rc->enabled) { @@ -4086,8 +4163,10 @@ static void copyValuesToReportBuffer(ReportControl* self) { int i; - for (i = 0; i < self->dataSet->elementCount; i++) { - if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) { + for (i = 0; i < self->dataSet->elementCount; i++) + { + if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) + { copySingleValueToReportBuffer(self, i); /* clear not-updated flag */ @@ -4104,13 +4183,14 @@ Reporting_processReportEventsAfterUnlock(MmsMapping* self) uint64_t currentTime = Hal_getTimeInMs(); - while ((element = LinkedList_getNext(element)) != NULL ) { + while ((element = LinkedList_getNext(element)) != NULL ) + { ReportControl* rc = (ReportControl*) element->data; ReportControl_lockNotify(rc); - if ((rc->enabled) || (rc->isBuffering)) { - + if ((rc->enabled) || (rc->isBuffering)) + { if (rc->triggered) { copyValuesToReportBuffer(rc); @@ -4128,10 +4208,13 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, { ReportControl_lockNotify(self); - if (self->inclusionFlags[dataSetEntryIndex] & flag) { /* report for this data set entry is already pending (bypass BufTm) */ + if (self->inclusionFlags[dataSetEntryIndex] & flag) + { + /* report for this data set entry is already pending (bypass BufTm and send report immediately) */ self->reportTime = Hal_getTimeInMs(); - if (modelLocked) { + if (modelLocked) + { /* buffer all relevant values */ copyValuesToReportBuffer(self); } @@ -4139,19 +4222,21 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, processEventsForReport(self, self->reportTime); } - if (modelLocked) { + if (modelLocked) + { /* set flag to update values when report is to be sent or data model unlocked */ self->inclusionFlags[dataSetEntryIndex] = self->inclusionFlags[dataSetEntryIndex] | flag | REPORT_CONTROL_NOT_UPDATED; - } - else { + else + { self->inclusionFlags[dataSetEntryIndex] = flag; /* buffer value for report */ copySingleValueToReportBuffer(self, dataSetEntryIndex); } - if (self->triggered == false) { + if (self->triggered == false) + { uint64_t currentTime = Hal_getTimeInMs(); MmsValue_setBinaryTime(self->timeOfEntry, currentTime); @@ -4180,7 +4265,8 @@ ReportControlBlock_getRptEna(ReportControlBlock* self) char* ReportControlBlock_getRptID(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index 122fa8bd..211e925e 100644 --- a/src/iec61850/server/model/cdc.c +++ b/src/iec61850/server/model/cdc.c @@ -3,7 +3,7 @@ * * Helper functions for the dynamic creation of Common Data Classes (CDCs) * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,7 +35,7 @@ DataAttribute* CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool isIntegerNotFloat) { - DataAttribute* analogeValue = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* analogeValue = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); if (isIntegerNotFloat) DataAttribute_create("i", (ModelNode*) analogeValue, IEC61850_INT32, fc, triggerOptions, 0, 0); @@ -48,7 +48,7 @@ CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstrai DataAttribute* CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasTransientIndicator) { - DataAttribute* valWithTrans = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* valWithTrans = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); DataAttribute_create("posVal", (ModelNode*) valWithTrans, IEC61850_INT8, fc, triggerOptions, 0, 0); @@ -64,7 +64,7 @@ CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstrain DataAttribute* CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, FunctionalConstraint fc, uint8_t triggerOptions) { - DataAttribute* vector = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* vector = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); CAC_AnalogueValue_create("mag", (ModelNode*) vector, fc, triggerOptions, false); @@ -77,7 +77,7 @@ CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, Functio DataAttribute* CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasZVal) { - DataAttribute* point = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* point = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); DataAttribute_create("xVal", (ModelNode*) point, IEC61850_FLOAT32, fc, triggerOptions, 0, 0); DataAttribute_create("yVal", (ModelNode*) point, IEC61850_FLOAT32, fc, triggerOptions, 0, 0); @@ -91,7 +91,7 @@ CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, u DataAttribute* CAC_ScaledValueConfig_create(const char* name, ModelNode* parent) { - DataAttribute* scaling = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + DataAttribute* scaling = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("scaleFactor", (ModelNode*) scaling, IEC61850_FLOAT32, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("offset", (ModelNode*) scaling, IEC61850_FLOAT32, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); @@ -102,7 +102,7 @@ CAC_ScaledValueConfig_create(const char* name, ModelNode* parent) DataAttribute* CAC_Unit_create(const char* name, ModelNode* parent, bool hasMagnitude) { - DataAttribute* unit = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + DataAttribute* unit = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("SIUnit", (ModelNode*) unit, IEC61850_ENUMERATED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); @@ -255,7 +255,7 @@ CDC_addStandardOptions(DataObject* dataObject, uint32_t options) DataObject* CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newSPS, IEC61850_BOOLEAN); @@ -273,7 +273,7 @@ CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newDPS, IEC61850_CODEDENUM); @@ -291,7 +291,7 @@ CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newINS = DataObject_create(dataObjectName, parent, 0); + DataObject* newINS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newINS, IEC61850_INT32); @@ -310,7 +310,7 @@ CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newENS = DataObject_create(dataObjectName, parent, 0); + DataObject* newENS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newENS, IEC61850_ENUMERATED); @@ -328,7 +328,7 @@ CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newBCR = DataObject_create(dataObjectName, parent, 0); + DataObject* newBCR = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("actVal", (ModelNode*) newBCR, IEC61850_INT64, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -359,7 +359,7 @@ CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSEC = DataObject_create(dataObjectName, parent, 0); + DataObject* newSEC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("cnt", (ModelNode*) newSEC, IEC61850_INT32U, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("sev", (ModelNode*) newSEC, IEC61850_ENUMERATED, IEC61850_FC_ST, 0, 0, 0); @@ -380,7 +380,7 @@ CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newSPS, IEC61850_VISIBLE_STRING_255); @@ -403,7 +403,7 @@ CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newMV = DataObject_create(dataObjectName, parent, 0); + DataObject* newMV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (options & CDC_OPTION_INST_MAG) CAC_AnalogueValue_create("instMag", (ModelNode*) newMV, IEC61850_FC_MX, 0, isIntegerNotFloat); @@ -432,7 +432,7 @@ CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, b DataObject* CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newMV = DataObject_create(dataObjectName, parent, 0); + DataObject* newMV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (options & CDC_OPTION_INST_MAG) CAC_Vector_create("instCVal", (ModelNode*) newMV, options, IEC61850_FC_MX, 0); @@ -465,7 +465,7 @@ CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newSAV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSAV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CAC_AnalogueValue_create("instMag", (ModelNode*) newSAV, IEC61850_FC_MX, 0, isIntegerNotFloat); @@ -491,7 +491,7 @@ CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_HST_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint16_t maxPts) { - DataObject* newHST = DataObject_create(dataObjectName, parent, 0); + DataObject* newHST = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("hstVal", (ModelNode*) newHST, IEC61850_INT32, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED | TRG_OPT_DATA_UPDATE, maxPts, 0); @@ -577,7 +577,7 @@ addCommonControlAttributes(DataObject* dobj, uint32_t controlOptions) DataObject* CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newSPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newSPC, controlOptions); @@ -613,7 +613,7 @@ CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_DPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newDPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newDPC, controlOptions); @@ -695,7 +695,7 @@ addControlStatusAttributesForAnalogControl(DataObject* dobj, uint32_t controlOpt DataObject* CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { - DataObject* newAPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newAPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addControlStatusAttributesForAnalogControl(newAPC, controlOptions); @@ -728,7 +728,7 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newINC = DataObject_create(dataObjectName, parent, 0); + DataObject* newINC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newINC, controlOptions); @@ -764,7 +764,7 @@ CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newENC = DataObject_create(dataObjectName, parent, 0); + DataObject* newENC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newENC, controlOptions); @@ -791,7 +791,7 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) { - DataObject* newBSC = DataObject_create(dataObjectName, parent, 0); + DataObject* newBSC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newBSC, controlOptions); @@ -821,7 +821,7 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) { - DataObject* newISC = DataObject_create(dataObjectName, parent, 0); + DataObject* newISC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newISC, controlOptions); @@ -855,7 +855,7 @@ CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { - DataObject* newBAC = DataObject_create(dataObjectName, parent, 0); + DataObject* newBAC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addControlStatusAttributesForAnalogControl(newBAC, controlOptions); @@ -899,7 +899,7 @@ CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newLPL = DataObject_create(dataObjectName, parent, 0); + DataObject* newLPL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("vendor", (ModelNode*) newLPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); DataAttribute_create("swRev", (ModelNode*) newLPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); @@ -921,7 +921,7 @@ CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDPL = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("vendor", (ModelNode*) newDPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); @@ -949,7 +949,7 @@ CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newACD = DataObject_create(dataObjectName, parent, 0); + DataObject* newACD = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("general", (ModelNode*) newACD, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("dirGeneral", (ModelNode*) newACD, IEC61850_ENUMERATED, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -984,7 +984,7 @@ CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newACT = DataObject_create(dataObjectName, parent, 0); + DataObject* newACT = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("general", (ModelNode*) newACT, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1010,7 +1010,7 @@ CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newWYE = DataObject_create(dataObjectName, parent, 0); + DataObject* newWYE = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); /* TODO check if some options should be masked */ /* TODO take care for GC_1 */ @@ -1033,7 +1033,7 @@ CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDEL = DataObject_create(dataObjectName, parent, 0); + DataObject* newDEL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); /* TODO check if some options should be masked */ CDC_CMV_create("phsAB", (ModelNode*) newDEL, options); @@ -1052,7 +1052,7 @@ CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPG = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_BOOLEAN, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1064,7 +1064,7 @@ CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPG = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_VISIBLE_STRING_255, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1077,7 +1077,7 @@ CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newENG = DataObject_create(dataObjectName, parent, 0); + DataObject* newENG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newENG, IEC61850_ENUMERATED, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1089,7 +1089,7 @@ CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newING = DataObject_create(dataObjectName, parent, 0); + DataObject* newING = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newING, IEC61850_INT32, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1114,7 +1114,7 @@ CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newASG = DataObject_create(dataObjectName, parent, 0); + DataObject* newASG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CAC_AnalogueValue_create("setMag", (ModelNode*) newASG, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, isIntegerNotFloat); @@ -1145,7 +1145,7 @@ CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_SPV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, uint32_t wpOptions, bool hasChaManRs) { - DataObject* newSPV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (hasChaManRs) CDC_SPC_create("chaManRs", (ModelNode*) newSPV, 0, CDC_CTL_MODEL_DIRECT_NORMAL); @@ -1196,7 +1196,7 @@ CDC_STV_create(const char* dataObjectName, ModelNode* parent, (void)controlOptions; /* TODO implement */ (void)wpOptions; /* TODO implement */ - DataObject* newSTV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSTV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_INS_create("actSt", (ModelNode*) newSTV, 0); @@ -1218,7 +1218,7 @@ CDC_ALM_create(const char* dataObjectName, ModelNode* parent, (void)controlOptions; /* TODO implement */ (void)wpOptions; /* TODO implement */ - DataObject* newALM = DataObject_create(dataObjectName, parent, 0); + DataObject* newALM = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("almAck", (ModelNode*) newALM, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1244,7 +1244,7 @@ CDC_CMD_create(const char* dataObjectName, ModelNode* parent, (void)hasCmTm; /* TODO implement */ (void)hasCmCt; /* TODO implement */ - DataObject* newCMD = DataObject_create(dataObjectName, parent, 0); + DataObject* newCMD = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_INC_create("actSt", (ModelNode*) newCMD, 0, controlOptions); @@ -1272,7 +1272,7 @@ CDC_CTE_create(const char* dataObjectName, ModelNode* parent, { (void)controlOptions; /* TODO implement */ - DataObject* newCTE = DataObject_create(dataObjectName, parent, 0); + DataObject* newCTE = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("manRs", (ModelNode*) newCTE, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1313,7 +1313,7 @@ CDC_TMS_create(const char* dataObjectName, ModelNode* parent, { (void)controlOptions; /* TODO implement */ - DataObject* newTMS = DataObject_create(dataObjectName, parent, 0); + DataObject* newTMS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("manRs", (ModelNode*) newTMS, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1344,4 +1344,3 @@ CDC_TMS_create(const char* dataObjectName, ModelNode* parent, return newTMS; } - diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c index 8f2abeca..f9489375 100644 --- a/src/iec61850/server/model/config_file_parser.c +++ b/src/iec61850/server/model/config_file_parser.c @@ -1,7 +1,7 @@ /* * config_file_parser.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -28,9 +28,9 @@ #include "libiec61850_platform_includes.h" #include "stack_config.h" -#define READ_BUFFER_MAX_SIZE 1024 +#include -static uint8_t lineBuffer[READ_BUFFER_MAX_SIZE]; +#define READ_BUFFER_MAX_SIZE 1024 static int readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize) @@ -114,18 +114,132 @@ ConfigFileParser_createModelFromConfigFileEx(const char* filename) return model; } +static bool +setValue(char* lineBuffer, DataAttribute* dataAttribute) +{ + char* valueIndicator = strchr((char*) lineBuffer, '='); + + if (valueIndicator != NULL) { + switch (dataAttribute->type) { + case IEC61850_UNICODE_STRING_255: + { + char* stringStart = valueIndicator + 2; + terminateString(stringStart, '"'); + dataAttribute->mmsValue = MmsValue_newMmsString(stringStart); + } + break; + + case IEC61850_VISIBLE_STRING_255: + case IEC61850_VISIBLE_STRING_129: + case IEC61850_VISIBLE_STRING_65: + case IEC61850_VISIBLE_STRING_64: + case IEC61850_VISIBLE_STRING_32: + case IEC61850_CURRENCY: + { + char* stringStart = valueIndicator + 2; + terminateString(stringStart, '"'); + dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart); + } + break; + + case IEC61850_INT8: + case IEC61850_INT16: + case IEC61850_INT32: + case IEC61850_INT64: + case IEC61850_INT128: + case IEC61850_ENUMERATED: + { + int32_t intValue; + if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue); + } + break; + + case IEC61850_INT8U: + case IEC61850_INT16U: + case IEC61850_INT24U: + case IEC61850_INT32U: + { + uint32_t uintValue; + if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue); + } + break; + + case IEC61850_FLOAT32: + { + float floatValue; + if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newFloat(floatValue); + } + break; + + case IEC61850_FLOAT64: + { + double doubleValue; + if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newDouble(doubleValue); + } + break; + + case IEC61850_BOOLEAN: + { + int boolean; + if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean); + } + break; + + case IEC61850_OPTFLDS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(-10); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + + case IEC61850_TRGOPS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(-6); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + + default: + break; + + } + } + + return true; + +exit_error: + return false; +} + IedModel* ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) { + uint8_t* lineBuffer = (uint8_t*)GLOBAL_MALLOC(READ_BUFFER_MAX_SIZE); + + if (lineBuffer == NULL) + goto exit_error; + int bytesRead = 1; bool stateInModel = false; int indendation = 0; + bool inArray = false; + bool inArrayElement = false; IedModel* model = NULL; LogicalDevice* currentLD = NULL; LogicalNode* currentLN = NULL; ModelNode* currentModelNode = NULL; + ModelNode* currentArrayNode = NULL; DataSet* currentDataSet = NULL; GSEControlBlock* currentGoCB = NULL; SVControlBlock* currentSMVCB = NULL; @@ -136,16 +250,30 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) int currentLine = 0; - while (bytesRead > 0) { + while (bytesRead > 0) + { bytesRead = readLine(fileHandle, lineBuffer, READ_BUFFER_MAX_SIZE); currentLine++; - if (bytesRead > 0) { + if (bytesRead > 0) + { lineBuffer[bytesRead] = 0; - if (stateInModel) { + /* trim trailing spaces */ + while (bytesRead > 1) { + bytesRead--; + + if (isspace(lineBuffer[bytesRead])) { + lineBuffer[bytesRead] = 0; + } + else { + break; + } + } + if (stateInModel) + { if (StringUtils_startsWith((char*) lineBuffer, "}")) { if (indendation == 1) { stateInModel = false; @@ -161,13 +289,25 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) indendation = 3; } else if (indendation > 4) { + + if (inArrayElement && currentModelNode->parent == currentArrayNode) { + inArrayElement = false; + } + else { + indendation--; + } + + if (inArray && currentModelNode == currentArrayNode) { + inArray = false; + } + currentModelNode = currentModelNode->parent; - indendation--; } } - - else if (indendation == 1) { - if (StringUtils_startsWith((char*) lineBuffer, "LD")) { + else if (indendation == 1) + { + if (StringUtils_startsWith((char*) lineBuffer, "LD")) + { indendation = 2; char ldName[65]; @@ -178,7 +318,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) terminateString(nameString, ')'); - if (ldName[0] != 0) { + if (ldName[0] != 0) + { terminateString(ldName, ')'); currentLD = LogicalDevice_createEx(nameString, model, ldName); @@ -190,8 +331,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - else if (indendation == 2) { - if (StringUtils_startsWith((char*) lineBuffer, "LN")) { + else if (indendation == 2) + { + if (StringUtils_startsWith((char*) lineBuffer, "LN")) + { indendation = 3; if (sscanf((char*) lineBuffer, "LN(%129s)", nameString) < 1) @@ -204,26 +347,35 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - else if (indendation == 3) { - if (StringUtils_startsWith((char*) lineBuffer, "DO")) { + else if (indendation == 3) + { + if (StringUtils_startsWith((char*) lineBuffer, "DO")) + { indendation = 4; int arrayElements = 0; - sscanf((char*) lineBuffer, "DO(%129s %i)", nameString, &arrayElements); + if (sscanf((char*)lineBuffer, "DO(%129s %i)", nameString, &arrayElements) != 2) { + goto exit_error; + } currentModelNode = (ModelNode*) DataObject_create(nameString, (ModelNode*) currentLN, arrayElements); } - else if (StringUtils_startsWith((char*) lineBuffer, "DS")) { + else if (StringUtils_startsWith((char*) lineBuffer, "DS")) + { indendation = 4; - sscanf((char*) lineBuffer, "DS(%129s)", nameString); + if (sscanf((char*)lineBuffer, "DS(%129s)", nameString) != 1) { + goto exit_error; + } + terminateString(nameString, ')'); currentDataSet = DataSet_create(nameString, currentLN); } - else if (StringUtils_startsWith((char*) lineBuffer, "RC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "RC")) + { int isBuffered; uint32_t confRef; int trgOps; @@ -250,7 +402,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) ReportControlBlock_create(nameString, currentLN, rptId, (bool) isBuffered, dataSetName, confRef, trgOps, options, bufTm, intgPd); } - else if (StringUtils_startsWith((char*) lineBuffer, "LC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "LC")) + { uint32_t trgOps; uint32_t intgPd; int logEna; @@ -271,7 +424,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) LogControlBlock_create(nameString, currentLN, dataSet, logRef, trgOps, intgPd, logEna, withReasonCode); } - else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) { + else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) + { int matchedItems = sscanf((char*) lineBuffer, "LOG(%129s)", nameString); if (matchedItems < 1) goto exit_error; @@ -281,7 +435,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) Log_create(nameString, currentLN); } - else if (StringUtils_startsWith((char*) lineBuffer, "GC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "GC")) + { uint32_t confRef; int fixedOffs; int minTime = -1; @@ -296,9 +451,9 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) nameString3, confRef, fixedOffs, minTime, maxTime); indendation = 4; - } - else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) + { uint32_t confRev; int smpMod; int smpRate; @@ -313,10 +468,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) currentSMVCB = SVControlBlock_create(nameString, currentLN, nameString2, nameString3, confRev, smpMod, smpRate, optFlds, (bool) isUnicast); indendation = 4; - } #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - else if (StringUtils_startsWith((char*) lineBuffer, "SG")) { + else if (StringUtils_startsWith((char*) lineBuffer, "SG")) + { if (strcmp(currentLN->name, "LLN0") != 0) { if (DEBUG_IED_SERVER) @@ -345,8 +500,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) } } - else if (indendation > 3) { - if (StringUtils_startsWith((char*) lineBuffer, "DO")) { + else if (indendation > 3) + { + if (StringUtils_startsWith((char*) lineBuffer, "DO")) + { indendation++; int arrayElements = 0; @@ -356,129 +513,116 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) if (matchedItems != 2) goto exit_error; currentModelNode = (ModelNode*) DataObject_create(nameString, currentModelNode, arrayElements); - } - else if (StringUtils_startsWith((char*) lineBuffer, "DA")) { - int arrayElements = 0; + if (arrayElements > 0) + { + inArray = true; + currentArrayNode = currentModelNode; + } + } + else if (StringUtils_startsWith((char*) lineBuffer, "[")) + { + if (inArray == false) { + goto exit_error; + } - int attributeType = 0; - int functionalConstraint = 0; - int triggerOptions = 0; - uint32_t sAddr = 0; + int arrayIndex; - sscanf((char*) lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr); + if (sscanf((char*)lineBuffer, "[%i]", &arrayIndex) != 1) { + goto exit_error; + } - DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode, - (DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr); + if (arrayIndex < 0) { + goto exit_error; + } - char* valueIndicator = strchr((char*) lineBuffer, '='); + if (currentArrayNode->modelType == DataAttributeModelType) + { + if (StringUtils_endsWith((char*)lineBuffer, ";")) + { + /* array of basic data attribute */ + ModelNode* arrayElementNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); - if (valueIndicator != NULL) { - switch (dataAttribute->type) { - case IEC61850_UNICODE_STRING_255: - { - char* stringStart = valueIndicator + 2; - terminateString(stringStart, '"'); - dataAttribute->mmsValue = MmsValue_newMmsString(stringStart); - } - break; - - case IEC61850_VISIBLE_STRING_255: - case IEC61850_VISIBLE_STRING_129: - case IEC61850_VISIBLE_STRING_65: - case IEC61850_VISIBLE_STRING_64: - case IEC61850_VISIBLE_STRING_32: - case IEC61850_CURRENCY: - { - char* stringStart = valueIndicator + 2; - terminateString(stringStart, '"'); - dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart); - } - break; - - case IEC61850_INT8: - case IEC61850_INT16: - case IEC61850_INT32: - case IEC61850_INT64: - case IEC61850_INT128: - case IEC61850_ENUMERATED: - { - int32_t intValue; - if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue); + if (arrayElementNode) { + setValue((char*)lineBuffer, (DataAttribute*)arrayElementNode); } - break; - - case IEC61850_INT8U: - case IEC61850_INT16U: - case IEC61850_INT24U: - case IEC61850_INT32U: - { - uint32_t uintValue; - if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue); + else { + goto exit_error; } - break; - - case IEC61850_FLOAT32: - { - float floatValue; - if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newFloat(floatValue); - } - break; + } + else if (StringUtils_endsWith((char*)lineBuffer, "{")) + { + /* array of constructed data attribtute */ + currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); - case IEC61850_FLOAT64: - { - double doubleValue; - if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newDouble(doubleValue); + if (currentModelNode) { + inArrayElement = true; } - break; - - case IEC61850_BOOLEAN: - { - int boolean; - if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean); + else { + goto exit_error; } - break; - - case IEC61850_OPTFLDS: - { - int value; - if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBitString(-10); - MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + } + else if (currentArrayNode->modelType == DataObjectModelType) + { + if (StringUtils_endsWith((char*)lineBuffer, "{")) + { + /* array of constructed data attribtute */ + currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); + + if (currentModelNode) { + inArrayElement = true; } - break; - - case IEC61850_TRGOPS: - { - int value; - if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBitString(-6); - MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + else { + goto exit_error; } - break; + } + else + { + if (DEBUG_IED_SERVER) + printf("Unexpected character at end of line: %s\n", lineBuffer); + goto exit_error; + } + } + } + else if (StringUtils_startsWith((char*) lineBuffer, "DA")) + { + int arrayElements = 0; + + int attributeType = 0; + int functionalConstraint = 0; + int triggerOptions = 0; + uint32_t sAddr = 0; - default: - break; + if (sscanf((char*)lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr) != 6) { + goto exit_error; + } - } + DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode, + (DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr); + + if (arrayElements > 0) + { + inArray = true; + currentArrayNode = (ModelNode*)dataAttribute; } + setValue((char*)lineBuffer, dataAttribute); + int lineLength = (int) strlen((char*) lineBuffer); - if (lineBuffer[lineLength - 1] == '{') { + if (lineBuffer[lineLength - 1] == '{') + { indendation++; currentModelNode = (ModelNode*) dataAttribute; } } - else if (StringUtils_startsWith((char*) lineBuffer, "DE")) { + else if (StringUtils_startsWith((char*) lineBuffer, "DE")) + { char* start = strchr((char*) lineBuffer, '('); - if (start) { + if (start) + { start++; StringUtils_copyStringMax(nameString, 130, start); @@ -491,7 +635,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) /* check for index */ char* sep = strchr(nameString, ' '); - if (sep) { + if (sep) + { char* indexStr = sep + 1; *sep = 0; @@ -509,7 +654,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) DataSetEntry_create(currentDataSet, nameString, indexVal, componentVal); } } - else if (StringUtils_startsWith((char*) lineBuffer, "PA")) { + else if (StringUtils_startsWith((char*) lineBuffer, "PA")) + { uint32_t vlanPrio; uint32_t vlanId; uint32_t appId; @@ -525,7 +671,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) if (StringUtils_createBufferFromHexString(nameString, (uint8_t*) nameString2) != 6) goto exit_error; - PhyComAddress* dstAddress = PhyComAddress_create((uint8_t) vlanPrio, (uint16_t) vlanId, (uint16_t) appId, (uint8_t*) nameString2); @@ -541,18 +686,20 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - - } - else { - if (StringUtils_startsWith((char*) lineBuffer, "MODEL{")) { - + else + { + if (StringUtils_startsWith((char*) lineBuffer, "MODEL{")) + { model = IedModel_create(""); stateInModel = true; indendation = 1; } - else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) { - sscanf((char*) lineBuffer, "MODEL(%129s)", nameString); + else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) + { + if (sscanf((char*)lineBuffer, "MODEL(%129s)", nameString) != 1) + goto exit_error; + terminateString(nameString, ')'); model = IedModel_create(nameString); stateInModel = true; @@ -564,14 +711,18 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) } } + GLOBAL_FREEMEM(lineBuffer); + return model; exit_error: + + GLOBAL_FREEMEM(lineBuffer); + if (DEBUG_IED_SERVER) printf("IED_SERVER: error parsing line %i (indentation level = %i)\n", currentLine, indendation); IedModel_destroy(model); + return NULL; } - - diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index f9fb87af..2290918e 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -1,7 +1,7 @@ /* * dynamic_model.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -75,10 +75,12 @@ IedModel_addDataSet(IedModel* self, DataSet* dataSet) { if (self->dataSets == NULL) self->dataSets = dataSet; - else { + else + { DataSet* lastDataSet = self->dataSets; - while (lastDataSet != NULL) { + while (lastDataSet != NULL) + { if (lastDataSet->sibling == NULL) { lastDataSet->sibling = dataSet; break; @@ -94,7 +96,8 @@ IedModel_addLogicalDevice(IedModel* self, LogicalDevice* lDevice) { if (self->firstChild == NULL) self->firstChild = lDevice; - else { + else + { LogicalDevice* sibling = self->firstChild; while (sibling->sibling != NULL) @@ -109,7 +112,8 @@ IedModel_addLog(IedModel* self, Log* log) { if (self->logs == NULL) self->logs = log; - else { + else + { Log* lastLog = self->logs; while (lastLog->sibling != NULL) @@ -124,7 +128,8 @@ IedModel_addLogControlBlock(IedModel* self, LogControlBlock* lcb) { if (self->lcbs == NULL) self->lcbs = lcb; - else { + else + { LogControlBlock* lastLcb = self->lcbs; while (lastLcb->sibling != NULL) @@ -139,7 +144,8 @@ IedModel_addReportControlBlock(IedModel* self, ReportControlBlock* rcb) { if (self->rcbs == NULL) self->rcbs = rcb; - else { + else + { ReportControlBlock* lastRcb = self->rcbs; while (lastRcb->sibling != NULL) @@ -155,7 +161,8 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s { if (self->sgcbs == NULL) self->sgcbs = sgcb; - else { + else + { SettingGroupControlBlock* lastSgcb = self->sgcbs; while (lastSgcb->sibling != NULL) @@ -166,28 +173,46 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s } #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ - static void IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb) { if (self->gseCBs == NULL) self->gseCBs = gcb; - else { + else + { GSEControlBlock* lastGcb = self->gseCBs; - while (lastGcb->sibling != NULL) + while (lastGcb->sibling) lastGcb = lastGcb->sibling; lastGcb->sibling = gcb; } } +static void +IedModel_addSMVControlBlock(IedModel* self, SVControlBlock* smvcb) +{ + if (self->svCBs == NULL) { + self->svCBs = smvcb; + } + else + { + SVControlBlock* lastSvCB = self->svCBs; + + while (lastSvCB->sibling) + lastSvCB = lastSvCB->sibling; + + lastSvCB->sibling = smvcb; + } +} + LogicalDevice* LogicalDevice_createEx(const char* inst, IedModel* parent, const char* ldName) { LogicalDevice* self = (LogicalDevice*) GLOBAL_CALLOC(1, sizeof(LogicalDevice)); - if (self) { + if (self) + { self->name = StringUtils_copyString(inst); self->modelType = LogicalDeviceModelType; self->parent = (ModelNode*) parent; @@ -242,7 +267,8 @@ LogicalNode_create(const char* name, LogicalDevice* parent) { LogicalNode* self = (LogicalNode*) GLOBAL_MALLOC(sizeof(LogicalNode)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = (ModelNode*) parent; self->modelType = LogicalNodeModelType; @@ -296,7 +322,8 @@ Log_create(const char* name, LogicalNode* parent) { Log* self = (Log*) GLOBAL_MALLOC(sizeof(Log)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; self->sibling = NULL; @@ -321,7 +348,8 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe { LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; self->sibling = NULL; @@ -373,7 +401,8 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rpt { ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; @@ -454,7 +483,8 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO SettingGroupControlBlock* self = (SettingGroupControlBlock*) GLOBAL_MALLOC(sizeof(SettingGroupControlBlock)); - if (self) { + if (self) + { self->parent = parent; self->actSG = actSG; self->numOfSGs = numOfSGs; @@ -482,7 +512,8 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, { GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; @@ -512,13 +543,22 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, return self; } +static void +LogicalNode_addSMVControlBlock(LogicalNode* self, SVControlBlock* smvcb) +{ + IedModel* model = (IedModel*) self->parent->parent; + + IedModel_addSMVControlBlock(model, smvcb); +} + SVControlBlock* SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod, uint16_t smpRate, uint8_t optFlds, bool isUnicast) { SVControlBlock* self = (SVControlBlock*) GLOBAL_MALLOC(sizeof(SVControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; @@ -536,6 +576,12 @@ SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, c self->optFlds = optFlds; self->isUnicast = isUnicast; + + self->dstAddress = NULL; + self->sibling = NULL; + + if (parent) + LogicalNode_addSMVControlBlock(parent, self); } return self; @@ -558,7 +604,8 @@ PhyComAddress_create(uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint { PhyComAddress* self = (PhyComAddress*) GLOBAL_MALLOC(sizeof(PhyComAddress)); - if (self) { + if (self) + { self->vlanPriority = vlanPriority; self->vlanId = vlanId; self->appId = appId; @@ -601,7 +648,8 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements) { DataObject* self = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->modelType = DataObjectModelType; self->firstChild = NULL; @@ -624,7 +672,7 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements) arrayElement->name = NULL; arrayElement->modelType = DataObjectModelType; arrayElement->firstChild = NULL; - arrayElement->parent = parent; + arrayElement->parent = (ModelNode*) self; arrayElement->sibling = NULL; arrayElement->elementCount = 0; @@ -677,7 +725,8 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type { DataAttribute* self = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->elementCount = arrayElements; self->arrayIndex = -1; @@ -691,13 +740,16 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type self->triggerOptions = triggerOptions; self->sAddr = sAddr; - if (arrayElements > 0) { + if (arrayElements > 0) + { int i; - for (i = 0; i < arrayElements; i++) { + for (i = 0; i < arrayElements; i++) + { DataAttribute* arrayElement = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute)); - if (arrayElement) { + if (arrayElement) + { arrayElement->name = NULL; arrayElement->elementCount = 0; arrayElement->arrayIndex = i; @@ -759,7 +811,8 @@ DataSet_create(const char* name, LogicalNode* parent) { DataSet* self = (DataSet*) GLOBAL_MALLOC(sizeof(DataSet)); - if (self) { + if (self) + { LogicalDevice* ld = (LogicalDevice*) parent->parent; self->name = StringUtils_createString(3, parent->name, "$", name); @@ -805,11 +858,12 @@ DataSet_addEntry(DataSet* self, DataSetEntry* newEntry) if (self->fcdas == NULL) self->fcdas = newEntry; - else { + else + { DataSetEntry* lastEntry = self->fcdas; - while (lastEntry != NULL) { - + while (lastEntry != NULL) + { if (lastEntry->sibling == NULL) { lastEntry->sibling = newEntry; break; @@ -825,21 +879,24 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha { DataSetEntry* self = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry)); - if (self) { + if (self) + { char variableName[130]; StringUtils_copyStringMax(variableName, 130, variable); char* separator = strchr(variableName, '/'); - if (separator != NULL) { + if (separator != NULL) + { *separator = 0; self->variableName = StringUtils_copyString(separator + 1); self->logicalDeviceName = StringUtils_copyString(variableName); self->isLDNameDynamicallyAllocated = true; } - else { + else + { self->variableName = StringUtils_copyString(variable); self->logicalDeviceName = dataSet->logicalDeviceName; self->isLDNameDynamicallyAllocated = false; @@ -865,14 +922,15 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha static void ModelNode_destroy(ModelNode* modelNode) { - if (modelNode) { - + if (modelNode) + { if (modelNode->name) GLOBAL_FREEMEM(modelNode->name); ModelNode* currentChild = modelNode->firstChild; - while (currentChild != NULL) { + while (currentChild != NULL) + { ModelNode* nextChild = currentChild->sibling; ModelNode_destroy(currentChild); @@ -880,7 +938,8 @@ ModelNode_destroy(ModelNode* modelNode) currentChild = nextChild; } - if (modelNode->modelType == DataAttributeModelType) { + if (modelNode->modelType == DataAttributeModelType) + { DataAttribute* dataAttribute = (DataAttribute*) modelNode; if (dataAttribute->mmsValue != NULL) { @@ -903,8 +962,8 @@ IedModel_destroy(IedModel* model) LogicalDevice* ld = model->firstChild; - while (ld != NULL) { - + while (ld != NULL) + { if (ld->name) GLOBAL_FREEMEM(ld->name); @@ -913,7 +972,8 @@ IedModel_destroy(IedModel* model) LogicalNode* ln = (LogicalNode*) ld->firstChild; - while (ln != NULL) { + while (ln != NULL) + { GLOBAL_FREEMEM(ln->name); /* delete all data objects */ @@ -934,7 +994,6 @@ IedModel_destroy(IedModel* model) GLOBAL_FREEMEM(currentLn); } - LogicalDevice* currentLd = ld; ld = (LogicalDevice*) ld->sibling; @@ -1066,4 +1125,3 @@ IedModel_destroy(IedModel* model) GLOBAL_FREEMEM(model); } } - diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c index b15cfdb8..53d3b38b 100644 --- a/src/iec61850/server/model/model.c +++ b/src/iec61850/server/model/model.c @@ -538,19 +538,34 @@ static int createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool withoutIedName) { int bufPos; + int arrayIndex = -1; - if (node->modelType != LogicalNodeModelType) { + if (node->modelType != LogicalNodeModelType) + { bufPos = createObjectReference(node->parent, objectReference, bufSize, withoutIedName); + if (node->modelType == DataAttributeModelType) + { + arrayIndex = ((DataAttribute*)(node))->arrayIndex; + } + else if (node->modelType == DataObjectModelType) + { + arrayIndex = ((DataObject*)(node))->arrayIndex; + } + if (bufPos == -1) return -1; - if (bufPos < bufSize) - objectReference[bufPos++] = '.'; - else - return -1; + if (arrayIndex < 0) + { + if (bufPos < bufSize) + objectReference[bufPos++] = '.'; + else + return -1; + } } - else { + else + { LogicalNode* lNode = (LogicalNode*) node; LogicalDevice* lDevice = (LogicalDevice*) lNode->parent; @@ -559,12 +574,13 @@ createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool bufPos = 0; - if (withoutIedName) { + if (withoutIedName) + { objectReference[0] = 0; StringUtils_appendString(objectReference, bufSize, lDevice->name); } - else { - + else + { if (lDevice->ldName) { StringUtils_copyStringMax(objectReference, bufSize, lDevice->ldName); } @@ -581,20 +597,49 @@ createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool return -1; } - /* append own name */ - int nameLength = strlen(node->name); + if (node->name) + { + /* append own name */ + int nameLength = strlen(node->name); - if (bufPos + nameLength < bufSize) { - int i; - for (i = 0; i < nameLength; i++) { - objectReference[bufPos++] = node->name[i]; - } + if (bufPos + nameLength < bufSize) + { + int i; + for (i = 0; i < nameLength; i++) { + objectReference[bufPos++] = node->name[i]; + } - return bufPos; + return bufPos; + } + else { + return -1; + } } - else { - return -1; + + if (arrayIndex > -1) + { + char arrayIndexStr[11]; + + snprintf(arrayIndexStr, 11, "%d", arrayIndex); + + int arrayIndexStrLength = strlen(arrayIndexStr); + + if (bufPos + arrayIndexStrLength + 2 < bufSize) + { + int i; + + objectReference[bufPos++] = '('; + + for (i = 0; i < arrayIndexStrLength; i++) { + objectReference[bufPos++] = arrayIndexStr[i]; + } + objectReference[bufPos++] = ')'; + } + else + return -1; } + + return bufPos; } char* @@ -608,15 +653,18 @@ ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool with { bool allocated = false; - if (objectReference == NULL) { + if (objectReference == NULL) + { objectReference = (char*) GLOBAL_MALLOC(130); allocated = true; } - if (objectReference) { + if (objectReference) + { int bufPos = createObjectReference(node, objectReference, 130, withoutIedName); - if (bufPos == -1) { + if (bufPos == -1) + { if (allocated) GLOBAL_FREEMEM(objectReference); @@ -659,26 +707,30 @@ ModelNode_getChild(ModelNode* self, const char* name) /* check for array separator */ const char* arraySeparator = strchr(name, '('); - if (arraySeparator) { - + if (arraySeparator) + { const char* arraySeparator2 = strchr(arraySeparator, ')'); - if (arraySeparator2) { + if (arraySeparator2) + { int idx = (int) strtol(arraySeparator + 1, NULL, 10); ModelNode* arrayNode = NULL; - if (name == arraySeparator) { + if (name == arraySeparator) + { arrayNode = ModelNode_getChildWithIdx(self, idx); } - else { + else + { char nameCopy[65]; const char* pos = name; int cpyIdx = 0; - while (pos < arraySeparator) { + while (pos < arraySeparator) + { nameCopy[cpyIdx] = *pos; cpyIdx++; pos++; @@ -695,18 +747,19 @@ ModelNode_getChild(ModelNode* self, const char* name) return NULL; } - if (arrayNode) { - - if (*(arraySeparator2 + 1) == 0) { + if (arrayNode) + { + if (*(arraySeparator2 + 1) == 0) + { return arrayNode; } - else { + else + { if (*(arraySeparator2 + 1) == '.') return ModelNode_getChild(arrayNode, arraySeparator2 + 2); else return ModelNode_getChild(arrayNode, arraySeparator2 + 1); } - } else return NULL; @@ -729,17 +782,18 @@ ModelNode_getChild(ModelNode* self, const char* name) ModelNode* matchingNode = NULL; - while (nextNode) { - + while (nextNode) + { if (nextNode->name == NULL) { break; /* is an array element */ } int nodeNameLen = strlen(nextNode->name); - if (nodeNameLen == nameElementLength) { - - if (memcmp(nextNode->name, name, nodeNameLen) == 0) { + if (nodeNameLen == nameElementLength) + { + if (memcmp(nextNode->name, name, nodeNameLen) == 0) + { matchingNode = nextNode; break; } @@ -768,7 +822,8 @@ ModelNode_getChildWithIdx(ModelNode* self, int idx) while (nextNode) { - if (currentIdx == idx) { + if (currentIdx == idx) + { foundElement = nextNode; break; } @@ -799,14 +854,18 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint ModelNode* matchingNode = NULL; - while (nextNode != NULL) { + while (nextNode != NULL) + { int nodeNameLen = strlen(nextNode->name); - if (nodeNameLen == nameElementLength) { - if (memcmp(nextNode->name, name, nodeNameLen) == 0) { - - if (separator == NULL) { - if (nextNode->modelType == DataAttributeModelType) { + if (nodeNameLen == nameElementLength) + { + if (memcmp(nextNode->name, name, nodeNameLen) == 0) + { + if (separator == NULL) + { + if (nextNode->modelType == DataAttributeModelType) + { DataAttribute* da = (DataAttribute*) nextNode; if (da->fc == fc) { @@ -815,12 +874,14 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint } } } - else { - - if (nextNode->modelType == DataAttributeModelType) { + else + { + if (nextNode->modelType == DataAttributeModelType) + { DataAttribute* da = (DataAttribute*) nextNode; - if (da->fc == fc) { + if (da->fc == fc) + { matchingNode = nextNode; break; } @@ -829,7 +890,6 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint matchingNode = nextNode; break; } - } } } diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 86f12c9b..7447b33b 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -160,6 +160,16 @@ MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath); LIB61850_API void MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs); +/** + * \brief Set the maximum number outstanding calls allowed for this connection + * + * \param self MmsConnection instance to operate on + * \param calling the maximum outstanding calls allowed by the caller (client) + * \param called the maximum outstanding calls allowed by the called endpoint (server) + */ +LIB61850_API void +MmsConnnection_setMaxOutstandingCalls(MmsConnection self, int calling, int called); + /** * \brief Get the request timeout in ms for this connection * diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index aa0e60f3..5a7b2242 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -95,6 +95,8 @@ struct sMmsConnection { Semaphore outstandingCallsLock; MmsOutstandingCall outstandingCalls; + int maxOutstandingCalled; + int maxOutstandingCalling; uint32_t requestTimeout; uint32_t connectTimeout; diff --git a/src/mms/inc_private/mms_common_internal.h b/src/mms/inc_private/mms_common_internal.h index f096e7bc..61faea58 100644 --- a/src/mms/inc_private/mms_common_internal.h +++ b/src/mms/inc_private/mms_common_internal.h @@ -1,7 +1,7 @@ /* * mms_common_internal.h * - * Copyright 2013-2019 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -30,10 +30,10 @@ #include "byte_buffer.h" #include "mms_server.h" -#define DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5 -#define DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5 #define DEFAULT_DATA_STRUCTURE_NESTING_LEVEL 10 +typedef struct sMmsOutstandingCall* MmsOutstandingCall; + #if (MMS_FILE_SERVICE == 1) #ifndef CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION @@ -42,8 +42,6 @@ #include "hal_filesystem.h" -typedef struct sMmsOutstandingCall* MmsOutstandingCall; - typedef struct { int32_t frsmId; uint32_t readPosition; diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index ec70d359..3fae4f26 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -120,6 +120,9 @@ struct sMmsServer { MmsWriteVariableHandler writeHandler; void* writeHandlerParameter; + MmsListAccessHandler listAccessHandler; + void* listAccessHandlerParameter; + MmsConnectionHandler connectionHandler; void* connectionHandlerParameter; @@ -396,7 +399,7 @@ mmsServer_isAccessToArrayComponent(AlternateAccess_t* alternateAccess); LIB61850_INTERNAL MmsValue* mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, - MmsValue* structuredValue); + MmsValue* structuredValue, char* componentId); LIB61850_INTERNAL int mmsServer_getLowIndex(AlternateAccess_t* alternateAccess); @@ -414,6 +417,10 @@ LIB61850_INTERNAL MmsDataAccessError mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value, MmsServerConnection connection); +LIB61850_INTERNAL MmsDataAccessError +mmsServer_setValueEx(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value, + MmsServerConnection connection, int arrayIdx, const char* componentId); + /** * \brief Get the current value of a variable in the server data model * @@ -422,6 +429,9 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va LIB61850_INTERNAL MmsValue* mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess); +LIB61850_INTERNAL bool +mmsServer_checkListAccess(MmsServer self, MmsGetNameListType listType, MmsDomain* domain, char* itemId, MmsServerConnection connection); + LIB61850_INTERNAL void mmsServer_createMmsWriteResponse(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index 444989c9..f7daa70c 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -34,9 +34,12 @@ typedef MmsDataAccessError (*MmsReadAccessHandler) (void* parameter, MmsDomain* char* variableId, MmsServerConnection connection, bool isDirectAccess); typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter, - MmsDomain* domain, char* variableId, MmsValue* value, + MmsDomain* domain, const char* variableId, int arrayIdx, const char* componentId, MmsValue* value, MmsServerConnection connection); +typedef bool (*MmsListAccessHandler) (void* parameter, MmsGetNameListType listType, MmsDomain* domain, + char* variableId, MmsServerConnection connection); + typedef void (*MmsConnectionHandler)(void* parameter, MmsServerConnection connection, MmsServerEvent event); @@ -63,6 +66,9 @@ LIB61850_INTERNAL void MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler, void* parameter); +LIB61850_INTERNAL void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter); + /** * A connection handler will be invoked whenever a new client connection is opened or closed */ diff --git a/src/mms/iso_acse/acse.c b/src/mms/iso_acse/acse.c index f46b024d..9f5b4186 100644 --- a/src/mms/iso_acse/acse.c +++ b/src/mms/iso_acse/acse.c @@ -1,7 +1,7 @@ /* * acse.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -43,9 +43,10 @@ checkAuthMechanismName(uint8_t* authMechanism, int authMechLen) { AcseAuthenticationMechanism authenticationMechanism = ACSE_AUTH_NONE; - if (authMechanism != NULL) { - - if (authMechLen == 3) { + if (authMechanism != NULL) + { + if (authMechLen == 3) + { if (memcmp(auth_mech_password_oid, authMechanism, 3) == 0) { authenticationMechanism = ACSE_AUTH_PASSWORD; } @@ -64,11 +65,13 @@ authenticateClient(AcseConnection* self, AcseAuthenticationMechanism mechanism, authParameter->mechanism = mechanism; - if (mechanism == ACSE_AUTH_PASSWORD) { + if (mechanism == ACSE_AUTH_PASSWORD) + { authParameter->value.password.octetString = authValue; authParameter->value.password.passwordLength = authValueLen; } - else if (mechanism == ACSE_AUTH_TLS) { + else if (mechanism == ACSE_AUTH_TLS) + { authParameter->value.certificate.buf = authValue; authParameter->value.certificate.length = authValueLen; } @@ -81,15 +84,15 @@ checkAuthentication(AcseConnection* self, uint8_t* authMechanism, int authMechLe { self->securityToken = NULL; - if (self->authenticator != NULL) { - + if (self->authenticator != NULL) + { AcseAuthenticationMechanism mechanism = checkAuthMechanismName(authMechanism, authMechLen); - if (mechanism == ACSE_AUTH_NONE) { - + if (mechanism == ACSE_AUTH_NONE) + { #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self->tlsSocket) { - + if (self->tlsSocket) + { int certLen; uint8_t* certBuf = TLSSocket_getPeerCertificate(self->tlsSocket, &certLen); @@ -120,13 +123,18 @@ parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxB bool hasindirectReference = false; bool isDataValid = false; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (len == 0) + continue; + + if ((bufPos < 0) || (bufPos + len > maxBufPos)) + { *userInfoValid = false; return -1; } @@ -155,7 +163,8 @@ parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxB } } - if (DEBUG_ACSE) { + if (DEBUG_ACSE) + { if (!hasindirectReference) printf("ACSE: User data has no indirect reference!\n"); @@ -181,13 +190,22 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) uint32_t result = 99; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) + + if (len == 0) + continue; + + if ((bufPos < 0) || (bufPos + len > maxBufPos)) + { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); return ACSE_ERROR; + } switch (tag) { @@ -212,12 +230,14 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) break; case 0xbe: /* user information */ - if (buffer[bufPos] != 0x28) { + if (buffer[bufPos] != 0x28) + { if (DEBUG_ACSE) printf("ACSE: invalid user info\n"); bufPos += len; } - else { + else + { bufPos++; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); @@ -263,13 +283,18 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) int authMechLen = 0; bool userInfoValid = false; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (len == 0) + continue; + + if ((bufPos < 0) || (bufPos + len > maxBufPos)) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -290,7 +315,9 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) case 0xa6: /* calling AP title */ { - if (buffer[bufPos] == 0x06) { /* ap-title-form2 */ + if (buffer[bufPos] == 0x06) + { + /* ap-title-form2 */ int innerLength = buffer[bufPos + 1]; @@ -303,7 +330,9 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) case 0xa7: /* calling AE qualifier */ { - if (buffer[bufPos] == 0x02) { /* ae-qualifier-form2 */ + if (buffer[bufPos] == 0x02) + { + /* ae-qualifier-form2 */ int innerLength = buffer[bufPos + 1]; @@ -328,7 +357,8 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) bufPos++; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -340,17 +370,20 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) break; case 0xbe: /* user information */ - if (buffer[bufPos] != 0x28) { + if (buffer[bufPos] != 0x28) + { if (DEBUG_ACSE) printf("ACSE: invalid user info\n"); bufPos += len; } - else { + else + { bufPos++; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -358,7 +391,8 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -378,14 +412,16 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) } } - if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false) { + if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false) + { if (DEBUG_ACSE) printf("ACSE: parseAarqPdu: check authentication failed!\n"); return ACSE_ASSOCIATE_FAILED; } - if (userInfoValid == false) { + if (userInfoValid == false) + { if (DEBUG_ACSE) printf("ACSE: parseAarqPdu: user info invalid!\n"); @@ -420,6 +456,14 @@ AcseConnection_parseMessage(AcseConnection* self, ByteBuffer* message) { AcseIndication indication = ACSE_ERROR; + if (message == NULL || message->size < 1) + { + if (DEBUG_ACSE) + printf("ACSE: invalid message - no payload\n"); + + return ACSE_ERROR; + } + uint8_t* buffer = message->buffer; int messageSize = message->size; @@ -826,4 +870,3 @@ AcseConnection_createReleaseResponseMessage(AcseConnection* self, BufferChain wr writeBuffer->length = 2; writeBuffer->nextPart = NULL; } - diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 4f37176b..ee2202a3 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -3,7 +3,7 @@ * * Client side representation of the ISO stack (COTP, session, presentation, ACSE) * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -146,7 +146,8 @@ IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCall { IsoClientConnection self = (IsoClientConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoClientConnection)); - if (self) { + if (self) + { self->parameters = parameters; self->callback = callback; self->callbackParameter = callbackParameter; @@ -196,7 +197,8 @@ sendConnectionRequestMessage(IsoClientConnection self) int socketExtensionBufferSize = CONFIG_MMS_MAXIMUM_PDU_SIZE + 1000; uint8_t* socketExtensionBuffer = NULL; - if (self->cotpConnection) { + if (self->cotpConnection) + { /* Destroy existing handle set when connection is reused */ if (self->cotpConnection->handleSet) Handleset_destroy(self->cotpConnection->handleSet); @@ -205,19 +207,20 @@ sendConnectionRequestMessage(IsoClientConnection self) socketExtensionBuffer = self->cotpConnection->socketExtensionBuffer; } - if (socketExtensionBuffer == NULL) { + if (socketExtensionBuffer == NULL) + { socketExtensionBuffer = (uint8_t*)GLOBAL_MALLOC(socketExtensionBufferSize); } - if (socketExtensionBuffer) { - + if (socketExtensionBuffer) + { /* COTP (ISO transport) handshake */ CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer, socketExtensionBuffer, socketExtensionBufferSize); #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self->parameters->tlsConfiguration) { - + if (self->parameters->tlsConfiguration) + { TLSConfiguration_setClientMode(self->parameters->tlsConfiguration); /* create TLSSocket and start TLS authentication */ @@ -225,8 +228,8 @@ sendConnectionRequestMessage(IsoClientConnection self) if (tlsSocket) self->cotpConnection->tlsSocket = tlsSocket; - else { - + else + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: TLS handshake failed!\n"); @@ -244,7 +247,8 @@ sendConnectionRequestMessage(IsoClientConnection self) else return true; } - else { + else + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Failed to allocate socket extension buffer\n"); @@ -298,14 +302,14 @@ sendAcseInitiateRequest(IsoClientConnection self) Semaphore_post(self->transmitBufferMutex); } - static void releaseSocket(IsoClientConnection self) { - if (self->socket) { - + if (self->socket) + { #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self->cotpConnection->tlsSocket) { + if (self->cotpConnection->tlsSocket) + { TLSSocket_close(self->cotpConnection->tlsSocket); self->cotpConnection->tlsSocket = NULL; } @@ -345,29 +349,34 @@ IsoClientConnection_handleConnection(IsoClientConnection self) { SocketState socketState = Socket_checkAsyncConnectState(self->socket); - if (socketState == SOCKET_STATE_CONNECTED) { - if (sendConnectionRequestMessage(self)) { + if (socketState == SOCKET_STATE_CONNECTED) + { + if (sendConnectionRequestMessage(self)) + { self->nextReadTimeout = Hal_getTimeInMs() + self->readTimeoutInMs; nextState = INT_STATE_WAIT_FOR_COTP_CONNECT_RESP; } - else { + else + { IsoClientConnection_releaseTransmitBuffer(self); self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); nextState = INT_STATE_CLOSE_ON_ERROR; } } - else if (socketState == SOCKET_STATE_FAILED) { + else if (socketState == SOCKET_STATE_FAILED) + { IsoClientConnection_releaseTransmitBuffer(self); self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { /* check connect timeout */ uint64_t currentTime = Hal_getTimeInMs(); - if (currentTime > self->nextReadTimeout) { + if (currentTime > self->nextReadTimeout) + { IsoClientConnection_releaseTransmitBuffer(self); self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); nextState = INT_STATE_CLOSE_ON_ERROR; @@ -385,8 +394,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) { uint64_t currentTime = Hal_getTimeInMs(); - if (currentTime > self->nextReadTimeout) { - + if (currentTime > self->nextReadTimeout) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Timeout waiting for COTP CR\n"); @@ -396,15 +405,16 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); - if (packetState == TPKT_PACKET_COMPLETE) { - + if (packetState == TPKT_PACKET_COMPLETE) + { CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - if (cotpIndication != COTP_CONNECT_INDICATION) { + if (cotpIndication != COTP_CONNECT_INDICATION) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Unexpected COTP state (%i)\n", cotpIndication); @@ -414,7 +424,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { + else + { sendAcseInitiateRequest(self); self->nextReadTimeout = Hal_getTimeInMs() + self->readTimeoutInMs; @@ -422,7 +433,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_WAIT_FOR_ACSE_RESP; } } - else if (packetState == TPKT_ERROR) { + else if (packetState == TPKT_ERROR) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Error receiving COTP message\n"); @@ -435,7 +447,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self) else { waits = true; } - } } break; @@ -444,8 +455,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) { uint64_t currentTime = Hal_getTimeInMs(); - if (currentTime > self->nextReadTimeout) { - + if (currentTime > self->nextReadTimeout) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Timeout waiting for ACSE initiate response\n"); @@ -453,15 +464,16 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); - if (packetState == TPKT_PACKET_COMPLETE) { - + if (packetState == TPKT_PACKET_COMPLETE) + { CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - if (cotpIndication != COTP_DATA_INDICATION) { + if (cotpIndication != COTP_DATA_INDICATION) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Unexpected COTP state (%i)\n", cotpIndication); @@ -469,67 +481,71 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { /* parse ACSE response */ - IsoSessionIndication sessionIndication; - - sessionIndication = - IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); + IsoSessionIndication sessionIndication; - if (sessionIndication != SESSION_CONNECT) { - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_associate: no session connect indication\n"); + sessionIndication = + IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - - nextState = INT_STATE_CLOSE_ON_ERROR; - } - else { - - if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false) { - - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_associate: no presentation ok indication\n"); - - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - - nextState = INT_STATE_CLOSE_ON_ERROR; - } - else { - - AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); - - if (acseIndication != ACSE_ASSOCIATE) { - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n"); - - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - - nextState = INT_STATE_CLOSE_ON_ERROR; - } - else { - - ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer, - self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize); + if (sessionIndication != SESSION_CONNECT) + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: IsoClientConnection_associate: no session connect indication\n"); - setState(self, STATE_CONNECTED); - nextState = INT_STATE_WAIT_FOR_DATA_MSG; + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - if (self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer) == false) { - nextState = INT_STATE_CLOSE_ON_ERROR; - } + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else + { + if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false) + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: no presentation accept indication\n"); - } + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - } + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else + { + AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); + + if (acseIndication != ACSE_ASSOCIATE) + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: no ACSE_ASSOCIATE indication\n"); + + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); + + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: ACSE AARE - association accepted\n"); + + ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer, + self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize); + + setState(self, STATE_CONNECTED); + nextState = INT_STATE_WAIT_FOR_DATA_MSG; + + if (self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer) == false) { + nextState = INT_STATE_CLOSE_ON_ERROR; + } + } + } - CotpConnection_resetPayload(self->cotpConnection); - } + CotpConnection_resetPayload(self->cotpConnection); + } } } - else if (packetState == TPKT_ERROR) { + else if (packetState == TPKT_ERROR) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Error receiving COTP message\n"); @@ -540,7 +556,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self) else { waits = true; } - } } break; @@ -552,8 +567,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) if (packetState == TPKT_ERROR) { nextState = INT_STATE_CLOSE_ON_ERROR; } - else if (packetState == TPKT_PACKET_COMPLETE) { - + else if (packetState == TPKT_PACKET_COMPLETE) + { CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); switch (cotpIndication) { @@ -576,23 +591,24 @@ IsoClientConnection_handleConnection(IsoClientConnection self) IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); - if (sessionIndication != SESSION_DATA) { + if (sessionIndication != SESSION_DATA) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT_CONNECTION: Invalid session message\n"); nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - - if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) { - + else + { + if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n"); nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { self->callback(ISO_IND_DATA, self->callbackParameter, &(self->presentation->nextPayload)); @@ -659,7 +675,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self) return waits; } - bool IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTimeoutInMs, uint32_t readTimeoutInMs) { @@ -669,7 +684,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim self->socket = TcpSocket_create(); - if (self->socket == NULL) { + if (self->socket == NULL) + { Semaphore_post(self->tickMutex); return false; } @@ -697,8 +713,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort); } - if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) { - + if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) + { Socket_destroy(self->socket); self->socket = NULL; @@ -718,7 +734,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim void IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuffer) { - if (getState(self) == STATE_CONNECTED) { + if (getState(self) == STATE_CONNECTED) + { struct sBufferChain payloadBCMemory; BufferChain payload = &payloadBCMemory; @@ -743,7 +760,8 @@ IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuf if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n"); } - else { + else + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Not connected --> cannot send message\n"); } @@ -762,7 +780,8 @@ IsoClientConnection_close(IsoClientConnection self) eIsoClientInternalState intState = getIntState(self); - if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR)) { + if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR)) + { setIntState(self, INT_STATE_CLOSING_CONNECTION); Semaphore_post(self->tickMutex); @@ -783,8 +802,8 @@ IsoClientConnection_destroy(IsoClientConnection self) int state = getState(self); - if (state == STATE_CONNECTED) { - + if (state == STATE_CONNECTED) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: call IsoClientConnection_close\n"); @@ -798,7 +817,8 @@ IsoClientConnection_destroy(IsoClientConnection self) if (self->receiveBuffer != NULL) GLOBAL_FREEMEM(self->receiveBuffer); - if (self->cotpConnection != NULL) { + if (self->cotpConnection != NULL) + { if (self->cotpConnection->handleSet != NULL) Handleset_destroy(self->cotpConnection->handleSet); @@ -910,7 +930,6 @@ IsoClientConnection_release(IsoClientConnection self) Semaphore_post(self->transmitBufferMutex); } - ByteBuffer* IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self) { diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index a9763c08..e92f8046 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -1,7 +1,7 @@ /* * mms_client_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -37,7 +37,6 @@ #define CONFIG_MMS_CONNECTION_DEFAULT_TIMEOUT 5000 #define CONFIG_MMS_CONNECTION_DEFAULT_CONNECT_TIMEOUT 10000 -#define OUTSTANDING_CALLS 10 static void setConnectionState(MmsConnection self, MmsConnectionState newState) @@ -65,7 +64,8 @@ getConnectionState(MmsConnection self) static void handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) { - if (self->reportHandler != NULL) { + if (self->reportHandler) + { MmsPdu_t* mmsPdu = NULL; /* allow asn1c to allocate structure */ if (DEBUG_MMS_CLIENT) @@ -74,12 +74,13 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); - if (rval.code == RC_OK) { + if (rval.code == RC_OK) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received report (size:%i)\n", (int) rval.consumed); - if (mmsPdu->present == MmsPdu_PR_unconfirmedPDU) { - + if (mmsPdu->present == MmsPdu_PR_unconfirmedPDU) + { if (mmsPdu->choice.unconfirmedPDU.unconfirmedService.present == UnconfirmedService_PR_informationReport) { @@ -121,7 +122,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) int listSize = report->listOfAccessResult.list.count; int variableSpecSize = report->variableAccessSpecification.choice.listOfVariable.list.count; - if (listSize != variableSpecSize) { + if (listSize != variableSpecSize) + { if (DEBUG_MMS_CLIENT) printf("report contains wrong number of access results\n"); return; @@ -131,7 +133,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) report->listOfAccessResult.list.array, listSize, false); int i; - for (i = 0; i < variableSpecSize; i++) { + for (i = 0; i < variableSpecSize; i++) + { if (report->variableAccessSpecification.choice.listOfVariable.list.array[i]->variableSpecification.present == VariableSpecification_PR_name) { @@ -146,7 +149,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.vmdspecific.buf; - if (nameSize < 129) { + if (nameSize < 129) + { char variableListName[129]; memcpy(variableListName, buffer, nameSize); variableListName[nameSize] = 0; @@ -167,8 +171,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) } } else if (report->variableAccessSpecification.choice.listOfVariable.list.array[i] - ->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) { - + ->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) + { int domainNameSize = report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.domainspecific.domainId.size; @@ -177,7 +181,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.domainspecific.itemId.size; - if ((domainNameSize < 65) && (itemNameSize < 65)) { + if ((domainNameSize < 65) && (itemNameSize < 65)) + { char domainNameStr[65]; char itemNameStr[65]; @@ -216,7 +221,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) if (values != NULL) MmsValue_delete(values); } - else { + else + { /* Ignore */ if (DEBUG_MMS_CLIENT) printf("unrecognized information report\n"); @@ -226,7 +232,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("handleUnconfirmedMmsPdu: error parsing PDU at %u\n", (uint32_t) rval.consumed); } @@ -242,6 +249,10 @@ getNextInvokeId(MmsConnection self) Semaphore_wait(self->nextInvokeIdLock); self->nextInvokeId++; + + if (self->nextInvokeId == 0) + self->nextInvokeId = 1; + nextInvokeId = self->nextInvokeId; Semaphore_post(self->nextInvokeIdLock); @@ -255,9 +266,12 @@ checkForOutstandingCall(MmsConnection self, uint32_t invokeId) Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed) { - if (self->outstandingCalls[i].invokeId == invokeId) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed) + { + if (self->outstandingCalls[i].invokeId == invokeId) + { Semaphore_post(self->outstandingCallsLock); return &(self->outstandingCalls[i]); } @@ -276,8 +290,10 @@ addToOutstandingCalls(MmsConnection self, uint32_t invokeId, eMmsOutstandingCall Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed == false) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed == false) + { self->outstandingCalls[i].isUsed = true; self->outstandingCalls[i].invokeId = invokeId; self->outstandingCalls[i].timeout = Hal_getTimeInMs() + self->requestTimeout; @@ -302,9 +318,12 @@ removeFromOutstandingCalls(MmsConnection self, uint32_t invokeId) Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed) { - if (self->outstandingCalls[i].invokeId == invokeId) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed) + { + if (self->outstandingCalls[i].invokeId == invokeId) + { self->outstandingCalls[i].isUsed = false; break; } @@ -321,16 +340,18 @@ mmsClient_getMatchingObtainFileRequest(MmsConnection self, const char* filename) Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed) { - - if (self->outstandingCalls[i].type == MMS_CALL_TYPE_OBTAIN_FILE) { - + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed) + { + if (self->outstandingCalls[i].type == MMS_CALL_TYPE_OBTAIN_FILE) + { char* storedFilename = (char*) self->outstandingCalls[i].internalParameter.ptr; - if (storedFilename) { - - if (!strcmp(filename, storedFilename)) { + if (storedFilename) + { + if (!strcmp(filename, storedFilename)) + { Semaphore_post(self->outstandingCallsLock); return &(self->outstandingCalls[i]); } @@ -348,7 +369,8 @@ static void sendMessage(MmsConnection self, ByteBuffer* message) { #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { + if (self->rawMmsMessageHandler != NULL) + { MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; handler(self->rawMmsMessageHandlerParameter, message->buffer, message->size, false); } @@ -361,8 +383,8 @@ static MmsError sendAsyncRequest(MmsConnection self, uint32_t invokeId, ByteBuffer* message, eMmsOutstandingCallType type, void* userCallback, void* userParameter, MmsClientInternalParameter internalParameter) { - if (addToOutstandingCalls(self, invokeId, type, userCallback, userParameter, internalParameter) == false) { - + if (addToOutstandingCalls(self, invokeId, type, userCallback, userParameter, internalParameter) == false) + { /* message cannot be sent - release resources */ IsoClientConnection_releaseTransmitBuffer(self->isoClient); @@ -436,7 +458,17 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) break; case 4: /* class: service */ - mmsError = MMS_ERROR_SERVICE_OTHER; + + switch (serviceError.errorCode) + { + case 5: + mmsError = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + + default: + mmsError = MMS_ERROR_SERVICE_OTHER; + break; + } break; case 5: /* class: service-preempt */ @@ -517,7 +549,8 @@ parseServiceError(uint8_t* buffer, int bufPos, int maxLength, MmsServiceError* e int endPos = bufPos + maxLength; int length; - while (bufPos < endPos) { + while (bufPos < endPos) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, endPos); @@ -578,7 +611,8 @@ mmsMsg_parseConfirmedErrorPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32 int endPos = bufPos + length; - while (bufPos < endPos) { + while (bufPos < endPos) + { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); @@ -639,7 +673,8 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo int endPos = bufPos + length; - while (bufPos < endPos) { + while (bufPos < endPos) + { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); @@ -647,13 +682,16 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo if (bufPos < 0) goto exit_error; - if (tag == 0x80) { /* invoke id */ + if (tag == 0x80) + { + /* invoke id */ if (hasInvokeId) *hasInvokeId = true; if (invokeId != NULL) *invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); } - else if (tag > 0x80 && tag < 0x8c) { + else if (tag > 0x80 && tag < 0x8c) + { *rejectType = tag - 0x80; *rejectReason = BerDecoder_decodeInt32(buffer, length, bufPos); } @@ -676,15 +714,19 @@ exit_error: static void handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, MmsOutstandingCall outstandingCall, MmsError err) { - if (outstandingCall->type == MMS_CALL_TYPE_READ_VARIABLE) { - + if (outstandingCall->type == MMS_CALL_TYPE_READ_VARIABLE) + { MmsConnection_ReadVariableHandler handler = (MmsConnection_ReadVariableHandler) outstandingCall->userCallback; if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); - else { - if (response) { + } + else + { + if (response) + { MmsValue* value = mmsClient_parseReadResponse(response, NULL, false); if (value == NULL) @@ -695,15 +737,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } - else if (outstandingCall->type == MMS_CALL_TYPE_READ_MULTIPLE_VARIABLES) { - + else if (outstandingCall->type == MMS_CALL_TYPE_READ_MULTIPLE_VARIABLES) + { MmsConnection_ReadVariableHandler handler = (MmsConnection_ReadVariableHandler) outstandingCall->userCallback; if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); - else { - if (response) { + } + else + { + if (response) + { MmsValue* value = mmsClient_parseReadResponse(response, NULL, true); if (value == NULL) @@ -711,19 +757,21 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, value); } - } } - else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_VARIABLE) { - + else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_VARIABLE) + { MmsConnection_WriteVariableHandler handler = (MmsConnection_WriteVariableHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, DATA_ACCESS_ERROR_NO_RESPONSE); } - else { - if (response) { + else + { + if (response) + { MmsDataAccessError daError = mmsClient_parseWriteResponse(response, bufPos, &err); handler(outstandingCall->invokeId, outstandingCall->userParameter, err, daError); @@ -731,16 +779,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } - else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES) { - + else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES) + { MmsConnection_WriteMultipleVariablesHandler handler = (MmsConnection_WriteMultipleVariablesHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); } - else { - if (response) { + else + { + if (response) + { LinkedList accessResults = NULL; mmsClient_parseWriteMultipleItemsResponse(response, bufPos, &err, -1, &accessResults); @@ -749,15 +800,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_READ_NVL_DIRECTORY) { + else if (outstandingCall->type == MMS_CALL_TYPE_READ_NVL_DIRECTORY) + { MmsConnection_ReadNVLDirectoryHandler handler = (MmsConnection_ReadNVLDirectoryHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false); } - else { - if (response) { + else + { + if (response) + { bool deletable = false; LinkedList accessSpec = mmsClient_parseGetNamedVariableListAttributesResponse(response, &deletable); @@ -769,15 +824,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_DEFINE_NVL) { - + else if (outstandingCall->type == MMS_CALL_TYPE_DEFINE_NVL) + { MmsConnection_GenericServiceHandler handler = (MmsConnection_GenericServiceHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, false); } - else { + else + { bool success = false; if (!mmsClient_parseDefineNamedVariableResponse(response, NULL)) @@ -788,15 +845,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, success); } } - else if (outstandingCall->type == MMS_CALL_TYPE_DELETE_NVL) { - + else if (outstandingCall->type == MMS_CALL_TYPE_DELETE_NVL) + { MmsConnection_GenericServiceHandler handler = (MmsConnection_GenericServiceHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, false); } - else { + else + { bool success = false; long numberMatched = 0; @@ -806,8 +865,11 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M success = true; if (numberMatched == 0) + { err = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; - else { + } + else + { if (numberDeleted == 0) err = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; } @@ -815,14 +877,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, success); } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_VAR_ACCESS_ATTR) { + else if (outstandingCall->type == MMS_CALL_TYPE_GET_VAR_ACCESS_ATTR) + { MmsConnection_GetVariableAccessAttributesHandler handler = (MmsConnection_GetVariableAccessAttributesHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); } - else { + else + { MmsVariableSpecification* typeSpec = mmsClient_parseGetVariableAccessAttributesResponse(response, NULL); if (typeSpec == NULL) @@ -831,14 +896,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, typeSpec); } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_SERVER_STATUS) { + else if (outstandingCall->type == MMS_CALL_TYPE_GET_SERVER_STATUS) + { MmsConnection_GetServerStatusHandler handler = (MmsConnection_GetServerStatusHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, 0, 0); } - else { + else + { int vmdLogicalStatus; int vmdPhysicalStatus; @@ -848,15 +916,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, vmdLogicalStatus, vmdPhysicalStatus); } } - else if (outstandingCall->type == MMS_CALL_TYPE_IDENTIFY) { + else if (outstandingCall->type == MMS_CALL_TYPE_IDENTIFY) + { MmsConnection_IdentifyHandler handler = (MmsConnection_IdentifyHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, NULL, NULL); } - else { - + else + { if (mmsClient_parseIdentifyResponse(self, response, bufPos, outstandingCall->invokeId, handler, outstandingCall->userParameter) == false) { @@ -866,15 +936,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } - else if (outstandingCall->type == MMS_CALL_TYPE_READ_JOURNAL) { - + else if (outstandingCall->type == MMS_CALL_TYPE_READ_JOURNAL) + { MmsConnection_ReadJournalHandler handler = (MmsConnection_ReadJournalHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false); } - else { + else + { bool moreFollows = false; LinkedList entries = NULL; @@ -886,15 +958,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_NAME_LIST) { - + else if (outstandingCall->type == MMS_CALL_TYPE_GET_NAME_LIST) + { MmsConnection_GetNameListHandler handler = (MmsConnection_GetNameListHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false); } - else { + else + { LinkedList nameList = (LinkedList) outstandingCall->internalParameter.ptr; bool moreFollows = mmsClient_parseGetNameListResponse(&nameList, response); @@ -907,15 +981,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_FILE_OPEN) { - + else if (outstandingCall->type == MMS_CALL_TYPE_FILE_OPEN) + { MmsConnection_FileOpenHandler handler = (MmsConnection_FileOpenHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, 0, 0, 0); } - else { + else + { int32_t frsmId; uint32_t fileSize; uint64_t lastModified; @@ -925,21 +1001,25 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M { handler(outstandingCall->invokeId, outstandingCall->userParameter, MMS_ERROR_PARSING_RESPONSE, 0, 0, 0); } - else { + else + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, frsmId, fileSize, lastModified); } } } - else if (outstandingCall->type == MMS_CALL_TYPE_FILE_READ) { + else if (outstandingCall->type == MMS_CALL_TYPE_FILE_READ) + { MmsConnection_FileReadHandler handler = (MmsConnection_FileReadHandler) outstandingCall->userCallback; int32_t frsmId = outstandingCall->internalParameter.i32; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, frsmId, NULL, 0, false); } - else { + else + { bool moreFollows; if (mmsMsg_parseFileReadResponse(ByteBuffer_getBuffer(response), bufPos, ByteBuffer_getSize(response), outstandingCall->invokeId, frsmId, &moreFollows, @@ -957,7 +1037,8 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M MmsConnection_GenericServiceHandler handler = (MmsConnection_GenericServiceHandler) outstandingCall->userCallback; - if (outstandingCall->type == MMS_CALL_TYPE_OBTAIN_FILE) { + if (outstandingCall->type == MMS_CALL_TYPE_OBTAIN_FILE) + { if (outstandingCall->internalParameter.ptr) GLOBAL_FREEMEM(outstandingCall->internalParameter.ptr); } @@ -969,14 +1050,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, true); } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_FILE_DIR) { + else if (outstandingCall->type == MMS_CALL_TYPE_GET_FILE_DIR) + { MmsConnection_FileDirectoryHandler handler = (MmsConnection_FileDirectoryHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, 0, 0, false); } - else { + else + { if (mmsClient_parseFileDirectoryResponse(response, bufPos, outstandingCall->invokeId, handler, outstandingCall->userParameter) == false) handler(outstandingCall->invokeId, outstandingCall->userParameter, MMS_ERROR_PARSING_RESPONSE, NULL, 0, 0, false); } @@ -995,24 +1079,24 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (indication != ISO_IND_TICK) printf("MMS_CLIENT: mmsIsoCallback called with indication %i\n", indication); - if (indication == ISO_IND_TICK) { - + if (indication == ISO_IND_TICK) + { /* check timeouts */ uint64_t currentTime = Hal_getTimeInMs(); int i = 0; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - + for (i = 0; i < self->maxOutstandingCalled; i++) + { Semaphore_wait(self->outstandingCallsLock); - if (self->outstandingCalls[i].isUsed) { - + if (self->outstandingCalls[i].isUsed) + { Semaphore_post(self->outstandingCallsLock); - if (currentTime > self->outstandingCalls[i].timeout) { - + if (currentTime > self->outstandingCalls[i].timeout) + { if (self->outstandingCalls[i].type != MMS_CALL_TYPE_NONE) handleAsyncResponse(self, NULL, 0, &(self->outstandingCalls[i]), MMS_ERROR_SERVICE_TIMEOUT); @@ -1028,8 +1112,10 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } } - if (self->concludeHandler) { - if (currentTime > self->concludeTimeout) { + if (self->concludeHandler) + { + if (currentTime > self->concludeTimeout) + { self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_SERVICE_TIMEOUT, false); self->concludeHandler = NULL; } @@ -1038,7 +1124,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return true; } - if (indication == ISO_IND_CLOSED) { + if (indication == ISO_IND_CLOSED) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: Connection lost or closed by client!\n"); @@ -1052,12 +1139,12 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) { int i; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - + for (i = 0; i < self->maxOutstandingCalled; i++) + { Semaphore_wait(self->outstandingCallsLock); - if (self->outstandingCalls[i].isUsed) { - + if (self->outstandingCalls[i].isUsed) + { Semaphore_post(self->outstandingCallsLock); if (self->outstandingCalls[i].type != MMS_CALL_TYPE_NONE) @@ -1075,7 +1162,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return true; } - if (indication == ISO_IND_ASSOCIATION_FAILED) { + if (indication == ISO_IND_ASSOCIATION_FAILED) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: association failed!\n"); @@ -1083,7 +1171,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return false; } - if (payload != NULL) { + if (payload != NULL) + { if (ByteBuffer_getSize(payload) < 1) { return false; } @@ -1092,7 +1181,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) uint8_t* buf = ByteBuffer_getBuffer(payload); #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { + if (self->rawMmsMessageHandler != NULL) + { MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; handler(self->rawMmsMessageHandlerParameter, buf, payload->size, true); } @@ -1103,20 +1193,25 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: MMS-PDU: %02x\n", tag); - if (tag == 0xa9) { /* initiate response PDU */ - - if (indication == ISO_IND_ASSOCIATION_SUCCESS) { + if (tag == 0xa9) + { + /* initiate response PDU */ - if (mmsClient_parseInitiateResponse(self, payload)) { + if (indication == ISO_IND_ASSOCIATION_SUCCESS) + { + if (mmsClient_parseInitiateResponse(self, payload)) + { setConnectionState(self, MMS_CONNECTION_STATE_CONNECTED); } - else { + else + { setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); goto exit_with_error; } } - else { + else + { setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); if (DEBUG_MMS_CLIENT) @@ -1125,7 +1220,9 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return false; } } - else if (tag == 0xaa) { /* initiate error PDU */ + else if (tag == 0xaa) + { + /* initiate error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received initiate error PDU\n"); @@ -1134,36 +1231,48 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return false; } - else if (tag == 0xa3) { /* unconfirmed PDU */ + else if (tag == 0xa3) + { + /* unconfirmed PDU */ handleUnconfirmedMmsPdu(self, payload); } - else if (tag == 0x8b) { /* conclude request PDU */ + else if (tag == 0x8b) + { + /* conclude request PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.request\n"); /* TODO block all new user requests? */ } - else if (tag == 0x8c) { /* conclude response PDU */ + else if (tag == 0x8c) + { + /* conclude response PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.response+\n"); - if (self->concludeHandler) { + if (self->concludeHandler) + { self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_NONE, true); self->concludeHandler = NULL; } IsoClientConnection_release(self->isoClient); } - else if (tag == 0x8d) { /* conclude error PDU */ + else if (tag == 0x8d) + { + /* conclude error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.reponse-\n"); - if (self->concludeHandler) { + if (self->concludeHandler) + { self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_CONCLUDE_REJECTED, false); self->concludeHandler = NULL; } } - else if (tag == 0xa2) { /* confirmed error PDU */ + else if (tag == 0xa2) + { + /* confirmed error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Confirmed error PDU!\n"); @@ -1173,37 +1282,43 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) MmsServiceError serviceError = { 0, 0 }; - if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &serviceError) < 0) { + if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &serviceError) < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Error parsing confirmedErrorPDU!\n"); goto exit_with_error; } - else { - - if (hasInvokeId) { + else + { + if (hasInvokeId) + { MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - if (call) { - + if (call) + { MmsError err = convertServiceErrorToMmsError(serviceError); - if (call->type != MMS_CALL_TYPE_NONE) { + if (call->type != MMS_CALL_TYPE_NONE) + { handleAsyncResponse(self, NULL, 0, call, err); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: internal problem (unexpected call type - error PDU)\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: server sent unexpected confirmed error PDU!\n"); return false; } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: server sent confirmed error PDU without invoke ID!\n"); @@ -1212,7 +1327,9 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } } - else if (tag == 0xa4) { /* reject PDU */ + else if (tag == 0xa4) + { + /* reject PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: reject PDU!\n"); @@ -1222,23 +1339,25 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) int rejectType; int rejectReason; - if (mmsMsg_parseRejectPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &rejectType, &rejectReason) >= 0) { - + if (mmsMsg_parseRejectPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &rejectType, &rejectReason) >= 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: reject PDU invokeID: %u type: %i reason: %i\n", invokeId, rejectType, rejectReason); - if (hasInvokeId) { + if (hasInvokeId) + { MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - if (call) { - + if (call) + { MmsError err = convertRejectCodesToMmsError(rejectType, rejectReason); - if (call->type != MMS_CALL_TYPE_NONE) { + if (call->type != MMS_CALL_TYPE_NONE) + { handleAsyncResponse(self, NULL, 0, call, err); } - else { - + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: internal problem (unexpected call type - reject PDU)\n"); } @@ -1250,12 +1369,13 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) else { return false; } - } else goto exit_with_error; } - else if (tag == 0xa1) { /* confirmed response PDU */ + else if (tag == 0xa1) + { + /* confirmed response PDU */ int length; int bufPos = 1; @@ -1264,7 +1384,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (bufPos < 0) goto exit_with_error; - if (buf[bufPos++] == 0x02) { + if (buf[bufPos++] == 0x02) + { int invokeIdLength; bufPos = BerDecoder_decodeLength(buf, &invokeIdLength, bufPos, payload->size); @@ -1282,17 +1403,20 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - if (call) { - - if (call->type != MMS_CALL_TYPE_NONE) { + if (call) + { + if (call->type != MMS_CALL_TYPE_NONE) + { handleAsyncResponse(self, payload, bufPos, call, MMS_ERROR_NONE); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: internal problem (unexpected call type - confirmed response PDU)\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); @@ -1303,8 +1427,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) goto exit_with_error; } #if (MMS_OBTAIN_FILE_SERVICE == 1) - else if (tag == 0xa0) { - + else if (tag == 0xa0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received confirmed request PDU (size=%i)\n", payload->size); @@ -1320,13 +1444,14 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) bool hasInvokeId = false; uint32_t invokeId = 0; - while (bufPos < payload->size) { - + while (bufPos < payload->size) + { uint8_t nestedTag = buf[bufPos++]; bool extendedTag = false; - if ((nestedTag & 0x1f) == 0x1f) { + if ((nestedTag & 0x1f) == 0x1f) + { extendedTag = true; nestedTag = buf[bufPos++]; } @@ -1335,9 +1460,10 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (bufPos < 0) goto exit_with_error; - if (extendedTag) { - - if (hasInvokeId == false) { + if (extendedTag) + { + if (hasInvokeId == false) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: invalid message received - missing invoke ID!\n"); @@ -1424,7 +1550,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unknown message type\n"); @@ -1450,7 +1577,8 @@ connectionHandlingThread(void* parameter) { MmsConnection self = (MmsConnection) parameter; - while (self->connectionThreadRunning) { + while (self->connectionThreadRunning) + { if (MmsConnection_tick(self)) Thread_sleep(10); } @@ -1469,8 +1597,8 @@ MmsConnection_createInternal(TLSConfiguration tlsConfig, bool createThread) MmsConnection self = (MmsConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsConnection)); - if (self) { - + if (self) + { self->parameters.dataStructureNestingLevel = -1; self->parameters.maxServOutstandingCalled = -1; self->parameters.maxServOutstandingCalling = -1; @@ -1488,7 +1616,9 @@ MmsConnection_createInternal(TLSConfiguration tlsConfig, bool createThread) self->concludeHandlerParameter = NULL; self->concludeTimeout = 0; - self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sMmsOutstandingCall)); + self->maxOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + self->maxOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED, sizeof(struct sMmsOutstandingCall)); self->isoParameters = IsoConnectionParameters_create(); @@ -1545,43 +1675,49 @@ MmsConnection_createNonThreaded(TLSConfiguration tlsConfig) void MmsConnection_destroy(MmsConnection self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK == 0) - if (self->createThread) { - if (self->connectionHandlingThread) { - if (self->connectionThreadRunning) { - self->connectionThreadRunning = false; - Thread_destroy(self->connectionHandlingThread); - self->connectionHandlingThread = NULL; + if (self->createThread) + { + if (self->connectionHandlingThread) + { + if (self->connectionThreadRunning) + { + self->connectionThreadRunning = false; + Thread_destroy(self->connectionHandlingThread); + self->connectionHandlingThread = NULL; + } } } - } #endif - if (self->isoClient != NULL) - IsoClientConnection_destroy(self->isoClient); + if (self->isoClient != NULL) + IsoClientConnection_destroy(self->isoClient); - if (self->isoParameters != NULL) - IsoConnectionParameters_destroy(self->isoParameters); + if (self->isoParameters != NULL) + IsoConnectionParameters_destroy(self->isoParameters); - Semaphore_destroy(self->nextInvokeIdLock); + Semaphore_destroy(self->nextInvokeIdLock); - Semaphore_destroy(self->outstandingCallsLock); + Semaphore_destroy(self->outstandingCallsLock); - Semaphore_destroy(self->associationStateLock); + Semaphore_destroy(self->associationStateLock); - GLOBAL_FREEMEM(self->outstandingCalls); + GLOBAL_FREEMEM(self->outstandingCalls); #if (MMS_OBTAIN_FILE_SERVICE == 1) #if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) - if (self->filestoreBasepath != NULL) - GLOBAL_FREEMEM(self->filestoreBasepath); + if (self->filestoreBasepath != NULL) + GLOBAL_FREEMEM(self->filestoreBasepath); #endif - /* Close outstanding open files */ - mmsClient_closeOutstandingOpenFiles(self); + /* Close outstanding open files */ + mmsClient_closeOutstandingOpenFiles(self); #endif - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } void @@ -1589,7 +1725,8 @@ MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath) { #if (MMS_OBTAIN_FILE_SERVICE == 1) #if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) - if (self->filestoreBasepath != NULL) { + if (self->filestoreBasepath != NULL) + { GLOBAL_FREEMEM(self->filestoreBasepath); self->filestoreBasepath = NULL; } @@ -1647,6 +1784,25 @@ MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs) self->requestTimeout = timeoutInMs; } +void +MmsConnnection_setMaxOutstandingCalls(MmsConnection self, int calling, int called) +{ + if (calling < 1) + calling = 1; + + if (called < 1) + called = 1; + + if (self->outstandingCalls) + { + GLOBAL_FREEMEM(self->outstandingCalls); + } + + self->maxOutstandingCalling = calling; + self->maxOutstandingCalled = called; + self->outstandingCalls = (MmsOutstandingCall)GLOBAL_CALLOC(called, sizeof(struct sMmsOutstandingCall)); +} + uint32_t MmsConnection_getRequestTimeout(MmsConnection self) { @@ -1730,22 +1886,25 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server MmsConnection_connectAsync(self, &err, serverName, serverPort); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(conParams.sem); - if (conParams.state == MMS_CONNECTION_STATE_CONNECTED) { + if (conParams.state == MMS_CONNECTION_STATE_CONNECTED) + { *mmsError = MMS_ERROR_NONE; success = true; } - else { + else + { *mmsError = MMS_ERROR_CONNECTION_REJECTED; } if (conParams.originalHandler) conParams.originalHandler(self, conParams.originalParameter, conParams.state); - } - else { + else + { *mmsError = err; } @@ -1760,7 +1919,8 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server void MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort) { - if (serverPort == -1) { + if (serverPort == -1) + { #if (CONFIG_MMS_SUPPORT_TLS == 1) if (self->isoParameters->tlsConfiguration) serverPort = 3782; @@ -1772,9 +1932,10 @@ MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* s } #if (CONFIG_MMS_THREADLESS_STACK == 0) - if (self->createThread) { - if (self->connectionHandlingThread == NULL) { - + if (self->createThread) + { + if (self->connectionHandlingThread == NULL) + { self->connectionHandlingThread = Thread_create(connectionHandlingThread, self, false); self->connectionThreadRunning = true; Thread_start(self->connectionHandlingThread); @@ -1792,13 +1953,15 @@ MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* s mmsClient_createInitiateRequest(self, payload); #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { + if (self->rawMmsMessageHandler != NULL) + { MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; handler(self->rawMmsMessageHandlerParameter, payload->buffer, payload->size, false); } #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ - if (IsoClientConnection_associateAsync(self->isoClient, self->connectTimeout, self->requestTimeout)) { + if (IsoClientConnection_associateAsync(self->isoClient, self->connectTimeout, self->requestTimeout)) + { setConnectionState(self, MMS_CONNECTION_STATE_CONNECTING); *mmsError = MMS_ERROR_NONE; } @@ -1827,7 +1990,8 @@ MmsConnection_abortAsync(MmsConnection self, MmsError* mmsError) { self->connectionLostHandler = NULL; - if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) + { IsoClientConnection_abortAsync(self->isoClient); *mmsError = MMS_ERROR_NONE; } @@ -1845,14 +2009,16 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError) bool success = false; - if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) { - + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) + { IsoClientConnection_abortAsync(self->isoClient); uint64_t timeout = Hal_getTimeInMs() + self->requestTimeout; - while (Hal_getTimeInMs() < timeout) { - if (getConnectionState(self) == MMS_CONNECTION_STATE_CLOSED) { + while (Hal_getTimeInMs() < timeout) + { + if (getConnectionState(self) == MMS_CONNECTION_STATE_CLOSED) + { success = true; break; } @@ -1860,10 +2026,10 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError) Thread_sleep(10); } } - } - if (success == false) { + if (success == false) + { IsoClientConnection_close(self->isoClient); *mmsError = MMS_ERROR_SERVICE_TIMEOUT; } @@ -1905,7 +2071,8 @@ MmsConnection_conclude(MmsConnection self, MmsError* mmsError) MmsConnection_concludeAsync(self, &err, concludeHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; } @@ -1919,7 +2086,8 @@ MmsConnection_conclude(MmsConnection self, MmsError* mmsError) void MmsConnection_concludeAsync(MmsConnection self, MmsError* mmsError, MmsConnection_ConcludeAbortHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1961,7 +2129,8 @@ mmsClient_getNameListSingleRequestAsync( void* parameter, LinkedList nameList) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -1977,8 +2146,8 @@ mmsClient_getNameListSingleRequestAsync( if (associationSpecific) mmsClient_createMmsGetNameListRequestAssociationSpecific(invokeId, payload, continueAfter); - else { - + else + { if (objectClass == MMS_OBJECT_CLASS_DOMAIN) mmsClient_createMmsGetNameListRequestVMDspecific(invokeId, payload, continueAfter); @@ -2022,7 +2191,6 @@ getNameListHandler(uint32_t invokeId, void* parameter, MmsError mmsError, Linked Semaphore_post(parameters->sem); } - static LinkedList /* */ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, const char* domainId, @@ -2045,7 +2213,8 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, mmsClient_getNameListSingleRequestAsync(self, NULL, &err, domainId, objectClass, associationSpecific, NULL, getNameListHandler, ¶meter, NULL); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; list = parameter.nameList; @@ -2054,7 +2223,8 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, Semaphore_destroy(parameter.sem); - while (moreFollows) { + while (moreFollows) + { parameter.sem = Semaphore_create(1); char* continueAfter = NULL; @@ -2067,12 +2237,18 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, mmsClient_getNameListSingleRequestAsync(self, NULL, &err, domainId, objectClass, associationSpecific, continueAfter, getNameListHandler, ¶meter, list); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; list = parameter.nameList; moreFollows = parameter.moreFollows; } + else + { + /* exit loop when message cannot be sent */ + moreFollows = false; + } Semaphore_destroy(parameter.sem); } @@ -2080,8 +2256,10 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, if (mmsError) *mmsError = err; - if (err != MMS_ERROR_NONE) { - if (list) { + if (err != MMS_ERROR_NONE) + { + if (list) + { LinkedList_destroy(list); list = NULL; } @@ -2174,7 +2352,6 @@ MmsConnection_getVariableListNamesAssociationSpecificAsync(MmsConnection self, u continueAfter, handler, parameter, NULL); } - struct readNVParameters { Semaphore sem; @@ -2200,7 +2377,8 @@ void MmsConnection_readVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; @@ -2245,7 +2423,8 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, MmsConnection_readVariableAsync(self, NULL, &err, domainId, itemId, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2265,7 +2444,8 @@ MmsConnection_readVariableComponentAsync(MmsConnection self, uint32_t* usedInvok const char* domainId, const char* itemId, const char* componentId, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; @@ -2310,7 +2490,8 @@ MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError, MmsConnection_readVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2344,7 +2525,8 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, MmsConnection_readArrayElementsAsync(self, NULL, &err, domainId, itemId, startIndex, numberOfElements, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2364,7 +2546,8 @@ MmsConnection_readArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId, uint32_t startIndex, uint32_t numberOfElements, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2410,7 +2593,8 @@ MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* MmsConnection_readSingleArrayElementWithComponentAsync(self, NULL, &err, domainId, itemId, index, componentId, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2432,7 +2616,8 @@ MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, uint3 uint32_t index, const char* componentId, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2478,7 +2663,8 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, MmsConnection_readMultipleVariablesAsync(self, NULL, &err, domainId, items, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2498,7 +2684,8 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvok const char* domainId, LinkedList /**/items, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2511,7 +2698,8 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvok if (usedInvokeId) *usedInvokeId = invokeId; - if (mmsClient_createReadRequestMultipleValues(invokeId, domainId, items, payload) > 0) { + if (mmsClient_createReadRequestMultipleValues(invokeId, domainId, items, payload) > 0) + { MmsClientInternalParameter intParam; intParam.ptr = NULL; @@ -2520,7 +2708,8 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvok if (mmsError) *mmsError = err; } - else { + else + { if (mmsError) *mmsError = MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE; } @@ -2548,7 +2737,8 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError MmsConnection_readNamedVariableListValuesAsync(self, NULL, &err, domainId, listName, specWithResult, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2568,7 +2758,8 @@ MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, uint32_t* use const char* domainId, const char* listName, bool specWithResult, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2616,7 +2807,8 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific( MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self, NULL, &err, listName, specWithResult, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2636,7 +2828,8 @@ MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection const char* listName, bool specWithResult, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2706,7 +2899,8 @@ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsEr MmsConnection_readNamedVariableListDirectoryAsync(self, NULL, &err, domainId, listName, readNVLDirectoryHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(waitForResponse); err = parameter.err; specs = parameter.specs; @@ -2728,7 +2922,8 @@ MmsConnection_readNamedVariableListDirectoryAsync(MmsConnection self, uint32_t* const char* domainId, const char* listName, MmsConnection_ReadNVLDirectoryHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2775,7 +2970,8 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection se MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self, NULL, &err, listName, readNVLDirectoryHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(waitForResponse); err = parameter.err; specs = parameter.specs; @@ -2797,7 +2993,8 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(MmsConnecti const char* listName, MmsConnection_ReadNVLDirectoryHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2862,7 +3059,8 @@ MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, MmsConnection_defineNamedVariableListAsync(self, NULL, &err, domainId, listName, variableSpecs, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; } @@ -2878,7 +3076,8 @@ MmsConnection_defineNamedVariableListAsync(MmsConnection self, uint32_t* usedInv const char* listName, LinkedList variableSpecs, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2921,7 +3120,8 @@ MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsConnection_defineNamedVariableListAssociationSpecificAsync(self, NULL, &err, listName, variableSpecs, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; } @@ -2937,7 +3137,8 @@ MmsConnection_defineNamedVariableListAssociationSpecificAsync(MmsConnection self const char* listName, LinkedList variableSpecs, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2981,7 +3182,8 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, MmsConnection_deleteNamedVariableListAsync(self, NULL, &err, domainId, listName, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; isDeleted = parameter.success; @@ -2999,7 +3201,8 @@ void MmsConnection_deleteNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* listName, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3042,7 +3245,8 @@ MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, MmsConnection_deleteAssociationSpecificNamedVariableListAsync(self, NULL, &err, listName, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; isDeleted = parameter.success; @@ -3060,7 +3264,8 @@ void MmsConnection_deleteAssociationSpecificNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* listName, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3125,7 +3330,8 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError MmsConnection_getVariableAccessAttributesAsync(self, NULL, &err, domainId, itemId, getAccessAttrHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; typeSpec = parameter.typeSpec; @@ -3144,7 +3350,8 @@ MmsConnection_getVariableAccessAttributesAsync(MmsConnection self, uint32_t* use const char* domainId, const char* itemId, MmsConnection_GetVariableAccessAttributesHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3214,7 +3421,8 @@ MmsConnection_identify(MmsConnection self, MmsError* mmsError) MmsConnection_identifyAsync(self, NULL, &err, identifyHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; identity = parameter.identify; @@ -3232,7 +3440,8 @@ void MmsConnection_identifyAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, MmsConnection_IdentifyHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3300,7 +3509,8 @@ MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLo MmsConnection_getServerStatusAsync(self, NULL, &err, extendedDerivation, getServerStatusHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3321,7 +3531,8 @@ void MmsConnection_getServerStatusAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, bool extendedDerivation, MmsConnection_GetServerStatusHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3351,7 +3562,8 @@ exit_function: static void MmsJournalVariable_destroy(MmsJournalVariable self) { - if (self != NULL) { + if (self) + { GLOBAL_FREEMEM(self->tag); MmsValue_delete(self->value); GLOBAL_FREEMEM(self); @@ -3361,7 +3573,8 @@ MmsJournalVariable_destroy(MmsJournalVariable self) void MmsJournalEntry_destroy(MmsJournalEntry self) { - if (self != NULL) { + if (self) + { MmsValue_delete(self->entryID); MmsValue_delete(self->occurenceTime); LinkedList_destroyDeep(self->journalVariables, @@ -3441,7 +3654,8 @@ MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const MmsConnection_readJournalTimeRangeAsync(self, NULL, &err, domainId, itemId, startTime, endTime, readJournalHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3462,15 +3676,16 @@ void MmsConnection_readJournalTimeRangeAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId, MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } if ((MmsValue_getType(startTime) != MMS_BINARY_TIME) || - (MmsValue_getType(endTime) != MMS_BINARY_TIME)) { - + (MmsValue_getType(endTime) != MMS_BINARY_TIME)) + { if (mmsError) *mmsError = MMS_ERROR_INVALID_ARGUMENTS; goto exit_function; @@ -3514,7 +3729,8 @@ MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, cons MmsConnection_readJournalStartAfterAsync(self, NULL, &err, domainId, itemId, timeSpecification, entrySpecification, readJournalHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3535,15 +3751,16 @@ void MmsConnection_readJournalStartAfterAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId, MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } if ((MmsValue_getType(timeSpecification) != MMS_BINARY_TIME) || - (MmsValue_getType(entrySpecification) != MMS_OCTET_STRING)) { - + (MmsValue_getType(entrySpecification) != MMS_OCTET_STRING)) + { if (mmsError) *mmsError = MMS_ERROR_INVALID_ARGUMENTS; goto exit_function; @@ -3616,7 +3833,8 @@ MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filen MmsConnection_fileOpenAsync(self, NULL, &err, filename, initialPosition, fileOpenHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3649,7 +3867,8 @@ MmsConnection_fileOpenAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3721,7 +3940,8 @@ MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId) MmsConnection_fileCloseAsync(self, NULL, &err, frsmId, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3745,7 +3965,8 @@ MmsConnection_fileCloseAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErro { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3796,7 +4017,8 @@ MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fil MmsConnection_fileDeleteAsync(self, NULL, &err, fileName, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3821,7 +4043,8 @@ MmsConnection_fileDeleteAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErr { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3903,7 +4126,8 @@ MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, M MmsConnection_fileReadAsync(self, NULL, &err, frsmId, fileReadHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3983,7 +4207,8 @@ getFileDirHandler(uint32_t invokeId, void* parameter, MmsError mmsError, char* f parameters->err = mmsError; - if ((mmsError != MMS_ERROR_NONE) || (filename == NULL)) { + if ((mmsError != MMS_ERROR_NONE) || (filename == NULL)) + { parameters->moreFollows = moreFollows; /* last call --> unblock user thread */ @@ -4014,7 +4239,8 @@ MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const cha MmsConnection_getFileDirectoryAsync(self, NULL, &err, fileSpecification, continueAfter, getFileDirHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; moreFollows = parameter.moreFollows; @@ -4041,7 +4267,8 @@ MmsConnection_getFileDirectoryAsync(MmsConnection self, uint32_t* usedInvokeId, { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4092,7 +4319,8 @@ MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* cur MmsConnection_fileRenameAsync(self, NULL, &err, currentFileName, newFileName, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4116,7 +4344,8 @@ MmsConnection_fileRenameAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErr { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4167,7 +4396,8 @@ MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sou MmsConnection_obtainFileAsync(self, NULL, &err, sourceFile, destinationFile, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4199,7 +4429,8 @@ MmsConnection_obtainFileAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErr { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4268,7 +4499,8 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError, MmsConnection_writeVariableAsync(self, NULL, &err, domainId, itemId, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4287,7 +4519,8 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms const char* domainId, const char* itemId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4331,7 +4564,8 @@ MmsConnection_writeSingleArrayElementWithComponent(MmsConnection self, MmsError* MmsConnection_writeSingleArrayElementWithComponentAsync(self, NULL, &err, domainId, itemId, arrayIndex, componentId, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4351,7 +4585,8 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint uint32_t arrayIndex, const char* componentId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4396,7 +4631,8 @@ MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError, MmsConnection_writeVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4415,7 +4651,8 @@ MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvo const char* domainId, const char* itemId, const char* componentId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4481,8 +4718,8 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con MmsConnection_writeMultipleVariablesAsync(self, NULL, &err, domainId, items, values, writeMultipleVariablesHandler, ¶meter); - if (err == MMS_ERROR_NONE) { - + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; @@ -4508,7 +4745,8 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvo LinkedList /**/ items, LinkedList /* */ values, MmsConnection_WriteMultipleVariablesHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4521,7 +4759,8 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvo if (usedInvokeId) *usedInvokeId = invokeId; - if (mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload) != -1) { + if (mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload) != -1) + { MmsClientInternalParameter intParam; intParam.ptr = NULL; @@ -4530,7 +4769,8 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvo if (mmsError) *mmsError = err; } - else { + else + { *mmsError = MMS_ERROR_RESOURCE_OTHER; } @@ -4556,7 +4796,8 @@ MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError, MmsConnection_writeArrayElementsAsync(self, NULL, &err, domainId, itemId, index, numberOfElements, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4576,7 +4817,8 @@ MmsConnection_writeArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4620,8 +4862,8 @@ MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, boo MmsConnection_writeNamedVariableListAsync(self, NULL, &err, isAssociationSpecific, domainId, itemId, values, writeMultipleVariablesHandler, ¶meter); - if (err == MMS_ERROR_NONE) { - + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; @@ -4631,7 +4873,8 @@ MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, boo else LinkedList_destroyDeep(parameter.result, (LinkedListValueDeleteFunction) MmsValue_delete); } - else { + else + { if (accessResults) *accessResults = NULL; } @@ -4647,7 +4890,8 @@ MmsConnection_writeNamedVariableListAsync(MmsConnection self, uint32_t* usedInvo const char* domainId, const char* itemId, LinkedList /* */values, MmsConnection_WriteMultipleVariablesHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4677,7 +4921,8 @@ exit_function: void MmsConnection_sendRawData(MmsConnection self, MmsError* mmsError, uint8_t* buffer, int bufSize) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4699,16 +4944,19 @@ exit_function: void MmsServerIdentity_destroy(MmsServerIdentity* self) { - if (self->modelName != NULL) - GLOBAL_FREEMEM(self->modelName); + if (self) + { + if (self->modelName != NULL) + GLOBAL_FREEMEM(self->modelName); - if (self->vendorName != NULL) - GLOBAL_FREEMEM(self->vendorName); + if (self->vendorName != NULL) + GLOBAL_FREEMEM(self->vendorName); - if (self->revision != NULL) - GLOBAL_FREEMEM(self->revision); + if (self->revision != NULL) + GLOBAL_FREEMEM(self->revision); - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } MmsVariableAccessSpecification* @@ -4717,10 +4965,13 @@ MmsVariableAccessSpecification_create(char* domainId, char* itemId) MmsVariableAccessSpecification* self = (MmsVariableAccessSpecification*) GLOBAL_MALLOC(sizeof(MmsVariableAccessSpecification)); - self->domainId = domainId; - self->itemId = itemId; - self->arrayIndex = -1; - self->componentName = NULL; + if (self) + { + self->domainId = domainId; + self->itemId = itemId; + self->arrayIndex = -1; + self->componentName = NULL; + } return self; } @@ -4732,10 +4983,13 @@ MmsVariableAccessSpecification_createAlternateAccess(char* domainId, char* itemI MmsVariableAccessSpecification* self = (MmsVariableAccessSpecification*) GLOBAL_MALLOC(sizeof(MmsVariableAccessSpecification)); - self->domainId = domainId; - self->itemId = itemId; - self->arrayIndex = index; - self->componentName = componentName; + if (self) + { + self->domainId = domainId; + self->itemId = itemId; + self->arrayIndex = index; + self->componentName = componentName; + } return self; } @@ -4743,14 +4997,17 @@ MmsVariableAccessSpecification_createAlternateAccess(char* domainId, char* itemI void MmsVariableAccessSpecification_destroy(MmsVariableAccessSpecification* self) { - if (self->domainId != NULL) - GLOBAL_FREEMEM((void*) self->domainId); + if (self) + { + if (self->domainId != NULL) + GLOBAL_FREEMEM((void*) self->domainId); - if (self->itemId != NULL) - GLOBAL_FREEMEM((void*) self->itemId); + if (self->itemId != NULL) + GLOBAL_FREEMEM((void*) self->itemId); - if (self->componentName != NULL) - GLOBAL_FREEMEM((void*) self->componentName); + if (self->componentName != NULL) + GLOBAL_FREEMEM((void*) self->componentName); - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c index 4fca418e..5ea8fb01 100644 --- a/src/mms/iso_mms/client/mms_client_files.c +++ b/src/mms/iso_mms/client/mms_client_files.c @@ -180,7 +180,6 @@ exit_reject_invalid_pdu: mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); } - void mmsClient_handleFileReadRequest( MmsConnection connection, @@ -191,7 +190,7 @@ mmsClient_handleFileReadRequest( int32_t frsmId = BerDecoder_decodeInt32(buffer, maxBufPos - bufPos, bufPos); if (DEBUG_MMS_CLIENT) - printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", frsmId); + printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", (int)frsmId); MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId); @@ -401,7 +400,6 @@ mmsClient_createFileDirectoryRequest(uint32_t invokeId, ByteBuffer* request, con request->size = bufPos; } - void mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const char* currentFileName, const char* newFileName) { @@ -466,7 +464,6 @@ mmsClient_createObtainFileRequest(uint32_t invokeId, ByteBuffer* request, const request->size = bufPos; } - static bool parseFileAttributes(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* fileSize, uint64_t* lastModified) { @@ -610,7 +607,6 @@ parseListOfDirectoryEntries(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t return true; } - bool mmsClient_parseFileDirectoryResponse(ByteBuffer* response, int bufPos, uint32_t invokeId, MmsConnection_FileDirectoryHandler handler, void* parameter) { @@ -731,16 +727,14 @@ mmsMsg_parseFileOpenResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t } bool -mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeId, int frsmId, bool* moreFollows, MmsConnection_FileReadHandler handler, void* handlerParameter) +mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeId, int32_t frsmId, bool* moreFollows, MmsConnection_FileReadHandler handler, void* handlerParameter) { int length; uint8_t* data = NULL; int dataLen = 0; - uint8_t tag = buffer[bufPos++]; - if (tag != 0xbf) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: unknown tag %02x\n", tag); diff --git a/src/mms/iso_mms/client/mms_client_initiate.c b/src/mms/iso_mms/client/mms_client_initiate.c index 62ca427d..8eb930de 100644 --- a/src/mms/iso_mms/client/mms_client_initiate.c +++ b/src/mms/iso_mms/client/mms_client_initiate.c @@ -39,18 +39,16 @@ static uint8_t servicesSupported[] = { 0xee, 0x1c, 0x00, 0x00, 0x04, 0x08, 0x00, void mmsClient_createInitiateRequest(MmsConnection self, ByteBuffer* message) { - int maxServerOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; - int maxServerOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; int dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; uint32_t localDetailSize = BerEncoder_UInt32determineEncodedSize(self->parameters.maxPduSize); uint32_t proposedMaxServerOutstandingCallingSize = - BerEncoder_UInt32determineEncodedSize(maxServerOutstandingCalling); + BerEncoder_UInt32determineEncodedSize(self->maxOutstandingCalling); uint32_t proposedMaxServerOutstandingCalledSize = - BerEncoder_UInt32determineEncodedSize(maxServerOutstandingCalled); + BerEncoder_UInt32determineEncodedSize(self->maxOutstandingCalled); uint32_t dataStructureNestingLevelSize = BerEncoder_UInt32determineEncodedSize(dataStructureNestingLevel); @@ -76,11 +74,11 @@ mmsClient_createInitiateRequest(MmsConnection self, ByteBuffer* message) /* proposedMaxServerOutstandingCalling */ bufPos = BerEncoder_encodeTL(0x81, proposedMaxServerOutstandingCallingSize, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(maxServerOutstandingCalling, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(self->maxOutstandingCalling, buffer, bufPos); /* proposedMaxServerOutstandingCalled */ bufPos = BerEncoder_encodeTL(0x82, proposedMaxServerOutstandingCalledSize, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(maxServerOutstandingCalled, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(self->maxOutstandingCalled, buffer, bufPos); /* proposedDataStructureNestingLevel */ bufPos = BerEncoder_encodeTL(0x83, dataStructureNestingLevelSize, buffer, bufPos); @@ -147,7 +145,7 @@ parseInitResponseDetail(MmsConnection self, uint8_t* buffer, int bufPos, int max int i; for (i = 0; i < 11; i++) - self->parameters.servicesSupported[i] = buffer[bufPos + i]; + self->parameters.servicesSupported[i] = buffer[bufPos + i + 1]; /* add 1 to skip padding */ } break; @@ -169,8 +167,8 @@ mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response) { self->parameters.maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; self->parameters.dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; - self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; - self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + self->parameters.maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->parameters.maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; int bufPos = 1; /* ignore tag - already checked */ @@ -203,16 +201,16 @@ mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response) case 0x81: /* proposed-max-serv-outstanding-calling */ self->parameters.maxServOutstandingCalling = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->parameters.maxServOutstandingCalling > DEFAULT_MAX_SERV_OUTSTANDING_CALLING) - self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + if (self->parameters.maxServOutstandingCalling > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING) + self->parameters.maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; break; case 0x82: /* proposed-max-serv-outstanding-called */ self->parameters.maxServOutstandingCalled = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->parameters.maxServOutstandingCalled > DEFAULT_MAX_SERV_OUTSTANDING_CALLED) - self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + if (self->parameters.maxServOutstandingCalled > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED) + self->parameters.maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; break; case 0x83: /* proposed-data-structure-nesting-level */ diff --git a/src/mms/iso_mms/common/mms_type_spec.c b/src/mms/iso_mms/common/mms_type_spec.c index f83b804e..c1db5d89 100644 --- a/src/mms/iso_mms/common/mms_type_spec.c +++ b/src/mms/iso_mms/common/mms_type_spec.c @@ -1,7 +1,7 @@ /* * mms_type_spec.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -108,36 +108,40 @@ MmsVariableSpecification_getType(MmsVariableSpecification* self) bool MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, const MmsValue* value) { - if ((self->type) == (value->type)) { - - if ((self->type == MMS_STRUCTURE) || (self->type == MMS_ARRAY)) { - + if ((self->type) == (value->type)) + { + if ((self->type == MMS_STRUCTURE) || (self->type == MMS_ARRAY)) + { int componentCount = self->typeSpec.structure.elementCount; if (componentCount != (int) MmsValue_getArraySize(value)) return false; - if (self->type == MMS_STRUCTURE) { - + if (self->type == MMS_STRUCTURE) + { int i; - for (i = 0; i < componentCount; i++) { - + for (i = 0; i < componentCount; i++) + { if (MmsVariableSpecification_isValueOfType(self->typeSpec.structure.elements[i], MmsValue_getElement(value, i)) == false) return false; } return true; } - else { + else + { int i; - for (i = 0; i < componentCount; i++) { - + for (i = 0; i < componentCount; i++) + { if (MmsVariableSpecification_isValueOfType(self->typeSpec.array.elementTypeSpec, MmsValue_getElement(value, i)) == false) return false; } + + return true; } } - else if (self->type == MMS_BIT_STRING) { + else if (self->type == MMS_BIT_STRING) + { if (self->typeSpec.bitString == value->value.bitString.size) return true; diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index 549619a8..f88e232b 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -1463,13 +1463,15 @@ MmsValue_newOctetString(int size, int maxSize) { MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self) { + if (self) + { self->type = MMS_OCTET_STRING; self->value.octetString.size = size; self->value.octetString.maxSize = maxSize; self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, abs(maxSize)); - if (self->value.octetString.buf == NULL) { + if ((maxSize != 0) && (self->value.octetString.buf == NULL)) + { GLOBAL_FREEMEM(self); self = NULL; } @@ -1677,14 +1679,16 @@ exit_function: } static void -setVisibleStringValue(MmsValue* self, const char* string) +setVisibleStringValue(MmsValue* self, const char* value) { - if (self->value.visibleString.buf != NULL) { - if (string != NULL) { - - int newStringSize = strlen(string); + if (self->value.visibleString.buf != NULL) + { + if (value != NULL) + { + int newStringSize = strlen(value); - if (newStringSize > self->value.visibleString.size) { + if (newStringSize > self->value.visibleString.size) + { GLOBAL_FREEMEM(self->value.visibleString.buf); self->value.visibleString.buf = (char*) GLOBAL_MALLOC(newStringSize + 1); @@ -1694,7 +1698,7 @@ setVisibleStringValue(MmsValue* self, const char* string) self->value.visibleString.size = newStringSize; } - StringUtils_copyStringMax(self->value.visibleString.buf, self->value.visibleString.size + 1, string); + StringUtils_copyStringMax(self->value.visibleString.buf, self->value.visibleString.size + 1, value); } else self->value.visibleString.buf[0] = 0; @@ -1906,7 +1910,8 @@ MmsValue_newStringFromByteArray(const uint8_t* byteArray, int size, MmsType type self->value.visibleString.buf = StringUtils_createStringFromBuffer(byteArray, size); - if (self->value.visibleString.buf == NULL) { + if (self->value.visibleString.buf == NULL) + { GLOBAL_FREEMEM(self); self = NULL; } @@ -2008,17 +2013,20 @@ MmsValue_createArray(const MmsVariableSpecification* elementType, int size) self->value.structure.size = size; self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*)); - if (self->value.structure.components == NULL) { + if (self->value.structure.components == NULL) + { GLOBAL_FREEMEM(self); self = NULL; goto exit_function; } int i; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { self->value.structure.components[i] = MmsValue_newDefaultValue(elementType); - if (self->value.structure.components[i] == NULL) { + if (self->value.structure.components[i] == NULL) + { MmsValue_delete(self); self = NULL; goto exit_function; @@ -2102,9 +2110,10 @@ MmsValue_setDeletable(MmsValue* self) void MmsValue_setDeletableRecursive(MmsValue* self) { - if (self != NULL) { - - if ((MmsValue_getType(self) == MMS_ARRAY) || (MmsValue_getType(self) == MMS_STRUCTURE)) { + if (self) + { + if ((MmsValue_getType(self) == MMS_ARRAY) || (MmsValue_getType(self) == MMS_STRUCTURE)) + { int i; int elementCount = MmsValue_getArraySize(self); @@ -2179,7 +2188,8 @@ MmsValue_getTypeString(MmsValue* self) const char* MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) { - if (self == NULL) { + if (self == NULL) + { StringUtils_copyStringMax(buffer, bufferSize, "(null)"); return buffer; @@ -2240,7 +2250,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) int size = MmsValue_getBitStringSize(self); /* fill buffer with zeros */ - if (size + 1 > bufferSize) { + if (size + 1 > bufferSize) + { memset(buffer, 0, bufferSize); size = bufferSize - 1; @@ -2250,7 +2261,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) } int i; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { if (MmsValue_getBitStringBit(self, i)) buffer[bufPos++] = '1'; else @@ -2290,7 +2302,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) int size = MmsValue_getOctetStringSize(self); int bufPos = 0; int i; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { snprintf(buffer + bufPos, bufferSize - bufPos, "%02x", self->value.octetString.buf[i]); bufPos += 2; diff --git a/src/mms/iso_mms/server/mms_access_result.c b/src/mms/iso_mms/server/mms_access_result.c index b17461f0..95dcef8a 100644 --- a/src/mms/iso_mms/server/mms_access_result.c +++ b/src/mms/iso_mms/server/mms_access_result.c @@ -171,9 +171,12 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu if (bufPos < 0) goto exit_with_error; - /* if not indefinite length end tag, data length must be > 0 */ - if ((tag != 0) && (dataLength == 0)) - goto exit_with_error; + /* if not indefinite length end tag, visible-string, mms-string, or octet-string, data length must be > 0 */ + if (tag != 0) + { + if (tag != 0x8a && tag != 0x90 && tag != 0x89 && dataLength == 0) + goto exit_with_error; + } switch (tag) { @@ -192,8 +195,8 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu int i; - for (i = 0; i < elementCount; i++) { - + for (i = 0; i < elementCount; i++) + { int elementLength; int newBufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos + 1, dataEndBufPos); @@ -304,7 +307,8 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu break; case 0x91: /* MMS_UTC_TIME */ - if (dataLength == 8) { + if (dataLength == 8) + { value = MmsValue_newUtcTime(0); MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); bufPos += dataLength; diff --git a/src/mms/iso_mms/server/mms_association_service.c b/src/mms/iso_mms/server/mms_association_service.c index 83a32f3e..667f365d 100644 --- a/src/mms/iso_mms/server/mms_association_service.c +++ b/src/mms/iso_mms/server/mms_association_service.c @@ -330,9 +330,9 @@ parseInitiateRequestPdu(MmsServerConnection self, uint8_t* buffer, int bufPos, i self->dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; - self->maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; - self->maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + self->maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; self->negotiatedParameterCBC[0] = 0; self->negotiatedParameterCBC[1] = 0; @@ -367,16 +367,16 @@ parseInitiateRequestPdu(MmsServerConnection self, uint8_t* buffer, int bufPos, i case 0x81: /* proposed-max-serv-outstanding-calling */ self->maxServOutstandingCalling = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->maxServOutstandingCalling > DEFAULT_MAX_SERV_OUTSTANDING_CALLING) - self->maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + if (self->maxServOutstandingCalling > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING) + self->maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; break; case 0x82: /* proposed-max-serv-outstanding-called */ self->maxServOutstandingCalled = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->maxServOutstandingCalled > DEFAULT_MAX_SERV_OUTSTANDING_CALLED) - self->maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + if (self->maxServOutstandingCalled > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED) + self->maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; break; case 0x83: /* proposed-data-structure-nesting-level */ diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 2170f8c1..e42dd25a 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -129,7 +129,7 @@ appendMmsSubVariable(char* name, char* child) } static LinkedList -addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpecification* variable) +addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameList, MmsDomain* domain, char* prefix, MmsVariableSpecification* variable) { LinkedList listElement = nameList; @@ -158,14 +158,23 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ if (variableName) - { - listElement = LinkedList_insertAfter(listElement, variableName); + { + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, variableName, connection); + + if (accessAllowed) { + + listElement = LinkedList_insertAfter(listElement, variableName); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[index[i]]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[index[i]]); #else - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[i]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ + + } + else { + GLOBAL_FREEMEM(variableName); + } } } @@ -209,7 +218,11 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName) MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); - LinkedList_add(nameList, (void*) journal->name); + allowAccess = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_JOURNALS, domain, journal->name, connection); + + if (allowAccess) { + LinkedList_add(nameList, (void*) journal->name); + } } } @@ -233,7 +246,7 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) bool allowAccess = true; if (connection->server->getNameListHandler) { - allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection); + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, domain, connection); } if (allowAccess) { @@ -255,22 +268,28 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) for (i = 0; i < domain->namedVariablesCount; i++) { + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, variables[index[i]]->name, connection); + + if (accessAllowed) { + #if (CONFIG_MMS_SORT_NAME_LIST == 1) - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); #else - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); #endif #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1) - char* prefix = variables[index[i]]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); + char* prefix = variables[index[i]]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[index[i]]); #else - char* prefix = variables[i]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); + char* prefix = variables[i]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ + } + } #if (CONFIG_MMS_SORT_NAME_LIST == 1) @@ -286,7 +305,7 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) #if (MMS_DATA_SET_SERVICE == 1) static LinkedList -createStringsFromNamedVariableList(LinkedList variableLists) +createStringsFromNamedVariableList(LinkedList variableLists, MmsServerConnection connection, MmsDomain* domain) { LinkedList nameList = LinkedList_create(); LinkedList variableListsElement = LinkedList_getNext(variableLists); @@ -295,8 +314,12 @@ createStringsFromNamedVariableList(LinkedList variableLists) MmsNamedVariableList variableList = (MmsNamedVariableList) variableListsElement->data; - LinkedList_add(nameList, - StringUtils_copyString(MmsNamedVariableList_getName(variableList))); + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATASETS, domain, variableList->name, connection); + + if (accessAllowed) { + LinkedList_add(nameList, + StringUtils_copyString(MmsNamedVariableList_getName(variableList))); + } variableListsElement = LinkedList_getNext(variableListsElement); } @@ -323,7 +346,7 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain if (allowAccess) { LinkedList variableLists = MmsDomain_getNamedVariableLists(domain); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, domain); } } @@ -339,7 +362,7 @@ getNamedVariableListsVMDSpecific(MmsServerConnection connection) LinkedList variableLists = MmsDevice_getNamedVariableLists(device); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, NULL); return nameList; } @@ -352,7 +375,7 @@ getNamedVariableListAssociationSpecific(MmsServerConnection connection) LinkedList variableLists = MmsServerConnection_getNamedVariableLists(connection); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, NULL); return nameList; } diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index abdf6d02..4adb6f4c 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -1,7 +1,7 @@ /* * mms_get_var_access_service.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,236 +32,250 @@ static int createTypeSpecification ( - MmsVariableSpecification* namedVariable, - TypeSpecification_t* typeSpec) + MmsVariableSpecification* namedVariable, + TypeSpecification_t* typeSpec) { - if (namedVariable->type == MMS_ARRAY) { - typeSpec->present = TypeSpecification_PR_array; - - asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), - (long) namedVariable->typeSpec.array.elementCount); - - typeSpec->choice.array.packed = NULL; - typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, - typeSpec->choice.array.elementType); - } - else if (namedVariable->type == MMS_STRUCTURE) { - - typeSpec->present = TypeSpecification_PR_structure; - - int componentCount = namedVariable->typeSpec.structure.elementCount; - - typeSpec->choice.structure.components.list.count = componentCount; - typeSpec->choice.structure.components.list.size = componentCount; - - typeSpec->choice.structure.components.list.array - = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); - - int i; - - for (i = 0; i < componentCount; i++) { - - typeSpec->choice.structure.components.list.array[i] = - (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName = - (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName->buf = - (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentName->size = - strlen(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentType = - (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.structure.elements[i], - typeSpec->choice.structure.components.list.array[i]->componentType); - } - } - else { - - switch (namedVariable->type) { - case MMS_BOOLEAN: - typeSpec->present = TypeSpecification_PR_boolean; - break; - case MMS_BIT_STRING: - typeSpec->present = TypeSpecification_PR_bitstring; - typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; - break; - case MMS_INTEGER: - typeSpec->present = TypeSpecification_PR_integer; - typeSpec->choice.integer = namedVariable->typeSpec.integer; - break; - case MMS_UNSIGNED: - typeSpec->present = TypeSpecification_PR_unsigned; - typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; - break; - case MMS_FLOAT: - typeSpec->present = TypeSpecification_PR_floatingpoint; - typeSpec->choice.floatingpoint.exponentwidth = - namedVariable->typeSpec.floatingpoint.exponentWidth; - typeSpec->choice.floatingpoint.formatwidth = - namedVariable->typeSpec.floatingpoint.formatWidth; - break; - case MMS_OCTET_STRING: - typeSpec->present = TypeSpecification_PR_octetstring; - typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; - break; - case MMS_VISIBLE_STRING: - typeSpec->present = TypeSpecification_PR_visiblestring; - typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; - break; - case MMS_STRING: - typeSpec->present = TypeSpecification_PR_mMSString; - typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; - break; - case MMS_UTC_TIME: - typeSpec->present = TypeSpecification_PR_utctime; - break; - case MMS_BINARY_TIME: - typeSpec->present = TypeSpecification_PR_binarytime; - - if (namedVariable->typeSpec.binaryTime == 6) - typeSpec->choice.binarytime = 1; - else - typeSpec->choice.binarytime = 0; - - break; - default: - if (DEBUG_MMS_SERVER) - printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); - return -1; - break; - } - } - - return 1; + if (namedVariable->type == MMS_ARRAY) { + typeSpec->present = TypeSpecification_PR_array; + + asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), + (long) namedVariable->typeSpec.array.elementCount); + + typeSpec->choice.array.packed = NULL; + typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, + typeSpec->choice.array.elementType); + } + else if (namedVariable->type == MMS_STRUCTURE) { + + typeSpec->present = TypeSpecification_PR_structure; + + int componentCount = namedVariable->typeSpec.structure.elementCount; + + typeSpec->choice.structure.components.list.count = componentCount; + typeSpec->choice.structure.components.list.size = componentCount; + + typeSpec->choice.structure.components.list.array + = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); + + int i; + + for (i = 0; i < componentCount; i++) { + + typeSpec->choice.structure.components.list.array[i] = + (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName = + (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName->buf = + (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentName->size = + strlen(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentType = + (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.structure.elements[i], + typeSpec->choice.structure.components.list.array[i]->componentType); + } + } + else { + + switch (namedVariable->type) { + case MMS_BOOLEAN: + typeSpec->present = TypeSpecification_PR_boolean; + break; + case MMS_BIT_STRING: + typeSpec->present = TypeSpecification_PR_bitstring; + typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; + break; + case MMS_INTEGER: + typeSpec->present = TypeSpecification_PR_integer; + typeSpec->choice.integer = namedVariable->typeSpec.integer; + break; + case MMS_UNSIGNED: + typeSpec->present = TypeSpecification_PR_unsigned; + typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; + break; + case MMS_FLOAT: + typeSpec->present = TypeSpecification_PR_floatingpoint; + typeSpec->choice.floatingpoint.exponentwidth = + namedVariable->typeSpec.floatingpoint.exponentWidth; + typeSpec->choice.floatingpoint.formatwidth = + namedVariable->typeSpec.floatingpoint.formatWidth; + break; + case MMS_OCTET_STRING: + typeSpec->present = TypeSpecification_PR_octetstring; + typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; + break; + case MMS_VISIBLE_STRING: + typeSpec->present = TypeSpecification_PR_visiblestring; + typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; + break; + case MMS_STRING: + typeSpec->present = TypeSpecification_PR_mMSString; + typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; + break; + case MMS_UTC_TIME: + typeSpec->present = TypeSpecification_PR_utctime; + break; + case MMS_BINARY_TIME: + typeSpec->present = TypeSpecification_PR_binarytime; + + if (namedVariable->typeSpec.binaryTime == 6) + typeSpec->choice.binarytime = 1; + else + typeSpec->choice.binarytime = 0; + + break; + default: + if (DEBUG_MMS_SERVER) + printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); + return -1; + break; + } + } + + return 1; } static void freeTypeSpecRecursive(TypeSpecification_t* typeSpec) { - if (typeSpec->present == TypeSpecification_PR_structure) { - int elementCount = - typeSpec->choice.structure.components.list.count; - - int i; - - for (i = 0; i < elementCount; i++) { - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); - freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); - } - else if (typeSpec->present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); - freeTypeSpecRecursive(typeSpec->choice.array.elementType); - GLOBAL_FREEMEM(typeSpec->choice.array.elementType); - } + if (typeSpec->present == TypeSpecification_PR_structure) { + int elementCount = + typeSpec->choice.structure.components.list.count; + + int i; + + for (i = 0; i < elementCount; i++) { + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); + freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); + } + else if (typeSpec->present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); + freeTypeSpecRecursive(typeSpec->choice.array.elementType); + GLOBAL_FREEMEM(typeSpec->choice.array.elementType); + } } static void deleteVariableAccessAttributesResponse( - GetVariableAccessAttributesResponse_t* getVarAccessAttr) + GetVariableAccessAttributesResponse_t* getVarAccessAttr) { - if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { - int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; - - int i; - for (i = 0; i < count; i++) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); - TypeSpecification_t* typeSpec = - getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; - freeTypeSpecRecursive(typeSpec); - GLOBAL_FREEMEM(typeSpec); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); - - getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; - getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; - getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; - } else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; - freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); - - getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; - } + if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { + int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; + + int i; + for (i = 0; i < count; i++) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); + TypeSpecification_t* typeSpec = + getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; + freeTypeSpecRecursive(typeSpec); + GLOBAL_FREEMEM(typeSpec); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); + + getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; + getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; + getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; + } + else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; + freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); + + getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; + } } static void createVariableAccessAttributesResponse( - MmsServerConnection connection, - char* domainId, - char* nameId, - int invokeId, - ByteBuffer* response) + MmsServerConnection connection, + char* domainId, + char* nameId, + int invokeId, + ByteBuffer* response) { - MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDevice* device = MmsServer_getDevice(connection->server); - MmsVariableSpecification* namedVariable = NULL; + MmsVariableSpecification* namedVariable = NULL; - if (domainId != NULL) { - MmsDomain* domain = MmsDevice_getDomain(device, domainId); + MmsDomain* domain = NULL; - if (domain == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); + if (domainId != NULL) { + domain = MmsDevice_getDomain(device, domainId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + if (domain == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); - namedVariable = MmsDomain_getNamedVariable(domain, nameId); - } + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_function; + } + + namedVariable = MmsDomain_getNamedVariable(domain, nameId); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else - namedVariable = MmsDevice_getNamedVariable(device, nameId); + else + namedVariable = MmsDevice_getNamedVariable(device, nameId); #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ + if (namedVariable == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); + + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + + goto exit_function; + } + + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, nameId, connection); - if (namedVariable == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s not known\n", nameId); + if (!accessAllowed) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + goto exit_function; + } - MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); + MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); - mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = - ConfirmedServiceResponse_PR_getVariableAccessAttributes; + mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = + ConfirmedServiceResponse_PR_getVariableAccessAttributes; - GetVariableAccessAttributesResponse_t* getVarAccessAttr; + GetVariableAccessAttributesResponse_t* getVarAccessAttr; - getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. - confirmedServiceResponse.choice.getVariableAccessAttributes); + getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. + confirmedServiceResponse.choice.getVariableAccessAttributes); - getVarAccessAttr->mmsDeletable = 0; + getVarAccessAttr->mmsDeletable = 0; - createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); + createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); - asn_enc_rval_t rval = - der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); + asn_enc_rval_t rval = + der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); - if (rval.encoded == -1) { - response->size = 0; + if (rval.encoded == -1) { + response->size = 0; if (DEBUG_MMS_SERVER) printf("MMS getVariableAccessAttributes: message to large! send error PDU!\n"); @@ -270,81 +284,90 @@ createVariableAccessAttributesResponse( MMS_ERROR_SERVICE_OTHER); goto exit_function; - } + } - deleteVariableAccessAttributesResponse(getVarAccessAttr); + deleteVariableAccessAttributesResponse(getVarAccessAttr); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); exit_function: - return; + return; } int mmsServer_handleGetVariableAccessAttributesRequest( - MmsServerConnection connection, - uint8_t* buffer, int bufPos, int maxBufPos, - uint32_t invokeId, - ByteBuffer* response) + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) { - int retVal = 0; + int retVal = 0; - GetVariableAccessAttributesRequest_t* request = 0; + GetVariableAccessAttributesRequest_t* request = 0; - asn_dec_rval_t rval; /* Decoder return value */ + asn_dec_rval_t rval; /* Decoder return value */ - rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, - (void**) &request, buffer + bufPos, maxBufPos - bufPos); + rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, + (void**) &request, buffer + bufPos, maxBufPos - bufPos); - if (rval.code == RC_OK) { - if (request->present == GetVariableAccessAttributesRequest_PR_name) { - if (request->choice.name.present == ObjectName_PR_domainspecific) { - Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; - Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; + if (rval.code == RC_OK) + { + if (request->present == GetVariableAccessAttributesRequest_PR_name) + { + if (request->choice.name.present == ObjectName_PR_domainspecific) + { + Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; + Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; - char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); - createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(domainIdStr); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(domainIdStr); + GLOBAL_FREEMEM(nameIdStr); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (request->choice.name.present == ObjectName_PR_vmdspecific) { - Identifier_t nameId = request->choice.name.choice.vmdspecific; + else if (request->choice.name.present == ObjectName_PR_vmdspecific) + { + Identifier_t nameId = request->choice.name.choice.vmdspecific; - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); - createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(nameIdStr); + } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); - retVal = -1; - } - - asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); - - return retVal; + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); + retVal = -1; + } + + asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); + + if (ByteBuffer_getSize(response) > connection->maxPduSize) + { + ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + + return retVal; } #endif /* (MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1) */ - diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c index 85f1b960..a38cfd7f 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list_service.c +++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c @@ -130,16 +130,22 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, goto exit_function; } - if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && - (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present - == ConfirmedServiceRequest_PR_deleteNamedVariableList)) - { - request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); - } - else { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_function; - } + if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && + (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present + == ConfirmedServiceRequest_PR_deleteNamedVariableList)) + { + request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); + } + else { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; + } + + if (request->listOfVariableListName == NULL) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; + } long scopeOfDelete = DeleteNamedVariableListRequest__scopeOfDelete_specific; @@ -675,7 +681,8 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, LinkedList variable = LinkedList_getNext(variables); int i; - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { MmsNamedVariableListEntry variableEntry = (MmsNamedVariableListEntry) variable->data; varListResponse->listOfVariable.list.array[i] = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member*) @@ -740,8 +747,8 @@ mmsServer_handleGetNamedVariableListAttributesRequest( goto exit_function; } - if (request->present == ObjectName_PR_domainspecific) { - + if (request->present == ObjectName_PR_domainspecific) + { char domainName[65]; char itemName[65]; @@ -761,11 +768,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest( MmsDomain* domain = MmsDevice_getDomain(mmsDevice, domainName); - if (domain != NULL) { + if (domain != NULL) + { MmsNamedVariableList varList = MmsDomain_getNamedVariableList(domain, itemName); - if (varList) { - + if (varList) + { MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_DOMAIN_SPECIFIC, domain, varList->name, connection); if (accessError == MMS_ERROR_NONE) { @@ -792,8 +800,8 @@ mmsServer_handleGetNamedVariableListAttributesRequest( } #if (MMS_DYNAMIC_DATA_SETS == 1) - else if (request->present == ObjectName_PR_aaspecific) { - + else if (request->present == ObjectName_PR_aaspecific) + { char listName[65]; if (request->choice.aaspecific.size > 64) { @@ -806,11 +814,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest( MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName); - if (varList) { - + if (varList) + { MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_ASSOCIATION_SPECIFIC, NULL, varList->name, connection); - if (accessError == MMS_ERROR_NONE) { + if (accessError == MMS_ERROR_NONE) + { if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) { /* encoding failed - probably because buffer size is too small for message */ @@ -829,7 +838,8 @@ mmsServer_handleGetNamedVariableListAttributesRequest( mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ - else if (request->present == ObjectName_PR_vmdspecific) { + else if (request->present == ObjectName_PR_vmdspecific) + { char listName[65]; if (request->choice.vmdspecific.size > 64) { @@ -844,11 +854,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest( MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName); - if (varList) { - + if (varList) + { MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_VMD_SPECIFIC, NULL, varList->name, connection); - if (accessError == MMS_ERROR_NONE) { + if (accessError == MMS_ERROR_NONE) + { if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) { /* encoding failed - probably because buffer size is too small for message */ @@ -870,6 +881,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest( mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } + if (ByteBuffer_getSize(response) > connection->maxPduSize) + { + ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + exit_function: asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetNamedVariableListAttributesRequest, diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 2b33dc41..043e03ce 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -1,7 +1,7 @@ /* * mms_read_service.c * - * Copyright 2013-2023 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -202,30 +202,35 @@ alternateArrayAccess(MmsServerConnection connection, MmsValue* arrayValue = mmsServer_getValue(connection->server, domain, itemId, connection, false); - if (arrayValue != NULL) { - + if (arrayValue != NULL) + { MmsValue* value = NULL; if (numberOfElements == 0) - if (mmsServer_isAccessToArrayComponent(alternateAccess)) { + { + if (mmsServer_isAccessToArrayComponent(alternateAccess)) + { if (namedVariable->typeSpec.array.elementTypeSpec->type == MMS_STRUCTURE) { MmsValue* structValue = MmsValue_getElement(arrayValue, index); if (structValue != NULL) value = mmsServer_getComponentOfArrayElement(alternateAccess, - namedVariable, structValue); + namedVariable, structValue, NULL); } } else { value = MmsValue_getElement(arrayValue, index); } - else { + } + else + { value = MmsValue_createEmptyArray(numberOfElements); MmsValue_setDeletable(value); int resultIndex = 0; - while (index < lowIndex + numberOfElements) { + while (index < lowIndex + numberOfElements) + { MmsValue* elementValue = NULL; elementValue = MmsValue_getElement(arrayValue, index); @@ -352,7 +357,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in varAccessSpecSize += itemIdLen + BerEncoder_determineLengthSize(itemIdLen) + 1; - if (accessSpec->domainId != NULL) { + if (accessSpec->domainId != NULL) + { uint32_t domainIdLen = strlen(accessSpec->domainId); varAccessSpecSize += domainIdLen + BerEncoder_determineLengthSize(domainIdLen) + 1; @@ -370,7 +376,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in varAccessSpecSize += 1 + BerEncoder_determineLengthSize(varAccessSpecLength); - if (encode == false) { + if (encode == false) + { bufPos = varAccessSpecSize; goto exit_function; } @@ -378,8 +385,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in /* encode to buffer */ bufPos = BerEncoder_encodeTL(0xa0, varAccessSpecLength, buffer, bufPos); - if (accessSpec->isNamedVariableList == true) { - + if (accessSpec->isNamedVariableList == true) + { bufPos = BerEncoder_encodeTL(0xa1, variableListNameLength, buffer, bufPos); if (accessSpec->specific == 0) { /* vmd-specific */ @@ -425,8 +432,8 @@ encodeReadResponse(MmsServerConnection connection, /* iterate values list to determine encoded size */ LinkedList value = LinkedList_getNext(values); - for (i = 0; i < variableCount; i++) { - + for (i = 0; i < variableCount; i++) + { MmsValue* data = (MmsValue*) value->data; accessResultSize += MmsValue_encodeMmsData(data, NULL, 0, false); @@ -452,12 +459,13 @@ encodeReadResponse(MmsServerConnection connection, confirmedResponseContentSize; /* Check if message would fit in the MMS PDU */ - if (mmsPduSize > connection->maxPduSize) { + if (mmsPduSize > connection->maxPduSize) + { if (DEBUG_MMS_SERVER) printf("MMS read: message to large! send error PDU!\n"); mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_SERVICE_OTHER); + MMS_ERROR_RESOURCE_OTHER); goto exit_function; } @@ -487,7 +495,8 @@ encodeReadResponse(MmsServerConnection connection, /* encode access results */ value = LinkedList_getNext(values); - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { MmsValue* data = (MmsValue*) value->data; bufPos = MmsValue_encodeMmsData(data, buffer, bufPos, true); @@ -529,16 +538,18 @@ handleReadListOfVariablesRequest( int i; - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { VariableSpecification_t varSpec = read->variableAccessSpecification.choice.listOfVariable.list.array[i]->variableSpecification; AlternateAccess_t* alternateAccess = read->variableAccessSpecification.choice.listOfVariable.list.array[i]->alternateAccess; - if (varSpec.present == VariableSpecification_PR_name) { - - if (varSpec.choice.name.present == ObjectName_PR_domainspecific) { + if (varSpec.present == VariableSpecification_PR_name) + { + if (varSpec.choice.name.present == ObjectName_PR_domainspecific) + { char domainIdStr[65]; char nameIdStr[65]; @@ -571,7 +582,8 @@ handleReadListOfVariablesRequest( } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (varSpec.choice.name.present == ObjectName_PR_vmdspecific) { + else if (varSpec.choice.name.present == ObjectName_PR_vmdspecific) + { char nameIdStr[65]; mmsMsg_copyAsn1IdentifierToStringBuffer(varSpec.choice.name.choice.vmdspecific, nameIdStr, 65); @@ -607,11 +619,12 @@ handleReadListOfVariablesRequest( LinkedList valueElement = LinkedList_getNext(values); - while (valueElement) { - + while (valueElement) + { MmsValue* value = (MmsValue*) LinkedList_getData(valueElement); - if (value) { + if (value) + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { if (MmsValue_getDataAccessError(value) == DATA_ACCESS_ERROR_NO_RESPONSE) { sendResponse = false; @@ -637,21 +650,24 @@ static void addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVariable, MmsDomain* domain, char* nameIdStr, LinkedList /**/ values, MmsServerConnection connection, MmsNamedVariableListEntry listEntry) { - if (namedVariable != NULL) { - + if (namedVariable != NULL) + { if (DEBUG_MMS_SERVER) printf("MMS read: found named variable %s with search string %s\n", namedVariable->name, nameIdStr); MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection, false); - if (value) { - if (listEntry->arrayIndex != -1) { - if (MmsValue_getType(value) == MMS_ARRAY) { - + if (value) + { + if (listEntry->arrayIndex != -1) + { + if (MmsValue_getType(value) == MMS_ARRAY) + { MmsValue* elementValue = MmsValue_getElement(value, listEntry->arrayIndex); - if (listEntry->componentName) { + if (listEntry->componentName) + { MmsVariableSpecification* elementType = namedVariable->typeSpec.array.elementTypeSpec; MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, listEntry->componentName); @@ -669,7 +685,8 @@ addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVar } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: data set entry of unexpected type!\n"); @@ -697,8 +714,8 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable LinkedList variable = LinkedList_getNext(variables); - while (variable) { - + while (variable) + { MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data; MmsDomain* variableDomain = MmsNamedVariableListEntry_getDomain(variableListEntry); @@ -757,11 +774,13 @@ handleReadNamedVariableListRequest( MmsDomain* domain = MmsDevice_getDomain(MmsServer_getDevice(connection->server), domainIdStr); - if (domain == NULL) { + if (domain == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS read: domain %s not found!\n", domainIdStr); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } - else { + else + { MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); if (namedList) @@ -796,12 +815,15 @@ handleReadNamedVariableListRequest( MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName); if (namedList == NULL) + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { - + } + else + { MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_VMD_SPECIFIC, NULL, namedList->name, connection); - if (accessError == MMS_ERROR_NONE) { + if (accessError == MMS_ERROR_NONE) + { VarAccessSpec accessSpec; accessSpec.isNamedVariableList = true; @@ -816,7 +838,6 @@ handleReadNamedVariableListRequest( mmsMsg_createServiceErrorPdu(invokeId, response, accessError); } - } } #if (MMS_DYNAMIC_DATA_SETS == 1) @@ -831,13 +852,15 @@ handleReadNamedVariableListRequest( MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName); if (namedList == NULL) + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { - + } + else + { MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection); - if (accessError == MMS_ERROR_NONE) { - + if (accessError == MMS_ERROR_NONE) + { VarAccessSpec accessSpec; accessSpec.isNamedVariableList = true; @@ -891,7 +914,8 @@ mmsServer_handleReadRequest( goto exit_function; } - if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) { + if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) + { MmsServer_lockModel(connection->server); handleReadListOfVariablesRequest(connection, request, invokeId, response); @@ -899,7 +923,8 @@ mmsServer_handleReadRequest( MmsServer_unlockModel(connection->server); } #if (MMS_DATA_SET_SERVICE == 1) - else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) { + else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) + { MmsServer_lockModel(connection->server); handleReadNamedVariableListRequest(connection, request, invokeId, response); @@ -911,6 +936,12 @@ mmsServer_handleReadRequest( mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } + if (ByteBuffer_getSize(response) > connection->maxPduSize) + { + ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + exit_function: asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index b9d4c798..2f6f2b27 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -351,6 +351,13 @@ MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler writeHandl self->writeHandlerParameter = parameter; } +void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter) +{ + self->listAccessHandler = listAccessHandler; + self->listAccessHandlerParameter = parameter; +} + void MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connectionHandler, void* parameter) { @@ -529,9 +536,10 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va { MmsDataAccessError indication; - if (self->writeHandler != NULL) { + if (self->writeHandler) + { indication = self->writeHandler(self->writeHandlerParameter, domain, - itemId, value, connection); + itemId, -1, NULL, value, connection); } else { MmsValue* cachedValue; @@ -541,7 +549,36 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va cachedValue = MmsServer_getValueFromCache(self, domain, itemId); - if (cachedValue != NULL) { + if (cachedValue) { + MmsValue_update(cachedValue, value); + indication = DATA_ACCESS_ERROR_SUCCESS; + } else + indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + } + + return indication; +} + +MmsDataAccessError +mmsServer_setValueEx(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value, + MmsServerConnection connection, int arrayIdx, const char* componentId) +{ + MmsDataAccessError indication; + + if (self->writeHandler) + { + indication = self->writeHandler(self->writeHandlerParameter, domain, + itemId, arrayIdx, componentId, value, connection); + } + else { + MmsValue* cachedValue = NULL; + + if (domain == NULL) + domain = (MmsDomain*) self->device; + + cachedValue = MmsServer_getValueFromCacheEx2(self, domain, itemId, arrayIdx, componentId); + + if (cachedValue) { MmsValue_update(cachedValue, value); indication = DATA_ACCESS_ERROR_SUCCESS; } else @@ -579,6 +616,32 @@ exit_function: return value; } +MmsDataAccessError +mmsServer_checkReadAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess) +{ + MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS; + + if (self->readAccessHandler) { + accessError = + self->readAccessHandler(self->readAccessHandlerParameter, (domain == (MmsDomain*) self->device) ? NULL : domain, + itemId, connection, isDirectAccess); + } + + return accessError; +} + +bool +mmsServer_checkListAccess(MmsServer self, MmsGetNameListType listType, MmsDomain* domain, char* itemId, MmsServerConnection connection) +{ + bool allowAccess = true; + + if (self->listAccessHandler) { + allowAccess = self->listAccessHandler(self->listAccessHandlerParameter, listType, domain, itemId, connection); + } + + return allowAccess; +} + MmsDevice* MmsServer_getDevice(MmsServer self) { diff --git a/src/mms/iso_mms/server/mms_server_common.c b/src/mms/iso_mms/server/mms_server_common.c index 455a26ec..545702be 100644 --- a/src/mms/iso_mms/server/mms_server_common.c +++ b/src/mms/iso_mms/server/mms_server_common.c @@ -287,7 +287,7 @@ mmsServer_isAccessToArrayComponent(AlternateAccess_t* alternateAccess) MmsValue* mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, - MmsValue* structuredValue) + MmsValue* structuredValue, char* componentId) { MmsValue* retValue = NULL; @@ -309,24 +309,43 @@ mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVari goto exit_function; int i; - for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) { - + for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) + { if ((int) strlen(structSpec->typeSpec.structure.elements[i]->name) - == component.size) { + == component.size) + { if (strncmp(structSpec->typeSpec.structure.elements[i]->name, - (char*) component.buf, component.size) == 0) { + (char*) component.buf, component.size) == 0) + { MmsValue* value = MmsValue_getElement(structuredValue, i); - if (mmsServer_isAccessToArrayComponent( - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) { - retValue = - mmsServer_getComponentOfArrayElement( - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, - structSpec->typeSpec.structure.elements[i], - value); + if (value) + { + if (mmsServer_isAccessToArrayComponent( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) + { + if (componentId) + { + strcat(componentId, structSpec->typeSpec.structure.elements[i]->name); + strcat(componentId, "$"); + } + + retValue = + mmsServer_getComponentOfArrayElement( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + structSpec->typeSpec.structure.elements[i], + value, componentId); + } + else + { + if (componentId) + { + strcat(componentId, structSpec->typeSpec.structure.elements[i]->name); + } + + retValue = value; + } } - else - retValue = value; goto exit_function; } diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c index 096bc30f..f46ddef6 100644 --- a/src/mms/iso_mms/server/mms_write_service.c +++ b/src/mms/iso_mms/server/mms_write_service.c @@ -517,33 +517,36 @@ getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_ { MmsVariableSpecification* retValue = NULL; - if (mmsServer_isComponentAccess(alternateAccess)) { + if (mmsServer_isComponentAccess(alternateAccess)) + { Identifier_t component = alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component; if (component.size > 129) goto exit_function; - if (namedVariable->type == MMS_STRUCTURE) { - + if (namedVariable->type == MMS_STRUCTURE) + { int i; - for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) { - + for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) + { if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name) - == component.size) { + == component.size) + { if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name, (char*) component.buf, component.size)) { - if (strlen(variableName) + component.size < 199) { - + if (strlen(variableName) + component.size < 199) + { StringUtils_appendString(variableName, 200, "$"); /* here we need strncat because component.buf is not null terminated! */ strncat(variableName, (const char*)component.buf, (size_t)component.size); if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess - != NULL) { + != NULL) + { retValue = getComponent(connection, domain, alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, @@ -598,12 +601,13 @@ mmsServer_handleWriteRequest( MmsServer_lockModel(connection->server); - if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) { + if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) + { handleWriteNamedVariableListRequest(connection, writeRequest, invokeId, response); goto exit_function; } - else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) { - + else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) + { int numberOfWriteItems = writeRequest->variableAccessSpecification.choice.listOfVariable.list.count; if (numberOfWriteItems < 1) { @@ -627,7 +631,8 @@ mmsServer_handleWriteRequest( int i; - for (i = 0; i < numberOfWriteItems; i++) { + for (i = 0; i < numberOfWriteItems; i++) + { ListOfVariableSeq_t* varSpec = writeRequest->variableAccessSpecification.choice.listOfVariable.list.array[i]; @@ -644,7 +649,8 @@ mmsServer_handleWriteRequest( char nameIdStr[65]; - if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) { + if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) + { Identifier_t domainId = varSpec->variableSpecification.choice.name.choice.domainspecific.domainId; char domainIdStr[65]; @@ -687,8 +693,8 @@ mmsServer_handleWriteRequest( AlternateAccess_t* alternateAccess = varSpec->alternateAccess; - if (alternateAccess != NULL) { - + if (alternateAccess) + { if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; continue; @@ -714,12 +720,13 @@ mmsServer_handleWriteRequest( continue; } - if (alternateAccess != NULL) { - + if (alternateAccess) + { if (domain == NULL) domain = (MmsDomain*) device; - if (mmsServer_isIndexAccess(alternateAccess)) { + if (mmsServer_isIndexAccess(alternateAccess)) + { MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr); if (cachedArray == NULL) { @@ -730,8 +737,8 @@ mmsServer_handleWriteRequest( int index = mmsServer_getLowIndex(alternateAccess); int numberOfElements = mmsServer_getNumberOfElements(alternateAccess); - if (numberOfElements == 0) { /* select single array element with index */ - + if (numberOfElements == 0) /* select single array element with index */ + { MmsValue* elementValue = MmsValue_getElement(cachedArray, index); if (elementValue == NULL) { @@ -739,34 +746,45 @@ mmsServer_handleWriteRequest( goto end_of_main_loop; } - if (mmsServer_isAccessToArrayComponent(alternateAccess)) { + if (mmsServer_isAccessToArrayComponent(alternateAccess)) + { MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); + char componentId[65]; + componentId[0] = 0; + if (namedVariable) { - elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue); + elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue, componentId); } if ((namedVariable == NULL) || (elementValue == NULL)) { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; - goto end_of_main_loop; } - } + else + { + accessResults[i] = mmsServer_setValueEx(connection->server, domain, nameIdStr, value, connection, index, componentId); + } - if (MmsValue_update(elementValue, value) == false) { - accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + goto end_of_main_loop; + } + else + { + accessResults[i] = mmsServer_setValueEx(connection->server, domain, nameIdStr, value, connection, index, NULL); goto end_of_main_loop; } } - else { /* select sub-array with start-index and number-of-elements */ - - if (MmsValue_getType(value) != MMS_ARRAY) { + else /* select sub-array with start-index and number-of-elements */ + { + if (MmsValue_getType(value) != MMS_ARRAY) + { accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } int elementNo; - for (elementNo = 0; elementNo < numberOfElements; elementNo++) { + for (elementNo = 0; elementNo < numberOfElements; elementNo++) + { MmsValue* newElement = MmsValue_getElement(value, elementNo); MmsValue* elementValue = MmsValue_getElement(cachedArray, index++); @@ -785,36 +803,37 @@ mmsServer_handleWriteRequest( accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; goto end_of_main_loop; } - else if (mmsServer_isComponentAccess(alternateAccess)) { + else if (mmsServer_isComponentAccess(alternateAccess)) + { variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr); - if (variable == NULL) { + if (variable == NULL) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; goto end_of_main_loop; } } - else { + else + { accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; goto end_of_main_loop; } } /* Check for correct type */ - if (MmsVariableSpecification_isValueOfType(variable, value) == false) { + if (MmsVariableSpecification_isValueOfType(variable, value) == false) + { accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } - MmsDataAccessError valueIndication = - mmsServer_setValue(connection->server, domain, nameIdStr, value, connection); - - if (valueIndication == DATA_ACCESS_ERROR_NO_RESPONSE) - sendResponse = false; - - accessResults[i] = valueIndication; + accessResults[i] = mmsServer_setValue(connection->server, domain, nameIdStr, value, connection); end_of_main_loop: + if (accessResults[i] == DATA_ACCESS_ERROR_NO_RESPONSE) + sendResponse = false; + MmsValue_delete(value); } diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c index 99f5d34f..ba5d8d6e 100644 --- a/src/sampled_values/sv_subscriber.c +++ b/src/sampled_values/sv_subscriber.c @@ -57,6 +57,7 @@ struct sSVReceiver { #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore subscriberListLock; + Thread thread; #endif }; @@ -99,6 +100,7 @@ SVReceiver_create(void) #if (CONFIG_MMS_THREADLESS_STACK == 0) self->subscriberListLock = Semaphore_create(1); + self->thread = NULL; #endif } @@ -193,15 +195,19 @@ SVReceiver_start(SVReceiver self) if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: SV receiver started for interface %s\n", self->interfaceId); - Thread thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, true); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + + self->thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, false); - if (thread) { - Thread_start(thread); + if (self->thread) { + Thread_start(self->thread); } else { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Failed to start thread\n"); } + +#endif /* (CONFIG_MMS_THREADLESS_STACK == 0) */ } else { if (DEBUG_SV_SUBSCRIBER) @@ -220,16 +226,24 @@ void SVReceiver_stop(SVReceiver self) { if (self->running) { - SVReceiver_stopThreadless(self); + self->running = false; - while (self->stopped == false) - Thread_sleep(1); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + if (self->thread) { + Thread_destroy(self->thread); + self->thread = NULL; + } +#endif /* (CONFIG_MMS_THREADLESS_STACK == 0) */ + + SVReceiver_stopThreadless(self); } } void SVReceiver_destroy(SVReceiver self) { + SVReceiver_stop(self); + LinkedList_destroyDeep(self->subscriberList, (LinkedListValueDeleteFunction) SVSubscriber_destroy); @@ -237,7 +251,12 @@ SVReceiver_destroy(SVReceiver self) GLOBAL_FREEMEM(self->interfaceId); #if (CONFIG_MMS_THREADLESS_STACK == 0) - Semaphore_destroy(self->subscriberListLock); + if (self->thread) { + Thread_destroy(self->thread); + self->thread = NULL; + } + + Semaphore_destroy(self->subscriberListLock); #endif GLOBAL_FREEMEM(self->buffer); diff --git a/tools/model_generator/build2.sh b/tools/model_generator/build2.sh index 8bf31dfb..52bd6f80 100755 --- a/tools/model_generator/build2.sh +++ b/tools/model_generator/build2.sh @@ -4,7 +4,7 @@ mkdir build find src/ -name "*.java" > listFile.tmp -javac -target 1.6 -source 1.6 -d build @listFile.tmp +javac -target 1.8 -source 1.8 -d build @listFile.tmp jar cfm genconfig.jar manifest-dynamic.mf -C build/ com/ diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar index 2c9ed1fc..51e83774 100644 Binary files a/tools/model_generator/genconfig.jar and b/tools/model_generator/genconfig.jar differ diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index 752df205..00bc30b2 100644 Binary files a/tools/model_generator/genmodel.jar and b/tools/model_generator/genmodel.jar differ diff --git a/tools/model_generator/src/com/libiec61850/scl/SclParser.java b/tools/model_generator/src/com/libiec61850/scl/SclParser.java index a606b27f..676d1507 100644 --- a/tools/model_generator/src/com/libiec61850/scl/SclParser.java +++ b/tools/model_generator/src/com/libiec61850/scl/SclParser.java @@ -291,6 +291,25 @@ public class SclParser SclParser sclParser = new SclParser(stream); } + public List getConnectedAPs() + { + List aps = new LinkedList(); + + if (communication != null) { + List subNetworks = communication.getSubNetworks(); + + for (SubNetwork subNetwork : subNetworks) { + List connectedAPs = subNetwork.getConnectedAPs(); + + for (ConnectedAP connectedAP : connectedAPs) { + aps.add(connectedAP); + } + } + } + + return aps; + } + public ConnectedAP getConnectedAP(IED ied, String accessPointName) { communication = this.getCommunication(); diff --git a/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java b/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java index d9765b04..b3cf608d 100644 --- a/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java +++ b/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java @@ -85,7 +85,7 @@ public class ConnectedAP { public List getSmvs() { return smvs; } - + public GSE lookupGSE(String logicalDeviceName, String name) { for (GSE gse : this.getGses()) { @@ -97,6 +97,18 @@ public class ConnectedAP { return null; } + + public SMV lookupSMV(String logicalDeviceName, String name) { + + for (SMV smv : this.getSmvs()) { + if (smv.getLdInst().equals(logicalDeviceName)) { + if (smv.getCbName().equals(name)) + return smv; + } + } + + return null; + } public PhyComAddress lookupSMVAddress(String logicalDeviceName, String name) { diff --git a/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java b/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java index 2bd18a3d..ccf4d8f0 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java @@ -188,19 +188,35 @@ public class DataModelValue { case TIMESTAMP: case ENTRY_TIME: - try { + { String modValueString = value.replace(',', '.'); - SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS"); - parser.setTimeZone(TimeZone.getTimeZone("UTC")); + try { + SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS"); + parser.setTimeZone(TimeZone.getTimeZone("UTC")); + + Date date = parser.parse(modValueString); - Date date = parser.parse(modValueString); + this.value = new Long(date.toInstant().toEpochMilli()); + + break; + } + catch (java.text.ParseException e) {}; + + try { + SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss"); + parser.setTimeZone(TimeZone.getTimeZone("UTC")); + + Date date = parser.parse(modValueString); - this.value = new Long(date.toInstant().toEpochMilli()); - } - catch (java.text.ParseException e) { + this.value = new Long(date.toInstant().toEpochMilli()); + + break; + } + catch (java.text.ParseException e) {}; + this.value = null; - System.out.println("Warning: Val element does not contain a valid time stamp: " + e.getMessage()); + System.out.println("Warning: Val element does not contain a valid time stamp: " + value); } break; diff --git a/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java b/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java index f2f03b66..6ef8c29b 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java @@ -16,9 +16,8 @@ public class SampledValueControl { private int nofASDU; private boolean multicast = false; private SmvOpts smvOpts; - - - + private SmpMod smpMod = SmpMod.SMP_PER_PERIOD; + public SampledValueControl(Node smvControlNode) throws SclParserException { this.name = ParserUtils.parseAttribute(smvControlNode, "name"); this.desc = ParserUtils.parseAttribute(smvControlNode, "desc"); @@ -49,10 +48,25 @@ public class SampledValueControl { Node smvOptsNode = ParserUtils.getChildNodeWithTag(smvControlNode, "SmvOpts"); this.smvOpts = new SmvOpts(smvOptsNode); + + String smpModString = ParserUtils.parseAttribute(smvControlNode, "smpMod"); + + if (smpModString != null) { + if (smpModString.equals("SmpPerPeriod")) { + smpMod = SmpMod.SMP_PER_PERIOD; + } + else if (smpModString.equals("SmpPerSec")) { + smpMod = SmpMod.SMP_PER_SECOND; + } + else if (smpModString.equals("SecPerSmp")) { + smpMod = SmpMod.SEC_PER_SMP; + } + else { + throw new SclParserException(smvControlNode, "Invalid smpMod value " + smpModString); + } + } } - - - + public String getName() { return name; } @@ -88,5 +102,8 @@ public class SampledValueControl { public SmvOpts getSmvOpts() { return smvOpts; } - + + public SmpMod getSmpMod() { + return smpMod; + } } diff --git a/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java b/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java new file mode 100644 index 00000000..4de9f6eb --- /dev/null +++ b/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java @@ -0,0 +1,56 @@ +package com.libiec61850.scl.model; + +/* + * Copyright 2023 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +public enum SmpMod +{ + SMP_PER_PERIOD(0), + SMP_PER_SECOND(1), + SEC_PER_SMP(2); + + private int value; + + private SmpMod(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public boolean compare(int i) + { + return (value == i); + } + + public static SmpMod fromValue(int val) + { + SmpMod[] errors = SmpMod.values(); + + for (int i = 0; i < errors.length; i++) { + if (errors[i].compare(val)) + return errors[i]; + } + + return SmpMod.SMP_PER_PERIOD; + } +} diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java index cd1d4593..fffd94ef 100644 --- a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java @@ -3,7 +3,7 @@ package com.libiec61850.tools; /* * DynamicModelGenerator.java * - * Copyright 2014-2020 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -37,6 +37,7 @@ import com.libiec61850.scl.SclParserException; import com.libiec61850.scl.communication.ConnectedAP; import com.libiec61850.scl.communication.GSE; import com.libiec61850.scl.communication.PhyComAddress; +import com.libiec61850.scl.communication.SMV; import com.libiec61850.scl.model.AccessPoint; import com.libiec61850.scl.model.DataAttribute; import com.libiec61850.scl.model.DataModelValue; @@ -51,6 +52,7 @@ import com.libiec61850.scl.model.LogicalDevice; import com.libiec61850.scl.model.LogicalNode; import com.libiec61850.scl.model.ReportControlBlock; import com.libiec61850.scl.model.ReportSettings; +import com.libiec61850.scl.model.SampledValueControl; import com.libiec61850.scl.model.Services; import com.libiec61850.scl.model.SettingControl; @@ -59,6 +61,7 @@ public class DynamicModelGenerator { private ConnectedAP connectedAP; private IED ied = null; private boolean hasOwner = false; + private List connectedAPs; public DynamicModelGenerator(InputStream stream, String icdFile, PrintStream output, String iedName, String accessPointName) throws SclParserException { @@ -94,6 +97,8 @@ public class DynamicModelGenerator { throw new SclParserException("No valid access point found!"); this.connectedAP = sclParser.getConnectedAP(ied, accessPoint.getName()); + + this.connectedAPs = sclParser.getConnectedAPs(); List logicalDevices = accessPoint.getServer().getLogicalDevices(); @@ -166,6 +171,64 @@ public class DynamicModelGenerator { for (Log log : logicalNode.getLogs()) output.println("LOG(" + log.getName() + ");"); + + for (SampledValueControl svcb : logicalNode.getSampledValueControlBlocks()) { + LogicalDevice ld = logicalNode.getParentLogicalDevice(); + + SMV smv = null; + PhyComAddress smvAddress = null; + + if (connectedAP != null) { + smv = connectedAP.lookupSMV(ld.getInst(), svcb.getName()); + + if (smv == null) { + for (ConnectedAP ap : connectedAPs) { + smv = ap.lookupSMV(ld.getInst(), svcb.getName()); + + if (smv != null) + break; + } + } + + if (smv == null) + System.out.println("ConnectedAP not found for SMV"); + + if (smv != null) + smvAddress = smv.getAddress(); + } + else + System.out.println("WARNING: IED \"" + ied.getName() + "\" has no connected access point!"); + + output.print("SMVC("); + output.print(svcb.getName() + " "); + output.print(svcb.getSmvID() + " "); + output.print(svcb.getDatSet() + " "); + output.print(svcb.getConfRev() + " "); + output.print(svcb.getSmpMod().getValue() + " "); + output.print(svcb.getSmpRate() + " "); + output.print(svcb.getSmvOpts().getIntValue() + " "); + output.print(svcb.isMulticast() ? "0" : "1"); + output.print(")"); + + if (smvAddress != null) { + output.println("{"); + + output.print("PA("); + output.print(smvAddress.getVlanPriority() + " "); + output.print(smvAddress.getVlanId() + " "); + output.print(smvAddress.getAppId() + " "); + + for (int i = 0; i < 6; i++) + output.printf("%02x", smvAddress.getMacAddress()[i]); + + output.println(");"); + + output.println("}"); + } + else { + output.println(";"); + } + } for (GSEControl gcb : logicalNode.getGSEControlBlocks()) { LogicalDevice ld = logicalNode.getParentLogicalDevice(); @@ -175,6 +238,18 @@ public class DynamicModelGenerator { if (connectedAP != null) { gse = connectedAP.lookupGSE(ld.getInst(), gcb.getName()); + + if (gse == null) { + for (ConnectedAP ap : connectedAPs) { + gse = ap.lookupGSE(ld.getInst(), gcb.getName()); + + if (gse != null) + break; + } + } + + if (gse == null) + System.out.println("ConnectedAP not found for GSE"); if (gse != null) gseAddress = gse.getAddress(); @@ -372,11 +447,7 @@ public class DynamicModelGenerator { output.println("}"); } - private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) { - - if (dataObject.isTransient()) - isTransient = true; - + private void exportDataObjectChild(PrintStream output, DataObject dataObject, boolean isTransient) { for (DataObject subDataObject : dataObject.getSubDataObjects()) { output.print("DO(" + subDataObject.getName() + " " + subDataObject.getCount() + "){\n"); @@ -390,6 +461,105 @@ public class DynamicModelGenerator { } } + private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) { + + if (dataObject.isTransient()) + isTransient = true; + + if (dataObject.getCount() > 0) { + /* data object is an array */ + for (int i = 0; i < dataObject.getCount(); i++) { + output.print("[" + i + "]{\n"); + + exportDataObjectChild(output, dataObject, isTransient); + + output.print("}\n"); + } + } + else { + exportDataObjectChild(output, dataObject, isTransient); + } + + } + + private void printDataAttributeValue(PrintStream output, DataAttribute dataAttribute, boolean isTransient) + { + if (dataAttribute.isBasicAttribute()) { + DataModelValue value = dataAttribute.getValue(); + + /* if no value is given use default value for type if present */ + if (value == null) { + value = dataAttribute.getDefinition().getValue(); + + if (value != null) + if (value.getValue() == null) + value.updateEnumOrdValue(ied.getTypeDeclarations()); + } + + if (value != null) { + + switch (dataAttribute.getType()) { + case ENUMERATED: + case INT8: + case INT16: + case INT32: + case INT64: + output.print("=" + value.getIntValue()); + break; + case INT8U: + case INT16U: + case INT24U: + case INT32U: + output.print("=" + value.getLongValue()); + break; + case BOOLEAN: + { + Boolean boolVal = (Boolean) value.getValue(); + + if (boolVal.booleanValue()) + output.print("=1"); + } + break; + case UNICODE_STRING_255: + output.print("=\"" + value.getValue()+ "\""); + break; + case CURRENCY: + case VISIBLE_STRING_32: + case VISIBLE_STRING_64: + case VISIBLE_STRING_129: + case VISIBLE_STRING_255: + case VISIBLE_STRING_65: + output.print("=\"" + value.getValue()+ "\""); + break; + case FLOAT32: + case FLOAT64: + output.print("=" + value.getValue()); + break; + case TIMESTAMP: + case ENTRY_TIME: + output.print("=" + value.getLongValue()); + break; + + default: + System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType()); + break; + } + + } + + output.println(";"); + } + else { + output.println("{"); + + for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) { + exportDataAttribute(output, subDataAttribute, isTransient); + } + + output.println("}"); + } + } + private void exportDataAttribute(PrintStream output, DataAttribute dataAttribute, boolean isTransient) { output.print("DA(" + dataAttribute.getName() + " "); @@ -418,78 +588,22 @@ public class DynamicModelGenerator { else output.print("0"); - output.print(")"); - - if (dataAttribute.isBasicAttribute()) { - DataModelValue value = dataAttribute.getValue(); - - /* if no value is given use default value for type if present */ - if (value == null) { - value = dataAttribute.getDefinition().getValue(); - - if (value != null) - if (value.getValue() == null) - value.updateEnumOrdValue(ied.getTypeDeclarations()); - } - - if (value != null) { - - switch (dataAttribute.getType()) { - case ENUMERATED: - case INT8: - case INT16: - case INT32: - case INT64: - output.print("=" + value.getIntValue()); - break; - case INT8U: - case INT16U: - case INT24U: - case INT32U: - output.print("=" + value.getLongValue()); - break; - case BOOLEAN: - { - Boolean boolVal = (Boolean) value.getValue(); - - if (boolVal.booleanValue()) - output.print("=1"); - } - break; - case UNICODE_STRING_255: - output.print("=\"" + value.getValue()+ "\""); - break; - case CURRENCY: - case VISIBLE_STRING_32: - case VISIBLE_STRING_64: - case VISIBLE_STRING_129: - case VISIBLE_STRING_255: - case VISIBLE_STRING_65: - output.print("=\"" + value.getValue()+ "\""); - break; - case FLOAT32: - case FLOAT64: - output.print("=" + value.getValue()); - break; - default: - System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType()); - break; - } - - } - - output.println(";"); - } - else { - output.println("{"); + output.print(")"); - for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) { - exportDataAttribute(output, subDataAttribute, isTransient); + if (dataAttribute.getCount() > 0) { + output.print("{\n"); + + for (int i = 0; i < dataAttribute.getCount(); i++) { + output.print("[" + i + "]"); + + printDataAttributeValue(output, dataAttribute, isTransient); } - output.println("}"); + output.print("}\n"); + } + else { + printDataAttributeValue(output, dataAttribute, isTransient); } - } public static void main(String[] args) throws FileNotFoundException { diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java index 4381aff0..a06313a0 100644 --- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java @@ -1247,8 +1247,10 @@ public class StaticModelGenerator { svString += "NULL, "; svString += svCB.getSmvOpts().getIntValue() + ", "; + + svString += svCB.getSmpMod().getValue() + ", "; - svString += "0, " + svCB.getSmpRate() + ", "; + svString += svCB.getSmpRate() + ", "; svString += svCB.getConfRev() + ", ";