diff --git a/config/stack_config.h b/config/stack_config.h index d95c632f..003dae30 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -179,6 +179,9 @@ /* Maximum number of the members in a data set (named variable list) */ #define CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 100 +/* maximum number of contemporary file upload tasks (obtainFile) per server instance */ +#define CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS 5 + /* Definition of supported services */ #define MMS_DEFAULT_PROFILE 1 @@ -194,6 +197,7 @@ #define MMS_STATUS_SERVICE 1 #define MMS_IDENTIFY_SERVICE 1 #define MMS_FILE_SERVICE 1 +#define MMS_OBTAIN_FILE_SERVICE 1 #endif /* MMS_DEFAULT_PROFILE */ #if (MMS_WRITE_SERVICE != 1) @@ -216,7 +220,7 @@ /* use short FC defines as in old API */ #define CONFIG_PROVIDE_OLD_FC_DEFINES 0 -/* Support user acccess to raw messages */ +/* Support user access to raw messages */ #define CONFIG_MMS_RAW_MESSAGE_LOGGING 1 #endif /* STACK_CONFIG_H_ */ diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index ec478b0f..c9ea73d7 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -186,6 +186,7 @@ #define MMS_STATUS_SERVICE 1 #define MMS_IDENTIFY_SERVICE 1 #define MMS_FILE_SERVICE 1 +#define MMS_OBTAIN_FILE_SERVICE 1 #endif /* MMS_DEFAULT_PROFILE */ #if (MMS_WRITE_SERVICE != 1) diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 2af52e80..74af4693 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -35,6 +35,7 @@ DataAttribute* toDataAttribute(ModelNode * MN) %include "iec61850_server.h" %include "iec61850_dynamic_model.h" %include "iec61850_cdc.h" +%include "linked_list.h ModelNode* toModelNode(LogicalNode *); ModelNode* toModelNode(DataObject *); DataAttribute* toDataAttribute(DataObject *); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 41a0b473..7f32dd3e 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3021,6 +3021,9 @@ processPeriodicTasks(MmsMapping* self) #if (CONFIG_IEC61850_LOG_SERVICE == 1) Logging_processIntegrityLogs(self, currentTimeInMs); #endif + + /* handle low priority MMS backgound tasks (like file upload...) */ + MmsServer_handleBackgroundTasks(self->mmsServer); } void diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 1a0c0176..972f0fe5 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -661,9 +661,13 @@ typedef void typedef void (*MmsFileReadHandler) (void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t bytesReceived); + /** * \brief open a file for read * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * * \return the FRSM ID (file read state machine) handle of the opened file */ int32_t diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index da2035bb..68f8904f 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -124,6 +124,17 @@ typedef MmsError (*MmsNamedVariableListChangedHandler)(void* parameter, bool cre void MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter); + +typedef bool (*MmsObtainFileHandler)(void* parameter, MmsServerConnection connection, const char* sourceFilename, const char* destinationFilename); + +void +MmsServer_installObtainFileHandler(MmsServer self, MmsObtainFileHandler handler, void* parameter); + +typedef void (*MmsGetFileCompleteHandler)(void* parameter, MmsServerConnection connection, const char* destinationFilename); + +void +MmsServer_installGetFileCompleteHandler(MmsServer self, MmsGetFileCompleteHandler handler, void* parameter); + /** * \brief lock the cached server data model * @@ -209,6 +220,14 @@ MmsServer_waitReady(MmsServer self, unsigned int timeoutMs); void MmsServer_handleIncomingMessages(MmsServer self); +/** + * \brief Handle MmsServer background task + * + * \param self the MmsServer instance to operate on + */ +void +MmsServer_handleBackgroundTasks(MmsServer self); + /** * \brief Stop the server (for non-threaded operation mode) * diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 0962cd3f..147825b7 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -32,6 +32,8 @@ #include "hal_thread.h" +#include "mms_common_internal.h" + #ifndef CONFIG_MMS_RAW_MESSAGE_LOGGING #define CONFIG_MMS_RAW_MESSAGE_LOGGING 0 #endif @@ -96,6 +98,11 @@ struct sMmsConnection { /* state of an active connection conclude/release process */ int concludeState; + +#if (MMS_OBTAIN_FILE_SERVICE == 1) + int32_t nextFrsmId; + MmsFileReadStateMachine frsms[CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION]; +#endif }; @@ -231,15 +238,9 @@ mmsClient_parseStatusResponse(MmsConnection self, int* vmdLogicalStatus, int* vm void mmsClient_createFileOpenRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName, uint32_t initialPosition); -bool -mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* fileSize, uint64_t* lastModified); - void mmsClient_createFileReadRequest(uint32_t invokeId, ByteBuffer* request, int32_t frsmId); -bool -mmsClient_parseFileReadResponse(MmsConnection self, int32_t frsmId, bool* moreFollows, MmsFileReadHandler handler, void* handlerParameter); - void mmsClient_createFileCloseRequest(uint32_t invokeId, ByteBuffer* request, int32_t frsmId); @@ -280,4 +281,25 @@ mmsClient_createReadJournalRequestStartAfter(uint32_t invokeId, ByteBuffer* requ bool mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, LinkedList* result); +void +mmsClient_handleFileOpenRequest(MmsConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, ByteBuffer* response); + +void +mmsClient_handleFileReadRequest( + MmsConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response); + +void +mmsClient_handleFileReadRequest( + MmsConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response); + + + #endif /* MMS_MSG_INTERNAL_H_ */ diff --git a/src/mms/inc_private/mms_common_internal.h b/src/mms/inc_private/mms_common_internal.h index f895e601..f30eede1 100644 --- a/src/mms/inc_private/mms_common_internal.h +++ b/src/mms/inc_private/mms_common_internal.h @@ -27,6 +27,50 @@ #include "mms_value.h" #include "MmsPdu.h" #include "conversions.h" +#include "byte_buffer.h" + +#if (MMS_FILE_SERVICE == 1) + +#ifndef CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION +#define CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION 5 +#endif + +#include "hal_filesystem.h" + +typedef struct { + int32_t frsmId; + uint32_t readPosition; + uint32_t fileSize; + FileHandle fileHandle; +} MmsFileReadStateMachine; + +//TODO already defined in public API mms_connection.h +typedef void +(*MmsFileReadHandler) (void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t bytesReceived); + +bool +mmsMsg_parseFileOpenResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t* frsmId, uint32_t* fileSize, uint64_t* lastModified); + +bool +mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t frsmId, bool* moreFollows, MmsFileReadHandler handler, void* handlerParameter); + +void +mmsMsg_createFileReadResponse(int maxPduSize, uint32_t invokeId, ByteBuffer* response, MmsFileReadStateMachine* frsm); + +void +mmsMsg_createFileCloseResponse(uint32_t invokeId, ByteBuffer* response); + +#endif /* (MMS_FILE_SERVICE == 1) */ + +typedef struct sMmsServiceError +{ + int errorClass; + int errorCode; +} MmsServiceError; + +int +mmsMsg_parseConfirmedErrorPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invokeId, MmsServiceError* serviceError); + MmsValue* mmsMsg_parseDataElement(Data_t* dataElement); diff --git a/src/mms/inc_private/mms_server_connection.h b/src/mms/inc_private/mms_server_connection.h index 8102bed6..01901ec7 100644 --- a/src/mms/inc_private/mms_server_connection.h +++ b/src/mms/inc_private/mms_server_connection.h @@ -98,6 +98,9 @@ MmsServerConnection_sendWriteResponse(MmsServerConnection self, uint32_t invokeI uint32_t MmsServerConnection_getLastInvokeId(MmsServerConnection self); +uint32_t +MmsServerConnection_getNextRequestInvokeId(MmsServerConnection self); + #endif /* MMS_SERVER_CONNECTION_H_ */ diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 0ddbbbd9..c4add8e5 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -26,7 +26,7 @@ #include "libiec61850_platform_includes.h" -#include +#include "MmsPdu.h" #include "mms_common.h" #include "mms_indication.h" #include "mms_server_connection.h" @@ -44,6 +44,11 @@ #include "ber_encoder.h" #include "ber_decode.h" +#if (MMS_OBTAIN_FILE_SERVICE == 1) +#include "hal_filesystem.h" +#endif + + #ifndef DEBUG_MMS_SERVER #define DEBUG_MMS_SERVER 0 #endif @@ -60,6 +65,42 @@ #define MMS_FILE_SERVICE 1 #endif +#ifndef CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS +#define CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS 5 +#endif + + +#if (MMS_OBTAIN_FILE_SERVICE == 1) + +#define MMS_FILE_UPLOAD_STATE_NOT_USED 0 +#define MMS_FILE_UPLOAD_STATE_READY 1 +#define MMS_FILE_UPLOAD_STATE_FILE_OPEN_SENT 2 + +#define MMS_FILE_UPLOAD_STATE_SEND_FILE_READ 3 +#define MMS_FILE_UPLOAD_STATE_FILE_READ_SENT 4 + +#define MMS_FILE_UPLOAD_STATE_SEND_FILE_CLOSE 5 +#define MMS_FILE_UPLOAD_STATE_FILE_CLOSE_SENT 6 + +#define MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE 8 +#define MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_DESTINATION 9 +#define MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE 10 + +typedef struct sMmsObtainFileTask* MmsObtainFileTask; + +struct sMmsObtainFileTask { + MmsServerConnection connection; + uint32_t lastRequestInvokeId; + uint32_t obtainFileRequestInvokeId; + FileHandle fileHandle; + char destinationFilename[256]; + uint64_t nextTimeout; + int32_t frmsId; + int state; +}; + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + struct sMmsServer { IsoServer isoServer; MmsDevice* device; @@ -83,7 +124,10 @@ struct sMmsServer { Map valueCaches; bool isLocked; - ByteBuffer* reportBuffer; /* global buffer for encoding reports */ + ByteBuffer* transmitBuffer; /* global buffer for encoding reports, delayed responses... */ +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore transmitBufferMutex; +#endif #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore modelMutex; @@ -101,24 +145,18 @@ struct sMmsServer { char* modelName; char* revision; #endif /* MMS_IDENTIFY_SERVICE == 1 */ -}; -#if (MMS_FILE_SERVICE == 1) +#if (MMS_OBTAIN_FILE_SERVICE == 1) + MmsObtainFileHandler obtainFileHandler; + void* obtainFileHandlerParameter; -#ifndef CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION -#define CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION 5 -#endif + MmsGetFileCompleteHandler getFileCompleteHandler; + void* getFileCompleteHandlerParameter; -#include "hal_filesystem.h" - -typedef struct { - int32_t frsmId; - uint32_t readPosition; - uint32_t fileSize; - FileHandle fileHandle; -} MmsFileReadStateMachine; + struct sMmsObtainFileTask fileUploadTasks[CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS]; +#endif -#endif /* (MMS_FILE_SERVICE == 1) */ +}; struct sMmsServerConnection { int maxServOutstandingCalling; @@ -129,6 +167,10 @@ struct sMmsServerConnection { MmsServer server; uint32_t lastInvokeId; +#if (MMS_OBTAIN_FILE_SERVICE == 1) + uint32_t lastRequestInvokeId; /* only used by obtainFile service */ +#endif + #if (MMS_DYNAMIC_DATA_SETS == 1) LinkedList /**/namedVariableLists; /* aa-specific named variable lists */ #endif @@ -137,13 +179,25 @@ struct sMmsServerConnection { int32_t nextFrsmId; MmsFileReadStateMachine frsms[CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION]; #endif - }; +#if (MMS_OBTAIN_FILE_SERVICE == 1) +MmsObtainFileTask +MmsServer_getObtainFileTask(MmsServer self); +#endif + +ByteBuffer* +MmsServer_reserveTransmitBuffer(MmsServer self); + +void +MmsServer_releaseTransmitBuffer(MmsServer self); + /* write_out function required for ASN.1 encoding */ int mmsServer_write_out(const void *buffer, size_t size, void *app_key); + + void mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, uint8_t* buffer, int bufPos, int maxBufPos, @@ -275,6 +329,13 @@ mmsServer_handleFileCloseRequest( uint32_t invokeId, ByteBuffer* response); +void +mmsServer_handleObtainFileRequest( + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response); + int mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess); diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index ba570e55..70ea8841 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -327,7 +327,7 @@ sendRequestAndWaitForResponse(MmsConnection self, uint32_t invokeId, ByteBuffer* if (!success) { if (DEBUG_MMS_CLIENT) - printf("TIMEOUT for request %u: \n", invokeId); + printf("MMS_CLIENT: TIMEOUT for request %u: \n", invokeId); self->lastResponseError = MMS_ERROR_SERVICE_TIMEOUT; } @@ -372,12 +372,6 @@ waitUntilLastResponseHasBeenProcessed(MmsConnection self) } } -typedef struct sMmsServiceError -{ - int errorClass; - int errorCode; -} MmsServiceError; - static MmsError convertServiceErrorToMmsError(MmsServiceError serviceError) { @@ -519,12 +513,9 @@ parseServiceError(uint8_t* buffer, int bufPos, int maxLength, MmsServiceError* e return bufPos; } -static int -parseConfirmedErrorPDU(ByteBuffer* message, uint32_t* invokeId, MmsServiceError* serviceError) +int +mmsMsg_parseConfirmedErrorPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invokeId, MmsServiceError* serviceError) { - uint8_t* buffer = message->buffer; - int maxBufPos = message->size; - int bufPos = 0; int length; uint8_t tag = buffer[bufPos++]; @@ -675,7 +666,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) uint32_t invokeId; MmsServiceError serviceError = {0, 0}; - if (parseConfirmedErrorPDU(payload, &invokeId, &serviceError) < 0) { + if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &serviceError) < 0) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Error parsing confirmedErrorPDU!\n"); } @@ -742,6 +733,124 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) else goto exit_with_error; } +#if (MMS_OBTAIN_FILE_SERVICE == 1) + else if (tag == 0xa0) { + + printf("MMS_CLIENT: received confirmed request PDU (size=%i)\n", payload->size); + + // TODO handle confirmed request PDU only when obtainFile service is enabled + // TODO extract function + + int bufPos = 1; + int length; + bufPos = BerDecoder_decodeLength(buf, &length, bufPos, payload->size); + + uint32_t invokeId; + + while (bufPos < payload->size) { + + uint8_t tag = buf[bufPos++]; + + + bool extendedTag = false; + + if ((tag & 0x1f) == 0x1f) { + extendedTag = true; + tag = buf[bufPos++]; + } + + bufPos = BerDecoder_decodeLength(buf, &length, bufPos, payload->size); + + // printf("tag:%02x len:%i\n", tag, length); + +// if (bufPos < 0) { +// mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); +// return; +// } + + if (extendedTag) { + switch(tag) { + +#if (MMS_FILE_SERVICE == 1) + case 0x48: /* file-open-request */ + { + printf("MMS_CLIENT: received file-open-request\n"); + + ByteBuffer* response = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + mmsClient_handleFileOpenRequest(self, buf, bufPos, bufPos + length, invokeId, response); + + IsoClientConnection_sendMessage(self->isoClient, response); + + IsoClientConnection_releaseReceiveBuffer(self->isoClient); + } + break; + + case 0x49: /* file-read-request */ + { + printf("MMS_CLIENT: received file-read-request\n"); + + ByteBuffer* response = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + mmsClient_handleFileReadRequest(self, buf, bufPos, bufPos + length, invokeId, response); + + IsoClientConnection_sendMessage(self->isoClient, response); + + IsoClientConnection_releaseReceiveBuffer(self->isoClient); + } + break; + + case 0x4a: /* file-close-request */ + { + printf("MMS_CLIENT: received file-close-request\n"); + + ByteBuffer* response = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + mmsClient_handleFileCloseRequest(self, buf, bufPos, bufPos + length, invokeId, response); + + IsoClientConnection_sendMessage(self->isoClient, response); + + IsoClientConnection_releaseReceiveBuffer(self->isoClient); + } + break; +#endif /* MMS_FILE_SERVICE == 1 */ + + default: + // mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + + printf("MMS_CLIENT: unexpected message from server!\n"); + IsoClientConnection_releaseReceiveBuffer(self->isoClient); + + break; + } + } + else { + switch(tag) { + case 0x02: /* invoke Id */ + invokeId = BerDecoder_decodeUint32(buf, length, bufPos); + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: received request with invokeId: %i\n", invokeId); + self->lastInvokeId = invokeId; + break; + + + default: + // mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + printf("MMS_CLIENT: unexpected message from server!\n"); + IsoClientConnection_releaseReceiveBuffer(self->isoClient); + + goto exit_with_error; + + break; + } + } + + bufPos += length; + } + + + } +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: LEAVE mmsIsoCallback - OK\n"); @@ -752,6 +861,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("received malformed message from server!\n"); + IsoClientConnection_releaseReceiveBuffer(self->isoClient); @@ -1811,7 +1921,12 @@ MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filen if (self->lastResponseError != MMS_ERROR_NONE) *mmsError = self->lastResponseError; else if (responseMessage != NULL) { - if (mmsClient_parseFileOpenResponse(self, &frsmId, fileSize, lastModified) == false) + + uint8_t* buffer = self->lastResponse->buffer; + int maxBufPos = self->lastResponse->size; + int bufPos = self->lastResponseBufPos; + + if (mmsMsg_parseFileOpenResponse(buffer, bufPos, maxBufPos, &frsmId, fileSize, lastModified) == false) *mmsError = MMS_ERROR_PARSING_RESPONSE; } @@ -1890,7 +2005,11 @@ MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, M if (self->lastResponseError != MMS_ERROR_NONE) *mmsError = self->lastResponseError; else if (responseMessage != NULL) { - if (mmsClient_parseFileReadResponse(self, frsmId, &moreFollows, handler, handlerParameter) == false) + uint8_t* buffer = self->lastResponse->buffer; + int maxBufPos = self->lastResponse->size; + int bufPos = self->lastResponseBufPos; + + if (mmsMsg_parseFileReadResponse(buffer, bufPos, maxBufPos, frsmId, &moreFollows, handler, handlerParameter) == false) *mmsError = MMS_ERROR_PARSING_RESPONSE; } @@ -1968,14 +2087,17 @@ MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sou uint32_t invokeId = getNextInvokeId(self); - mmsClient_createObtainFileRequest(invokeId, payload, sourceFile, destinationFile); + //TODO enable file service - sendRequestAndWaitForResponse(self, invokeId, payload); + mmsClient_createObtainFileRequest(invokeId, payload, sourceFile, destinationFile); + ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload); if (self->lastResponseError != MMS_ERROR_NONE) *mmsError = self->lastResponseError; + /* nothing to do - response contains no data to evaluate */ + releaseResponse(self); if (self->associationState == MMS_STATE_CLOSED) diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c index 81b18bb2..d1c1a6c3 100644 --- a/src/mms/iso_mms/client/mms_client_files.c +++ b/src/mms/iso_mms/client/mms_client_files.c @@ -1,7 +1,7 @@ /* * mms_client_files.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013 - 2016 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,6 +32,222 @@ #include "ber_decode.h" #include "conversions.h" +#if (MMS_OBTAIN_FILE_SERVICE == 1) + +static MmsFileReadStateMachine* +getFreeFrsm(MmsConnection connection) +{ + int i; + + MmsFileReadStateMachine* freeFrsm = NULL; + + for (i = 0; i < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; i++) { + if (connection->frsms[i].fileHandle == NULL) { + freeFrsm = &(connection->frsms[i]); + break; + } + } + + return freeFrsm; +} + +static MmsFileReadStateMachine* +getFrsm(MmsConnection connection, int32_t frsmId) +{ + int i; + + MmsFileReadStateMachine* frsm = NULL; + + for (i = 0; i < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; i++) { + if (connection->frsms[i].fileHandle != NULL) { + if (connection->frsms[i].frsmId == frsmId) { + frsm = &(connection->frsms[i]); + break; + } + } + } + + return frsm; +} + + +static int32_t +getNextFrsmId(MmsConnection connection) +{ + uint32_t nextFrsmId = connection->nextFrsmId; + connection->nextFrsmId++; + + return nextFrsmId; +} + +//TODO remove redundancy (with server implementation) +static void +createExtendedFilename(char* extendedFileName, char* fileName) +{ + strcpy(extendedFileName, CONFIG_VIRTUAL_FILESTORE_BASEPATH); + strncat(extendedFileName, fileName, sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256); +} + +//TODO remove redundancy (with server implementation) +static FileHandle +openFile(char* fileName, bool readWrite) +{ + char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256]; + + createExtendedFilename(extendedFileName, fileName); + + printf("open file %s\n", extendedFileName); + + return FileSystem_openFile(extendedFileName, readWrite); +} + +//TODO remove redundancy (with server implementation) +static bool +parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response) +{ + uint8_t tag = buffer[(*bufPos)++]; + int length; + + if (tag != 0x19) { + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return false; + } + + *bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos); + + if (*bufPos < 0) { + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return false; + } + + if (length > 255) { + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + return false; + } + + memcpy(filename, buffer + *bufPos, length); + filename[length] = 0; + *bufPos += length; + + return true; +} + + +void +mmsClient_handleFileOpenRequest( + MmsConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, ByteBuffer* response) +{ + char filename[256]; + bool hasFileName = false; + uint32_t filePosition = 0; + + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int length; + + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) goto exit_reject_invalid_pdu; + + switch(tag) { + case 0xa0: /* filename */ + + if (!parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response)) + return; + + hasFileName = true; + + break; + + case 0x81: /* initial position */ + filePosition = BerDecoder_decodeUint32(buffer, length, bufPos); + bufPos += length; + break; + + default: /* unrecognized parameter */ + bufPos += length; + goto exit_reject_invalid_pdu; + } + } + + if (hasFileName) { + + MmsFileReadStateMachine* frsm = getFreeFrsm(connection); + + if (frsm != NULL) { + FileHandle fileHandle = openFile(filename, false); + + if (fileHandle != NULL) { + frsm->fileHandle = fileHandle; + frsm->readPosition = filePosition; + frsm->frsmId = getNextFrsmId(connection); + + mmsServer_createFileOpenResponse(invokeId, response, filename, frsm); + } + else + mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT); + + } + else + mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + else + goto exit_invalid_parameter; + + return; + +exit_invalid_parameter: + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + return; + +exit_reject_invalid_pdu: + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); +} + + +void +mmsClient_handleFileReadRequest( + MmsConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) +{ + int32_t frsmId = (int32_t) BerDecoder_decodeUint32(buffer, maxBufPos - bufPos, bufPos); + + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", frsmId); + + MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId); + + if (frsm != NULL) + mmsMsg_createFileReadResponse(connection, invokeId, response, frsm); + else + mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER); +} + +void +mmsClient_handleFileCloseRequest( + MmsConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) +{ + int32_t frsmId = (int32_t) BerDecoder_decodeUint32(buffer, maxBufPos - bufPos, bufPos); + + MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId); + + FileSystem_closeFile(frsm->fileHandle); + frsm->fileHandle = NULL; + frsm->frsmId = 0; + + mmsMsg_createFileCloseResponse(invokeId, response); +} + + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + void mmsClient_createFileOpenRequest(uint32_t invokeId, ByteBuffer* request, const char* fileName, uint32_t initialPosition) { @@ -446,26 +662,23 @@ mmsClient_parseFileDirectoryResponse(MmsConnection self, MmsFileDirectoryHandler } bool -mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* fileSize, uint64_t* lastModified) +mmsMsg_parseFileOpenResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t* frsmId, uint32_t* fileSize, uint64_t* lastModified) { - uint8_t* buffer = self->lastResponse->buffer; - int maxBufPos = self->lastResponse->size; - int bufPos = self->lastResponseBufPos; int length; uint8_t tag = buffer[bufPos++]; if (tag != 0xbf) { - if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileOpenResponse: unknown tag %02x\n", tag); + //if (DEBUG_MMS_CLIENT) + printf("MMS: mmsClient_parseFileOpenResponse: unknown tag %02x\n", tag); return false; } tag = buffer[bufPos++]; if (tag != 0x48) { - if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileOpenResponse: unknown tag %02x\n", tag); + //if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: mmsClient_parseFileOpenResponse: unknown tag %02x\n", tag); return false; } @@ -477,7 +690,7 @@ mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* f if (endPos > maxBufPos) { if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileOpenResponse: message to short (length:%i maxBufPos:%i)!\n", length, maxBufPos); + printf("MMS_CLIENT/SERVER: mmsClient_parseFileOpenResponse: message to short (length:%i maxBufPos:%i)!\n", length, maxBufPos); return false; } @@ -499,7 +712,7 @@ mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* f default: bufPos += length; if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileOpenResponse: message contains unknown tag %02x!\n", tag); + printf("MMS_CLIENT/SERVER: mmsClient_parseFileOpenResponse: message contains unknown tag %02x!\n", tag); break; } } @@ -509,18 +722,15 @@ mmsClient_parseFileOpenResponse(MmsConnection self, int32_t* frsmId, uint32_t* f bool -mmsClient_parseFileReadResponse(MmsConnection self, int32_t frsmId, bool* moreFollows, MmsFileReadHandler handler, void* handlerParameter) +mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, int frsmId, bool* moreFollows, MmsFileReadHandler handler, void* handlerParameter) { - uint8_t* buffer = self->lastResponse->buffer; - int maxBufPos = self->lastResponse->size; - int bufPos = self->lastResponseBufPos; int length; uint8_t tag = buffer[bufPos++]; if (tag != 0xbf) { if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileReadResponse: unknown tag %02x\n", tag); + printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: unknown tag %02x\n", tag); return false; } @@ -530,7 +740,7 @@ mmsClient_parseFileReadResponse(MmsConnection self, int32_t frsmId, bool* moreF if (tag != 0x49) { if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileReadResponse: unknown tag %02x\n", tag); + printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: unknown tag %02x\n", tag); return false; } @@ -542,7 +752,7 @@ mmsClient_parseFileReadResponse(MmsConnection self, int32_t frsmId, bool* moreF if (endPos > maxBufPos) { if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileReadResponse: message to short (length:%i maxBufPos:%i)!\n", length, maxBufPos); + printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: message to short (length:%i maxBufPos:%i)!\n", length, maxBufPos); return false; } @@ -563,7 +773,7 @@ mmsClient_parseFileReadResponse(MmsConnection self, int32_t frsmId, bool* moreF default: bufPos += length; if (DEBUG_MMS_CLIENT) - printf("mmsClient_parseFileReadResponse: message contains unknown tag %02x!\n", tag); + printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: message contains unknown tag %02x!\n", tag); return false; } diff --git a/src/mms/iso_mms/server/mms_file_service.c b/src/mms/iso_mms/server/mms_file_service.c index 1f25ba73..77ff3033 100644 --- a/src/mms/iso_mms/server/mms_file_service.c +++ b/src/mms/iso_mms/server/mms_file_service.c @@ -110,8 +110,6 @@ encodeFileAttributes(uint8_t tag, uint32_t fileSize, char* gtString, uint8_t* bu + 2 + gtStringSize; if (buffer == NULL) { - - return fileAttributesSize; } else { @@ -180,8 +178,8 @@ deleteFile(char* fileName) { return FileSystem_deleteFile(extendedFileName); } -static void -createFileOpenResponse(uint32_t invokeId, ByteBuffer* response, char* fullPath, MmsFileReadStateMachine* frsm) +void +mmsServer_createFileOpenResponse(uint32_t invokeId, ByteBuffer* response, char* fullPath, MmsFileReadStateMachine* frsm) { uint64_t msTime; @@ -347,7 +345,7 @@ mmsServer_handleFileOpenRequest( frsm->readPosition = filePosition; frsm->frsmId = getNextFrsmId(connection); - createFileOpenResponse(invokeId, response, filename, frsm); + mmsServer_createFileOpenResponse(invokeId, response, filename, frsm); } else mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT); @@ -370,9 +368,240 @@ exit_reject_invalid_pdu: mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); } +#if (MMS_OBTAIN_FILE_SERVICE == 1) static void -createFileReadResponse(MmsServerConnection connection, uint32_t invokeId, +createObtainFileResponse(uint32_t invokeId, ByteBuffer* response) +{ + createNullResponseExtendedTag(invokeId, response, 0x2e); +} + + +//#define MMS_FILE_UPLOAD_STATE_COMPLETE 11 + +void +mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task) +{ + //send response message + + /* + * states: + * 0 - no state / not used + * 1 - + * 2 - file open sent + * 3 - file read sent + * 4 - file close sent + * + */ + printf("mmsServer_fileUploadTask (%p) state=%i\n", task, task->state); + + switch (task->state) { + case MMS_FILE_UPLOAD_STATE_NOT_USED: /* state: not-used */ + break; + + case MMS_FILE_UPLOAD_STATE_FILE_OPEN_SENT: + { + if (Hal_getTimeInMs() > task->nextTimeout) { + printf("MMS_SERVER: file open timeout!\n"); + task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE; + } + } + break; + + case MMS_FILE_UPLOAD_STATE_SEND_FILE_READ: + { + ByteBuffer* request = MmsServer_reserveTransmitBuffer(self); + + task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(task->connection); + + mmsClient_createFileReadRequest(task->lastRequestInvokeId, request, task->frmsId); + + IsoConnection_sendMessage(task->connection->isoConnection, request, false); + + MmsServer_releaseTransmitBuffer(self); + + task->nextTimeout = Hal_getTimeInMs() + 2000; /* timeout 2000 ms */ + + task->state = MMS_FILE_UPLOAD_STATE_FILE_READ_SENT; + } + + break; + + case MMS_FILE_UPLOAD_STATE_FILE_READ_SENT: + + if (Hal_getTimeInMs() > task->nextTimeout) { + printf("MMS_SERVER: file read timeout!\n"); + task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE; + } + + break; + + case MMS_FILE_UPLOAD_STATE_SEND_FILE_CLOSE: + { + ByteBuffer* request = MmsServer_reserveTransmitBuffer(self); + + task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(task->connection); + + mmsClient_createFileCloseRequest(task->lastRequestInvokeId, request, task->frmsId); + + IsoConnection_sendMessage(task->connection->isoConnection, request, false); + + MmsServer_releaseTransmitBuffer(self); + + task->nextTimeout = Hal_getTimeInMs() + 2000; /* timeout 2000 ms */ + + task->state = MMS_FILE_UPLOAD_STATE_FILE_CLOSE_SENT; + } + break; + + case MMS_FILE_UPLOAD_STATE_FILE_CLOSE_SENT: + + if (Hal_getTimeInMs() > task->nextTimeout) { + printf("MMS_SERVER: file close timeout!\n"); + task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE; + } + + break; + + case MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE: + + // TODO send ObtainFileError + + printf("MMS_SERVER: ObtainFile service: failed to open file from client\n"); + + task->state = MMS_FILE_UPLOAD_STATE_NOT_USED; + + break; + + + + case MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE: + { + ByteBuffer* response = MmsServer_reserveTransmitBuffer(self); + + createObtainFileResponse(task->obtainFileRequestInvokeId, response); + + IsoConnection_sendMessage(task->connection->isoConnection, response, false); + + MmsServer_releaseTransmitBuffer(self); + + if (self->getFileCompleteHandler) + self->getFileCompleteHandler(self->getFileCompleteHandlerParameter, task->connection, task->destinationFilename); + + task->state = MMS_FILE_UPLOAD_STATE_NOT_USED; + } + break; + } +} + +void +mmsServer_handleObtainFileRequest( + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) +{ + char sourceFilename[256]; + bool hasSourceFileName = false; + + char destinationFilename[256]; + bool hasDestinationFilename = false; + + while (bufPos < maxBufPos) { + uint8_t tag = buffer[bufPos++]; + int length; + + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) goto exit_reject_invalid_pdu; + + switch(tag) { + + case 0xa1: /* source filename */ + + if (!parseFileName(sourceFilename, buffer, &bufPos, bufPos + length, invokeId, response)) + return; + + hasSourceFileName = true; + + break; + + case 0xa2: /* destination filename */ + + if (!parseFileName(destinationFilename, buffer, &bufPos, bufPos + length, invokeId, response)) + return; + + hasDestinationFilename = true; + + break; + + default: /* unrecognized parameter */ + bufPos += length; + goto exit_reject_invalid_pdu; + } + } + + if (hasSourceFileName && hasDestinationFilename) { + + /* call callback to check if access is allowed */ + if (connection->server->obtainFileHandler) + if (connection->server->obtainFileHandler(connection->server->obtainFileHandlerParameter, connection, sourceFilename, destinationFilename) == false) + goto exit_access_denied; + + //TODO check if destination file already exists. If exists return error message + + printf("Download file %s from client to local file %s...\n", sourceFilename, destinationFilename); + + MmsObtainFileTask task = MmsServer_getObtainFileTask(connection->server); + + if (task != NULL) { + + /* send file open request */ + task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(connection); + task->connection = connection; + + strcpy(task->destinationFilename, destinationFilename); + + ByteBuffer* request = MmsServer_reserveTransmitBuffer(connection->server); + + mmsClient_createFileOpenRequest(task->lastRequestInvokeId, request, sourceFilename, 0); + + IsoConnection_sendMessage(task->connection->isoConnection, request, false); + + MmsServer_releaseTransmitBuffer(connection->server); + + task->nextTimeout = Hal_getTimeInMs() + 2000; /* timeout 2000 ms */ + + task->state = MMS_FILE_UPLOAD_STATE_FILE_OPEN_SENT; + } + else + goto exit_unavailable; + } + else + goto exit_invalid_parameter; + + return; + +exit_invalid_parameter: + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + return; + +exit_access_denied: + mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + return; + +exit_unavailable: + mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE); + return; + +exit_reject_invalid_pdu: + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); +} + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + +void +mmsMsg_createFileReadResponse(int maxPduSize, uint32_t invokeId, ByteBuffer* response, MmsFileReadStateMachine* frsm) { /* determine remaining bytes in file */ @@ -380,7 +609,7 @@ createFileReadResponse(MmsServerConnection connection, uint32_t invokeId, uint32_t fileChunkSize = 0; - uint32_t maxFileChunkSize = connection->maxPduSize - 20; + uint32_t maxFileChunkSize = maxPduSize - 20; uint32_t fileReadResponseSize = 1; /* for tag */ @@ -442,18 +671,17 @@ mmsServer_handleFileReadRequest( MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId); if (frsm != NULL) - createFileReadResponse(connection, invokeId, response, frsm); + mmsMsg_createFileReadResponse(connection->maxPduSize, invokeId, response, frsm); else mmsServer_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER); } -static void -createFileCloseResponse(uint32_t invokeId, ByteBuffer* response) +void +mmsMsg_createFileCloseResponse(uint32_t invokeId, ByteBuffer* response) { createNullResponseExtendedTag(invokeId, response, 0x4a); } - void mmsServer_handleFileCloseRequest( MmsServerConnection connection, @@ -469,7 +697,7 @@ mmsServer_handleFileCloseRequest( frsm->fileHandle = NULL; frsm->frsmId = 0; - createFileCloseResponse(invokeId, response); + mmsMsg_createFileCloseResponse(invokeId, response); } diff --git a/src/mms/iso_mms/server/mms_information_report.c b/src/mms/iso_mms/server/mms_information_report.c index ec014b8e..30b17d82 100644 --- a/src/mms/iso_mms/server/mms_information_report.c +++ b/src/mms/iso_mms/server/mms_information_report.c @@ -59,7 +59,7 @@ MmsServerConnection_sendInformationReportSingleVariableVMDSpecific(MmsServerConn if (DEBUG_MMS_SERVER) printf("MMS_SERVER: sendInfReportSingle variable: %s\n", itemId); - ByteBuffer* reportBuffer = self->server->reportBuffer; + ByteBuffer* reportBuffer = MmsServer_reserveTransmitBuffer(self->server); uint8_t* buffer = reportBuffer->buffer; int bufPos = 0; @@ -82,6 +82,8 @@ MmsServerConnection_sendInformationReportSingleVariableVMDSpecific(MmsServerConn IsoConnection_sendMessage(self->isoConnection, reportBuffer, handlerMode); + MmsServer_releaseTransmitBuffer(self->server); + exit_function: return; } @@ -151,7 +153,7 @@ MmsServerConnection_sendInformationReportListOfVariables( } /* encode message */ - ByteBuffer* reportBuffer = self->server->reportBuffer; + ByteBuffer* reportBuffer = MmsServer_reserveTransmitBuffer(self->server); uint8_t* buffer = reportBuffer->buffer; int bufPos = 0; @@ -212,6 +214,8 @@ MmsServerConnection_sendInformationReportListOfVariables( IsoConnection_sendMessage(self->isoConnection, reportBuffer, handlerMode); + MmsServer_releaseTransmitBuffer(self->server); + exit_function: return; } @@ -264,7 +268,7 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c if (DEBUG_MMS_SERVER) printf("MMS_SERVER: sendInfReport\n"); - ByteBuffer* reportBuffer = self->server->reportBuffer; + ByteBuffer* reportBuffer = MmsServer_reserveTransmitBuffer(self->server); uint8_t* buffer = reportBuffer->buffer; int bufPos = 0; @@ -294,6 +298,8 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c IsoConnection_sendMessage(self->isoConnection, reportBuffer, false); + MmsServer_releaseTransmitBuffer(self->server); + exit_function: return; } diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index f2ee46b7..1007e8f8 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -1,7 +1,7 @@ /* * mms_server.c * - * Copyright 2013, 2014, 2015 Michael Zillgith + * Copyright 2013 - 2016 Michael Zillgith * * This file is part of libIEC61850. * @@ -60,10 +60,11 @@ MmsServer_create(IsoServer isoServer, MmsDevice* device) self->valueCaches = createValueCaches(device); self->isLocked = false; - self->reportBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE); + self->transmitBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE); #if (CONFIG_MMS_THREADLESS_STACK != 1) self->modelMutex = Semaphore_create(1); + self->transmitBufferMutex = Semaphore_create(1); IsoServer_setUserLock(isoServer, self->modelMutex); #endif @@ -87,6 +88,45 @@ MmsServer_unlockModel(MmsServer self) #endif } +ByteBuffer* +MmsServer_reserveTransmitBuffer(MmsServer self) +{ +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->transmitBufferMutex); +#endif + + return self->transmitBuffer; +} + +void +MmsServer_releaseTransmitBuffer(MmsServer self) +{ +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->transmitBufferMutex); +#endif +} + +#if (MMS_OBTAIN_FILE_SERVICE == 1) + +MmsObtainFileTask +MmsServer_getObtainFileTask(MmsServer self) +{ + int i; + + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { + + if (self->fileUploadTasks[i].state == 0) { + self->fileUploadTasks[i].state = 1; + return &(self->fileUploadTasks[i]); + } + + } + + return NULL; +} + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + void MmsServer_installReadHandler(MmsServer self, MmsReadVariableHandler readHandler, void* parameter) { @@ -151,9 +191,10 @@ MmsServer_destroy(MmsServer self) #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_destroy(self->modelMutex); + Semaphore_destroy(self->transmitBufferMutex); #endif - ByteBuffer_destroy(self->reportBuffer); + ByteBuffer_destroy(self->transmitBuffer); GLOBAL_FREEMEM(self); } @@ -311,6 +352,22 @@ MmsServer_handleIncomingMessages(MmsServer self) IsoServer_processIncomingMessages(self->isoServer); } +void +MmsServer_handleBackgroundTasks(MmsServer self) +{ + +#if (MMS_OBTAIN_FILE_SERVICE == 1) + + int i; + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) + { + if (self->fileUploadTasks[i].state != 0) + mmsServer_fileUploadTask(self, &(self->fileUploadTasks[i])); + } + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ +} + void MmsServer_stopListeningThreadless(MmsServer self) { diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index eced91b7..92d5f189 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -111,6 +111,12 @@ handleConfirmedRequestPdu( if (extendedTag) { switch(tag) { +#if (MMS_OBTAIN_FILE_SERVICE == 1) + case 0x2e: /* obtain-file */ + mmsServer_handleObtainFileRequest(self, buffer, bufPos, bufPos + length, invokeId, response); + break; +#endif /* MMS_OBTAIN_FILE_SERVICE == 1 */ + #if (MMS_JOURNAL_SERVICE == 1) case 0x41: /* read-journal */ mmsServer_handleReadJournalRequest(self, buffer, bufPos, bufPos + length, invokeId, response); @@ -234,6 +240,199 @@ handleConfirmedRequestPdu( } } +#if (MMS_OBTAIN_FILE_SERVICE == 1) + +static void +handleConfirmedErrorPdu( + MmsServerConnection self, + uint8_t* buffer, int bufPos, int maxBufPos, + ByteBuffer* response) +{ + uint32_t invokeId; + MmsServiceError serviceError; + + + + if (mmsMsg_parseConfirmedErrorPDU(buffer, bufPos, maxBufPos, &invokeId, &serviceError)) { + + printf("MMS_SERVER: Handle confirmed error PDU: invokeID: %i\n", invokeId); + + /* check if message is related to an existing file upload task */ + int i; + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { + + if (self->server->fileUploadTasks[i].lastRequestInvokeId == invokeId) { + + self->server->fileUploadTasks[i].state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; + return; + } + } + } + else { + printf("MMS_SERVER: error parsing confirmed error PDU\n"); + } +} + + +static MmsObtainFileTask +getUploadTaskByInvokeId(MmsServer mmsServer, uint32_t invokeId) +{ + int i; + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { + if (mmsServer->fileUploadTasks[i].lastRequestInvokeId == invokeId) + return &(mmsServer->fileUploadTasks[i]); + } + + return NULL; +} + +static void +mmsFileReadHandler(void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t bytesReceived) +{ + MmsObtainFileTask task = (MmsObtainFileTask) parameter; + + printf(" FILE %i received %i bytes\n", frsmId, bytesReceived); + //TODO write data to file +} + +static void +handleConfirmedResponsePdu( + MmsServerConnection self, + uint8_t* buffer, int bufPos, int maxBufPos, + ByteBuffer* response) +{ + uint32_t invokeId = 0; + + while (bufPos < maxBufPos) { + int startBufPos = bufPos; + + uint8_t tag = buffer[bufPos++]; + int length; + + bool extendedTag = false; + + if ((tag & 0x1f) == 0x1f) { + extendedTag = true; + tag = buffer[bufPos++]; + } + + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + + if (bufPos < 0) { + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + return; + } + + if (extendedTag) { + switch(tag) { + +#if (MMS_FILE_SERVICE == 1) + case 0x48: /* file-open-response */ + printf("MMS_SERVER: received file-open-response\n"); + + { + MmsObtainFileTask fileTask = getUploadTaskByInvokeId(self->server, invokeId); + + if (fileTask != NULL) { + + int32_t frmsId; + + if (mmsMsg_parseFileOpenResponse(buffer, startBufPos, maxBufPos, &frmsId, NULL, NULL)) { + printf("MMS_SERVER: received file-open-response with frmsId=%i\n", frmsId); + + fileTask->frmsId = frmsId; + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_FILE_READ; + } + else { + printf("MMS_SERVER: error parsing file-open-response\n"); + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; + } + } + else + printf("MMS_SERVER: unexpected file-open-response\n"); + } + + break; + + case 0x49: /* file-read-response */ + printf("MMS_SERVER: received file-read-response\n"); + + { + MmsObtainFileTask fileTask = getUploadTaskByInvokeId(self->server, invokeId); + + if (fileTask != NULL) { + + bool moreFollows; + + if (mmsMsg_parseFileReadResponse(buffer, startBufPos, maxBufPos, fileTask->frmsId, &moreFollows, mmsFileReadHandler, (void*) fileTask)) { + + printf("MMS_SERVER: received file data\n"); + + if (moreFollows) { + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_FILE_READ; + } + else { + + //TODO close local file + + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_FILE_CLOSE; + } + } + else { + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; + printf("MMS_SERVER: error parsing file-read-response\n"); + } + } + else + printf("MMS_SERVER: unexpected file-read-response\n"); + } + + break; + + case 0x4a: /* file-close-response */ + { + + printf("MMS_SERVER: received file-close-response\n"); + + + MmsObtainFileTask fileTask = getUploadTaskByInvokeId(self->server, invokeId); + + if (fileTask != NULL) { + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE; + } + else + printf("MMS_SERVER: unexpected file-close-response\n"); + } + break; +#endif /* MMS_FILE_SERVICE == 1 */ + + default: + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + return; + break; + } + } + else { + switch(tag) { + case 0x02: /* invoke Id */ + invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: received request with invokeId: %i\n", invokeId); + self->lastInvokeId = invokeId; + break; + + default: + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); + return; + break; + } + } + + bufPos += length; + } +} /* handleConfirmedResponsePdu */ +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ + MmsIndication MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response) { @@ -266,6 +465,17 @@ MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, handleConfirmedRequestPdu(self, buffer, bufPos, bufPos + pduLength, response); retVal = MMS_CONFIRMED_REQUEST; break; +#if (MMS_OBTAIN_FILE_SERVICE == 1) + case 0xa1: /* Confirmed response PDU */ + handleConfirmedResponsePdu(self, buffer, bufPos, bufPos + pduLength, response); + retVal = MMS_CONFIRMED_REQUEST; + break; + case 0xa2: /* Confirmed error PDU */ + handleConfirmedErrorPdu(self, buffer, 0, bufPos + pduLength, response); + retVal = MMS_CONFIRMED_REQUEST; + break; + +#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ case 0x8b: /* Conclude request PDU */ mmsServer_writeConcludeResponsePdu(response); retVal = MMS_CONCLUDE; @@ -317,6 +527,10 @@ MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoCo self->namedVariableLists = LinkedList_create(); #endif +#if (MMS_OBTAIN_FILE_SERVICE == 1) + self->lastRequestInvokeId = 0; +#endif + IsoConnection_installListener(isoCon, messageReceived, (void*) self); return self; @@ -391,3 +605,10 @@ MmsServerConnection_getLastInvokeId(MmsServerConnection self) { return self->lastInvokeId; } + +uint32_t +MmsServerConnection_getNextRequestInvokeId(MmsServerConnection self) +{ + self->lastRequestInvokeId++; + return self->lastRequestInvokeId; +} diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c index 291cf14f..614aef9d 100644 --- a/src/mms/iso_mms/server/mms_write_service.c +++ b/src/mms/iso_mms/server/mms_write_service.c @@ -38,6 +38,7 @@ int mmsServer_createMmsWriteResponse(MmsServerConnection connection, int invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults) { + //TODO remove asn1c code MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = @@ -75,13 +76,15 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection, void MmsServerConnection_sendWriteResponse(MmsServerConnection self, uint32_t invokeId, MmsDataAccessError indication, bool handlerMode) { - ByteBuffer* response = ByteBuffer_create(NULL, self->maxPduSize); + ByteBuffer* response = MmsServer_reserveTransmitBuffer(self->server); + + ByteBuffer_setSize(response, 0); mmsServer_createMmsWriteResponse(self, invokeId, response, 1, &indication); IsoConnection_sendMessage(self->isoConnection, response, handlerMode); - ByteBuffer_destroy(response); + MmsServer_releaseTransmitBuffer(self->server); } void diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c index 5136afaf..12ca8349 100644 --- a/src/mms/iso_server/iso_connection.c +++ b/src/mms/iso_server/iso_connection.c @@ -54,10 +54,10 @@ struct sIsoConnection { uint8_t* receiveBuffer; - uint8_t* sendBuffer; - ByteBuffer rcvBuffer; + uint8_t* sendBuffer; + uint8_t* cotpReadBuf; uint8_t* cotpWriteBuf; ByteBuffer cotpReadBuffer; diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index 3f3a8939..c658a995 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -551,3 +551,6 @@ EXPORTS ModelNode_getType ControlObjectClient_setTestMode LogStorage_setMaxLogEntries + MmsServer_installObtainFileHandler + MmsServer_installGetFileCompleteHandler + MmsServer_handleBackgroundTasks diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index 760875ac..1d59bc29 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -629,3 +629,6 @@ EXPORTS ModelNode_getType ControlObjectClient_setTestMode LogStorage_setMaxLogEntries + MmsServer_installObtainFileHandler + MmsServer_installGetFileCompleteHandler + MmsServer_handleBackgroundTasks