/* * mms_server_connection.c * * Copyright 2013 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. * * * MMS client connection handling code for libiec61850. * * Handles a MMS client connection. */ #include "libiec61850_platform_includes.h" #include "mms_server_internal.h" #include "iso_server.h" #include "ber_encoder.h" #include "ber_decode.h" /********************************************************************************************** * MMS Common support functions *********************************************************************************************/ void mmsServer_writeMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response) { MmsPdu_t* mmsPdu = (MmsPdu_t*) GLOBAL_CALLOC(1, sizeof(MmsPdu_t)); mmsPdu->present = MmsPdu_PR_rejectPDU; if (invokeId != NULL) { mmsPdu->choice.rejectPDU.originalInvokeID = (Unsigned32_t*) GLOBAL_CALLOC(1, sizeof(Unsigned32_t)); asn_long2INTEGER(mmsPdu->choice.rejectPDU.originalInvokeID, *invokeId); } if (reason == MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE) { mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU; mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU = RejectPDU__rejectReason__confirmedRequestPDU_unrecognizedService; } else if(reason == MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE) { mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_pduError; asn_long2INTEGER(&mmsPdu->choice.rejectPDU.rejectReason.choice.pduError, RejectPDU__rejectReason__pduError_unknownPduType); } else if (reason == MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT) { mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU; mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU = RejectPDU__rejectReason__confirmedRequestPDU_invalidArgument; } else if (reason == MMS_ERROR_REJECT_INVALID_PDU) { mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_pduError; mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU = RejectPDU__rejectReason__pduError_invalidPdu; } else { mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU; mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU = RejectPDU__rejectReason__confirmedRequestPDU_other; } der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } /********************************************************************************************** * MMS General service handling functions *********************************************************************************************/ static void handleConfirmedRequestPdu( MmsServerConnection self, uint8_t* buffer, int bufPos, int maxBufPos, ByteBuffer* response) { uint32_t invokeId = 0; while (bufPos < maxBufPos) { 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 (DEBUG_MMS_SERVER) printf("tag %02x extended tag: %i size: %i\n", tag, extendedTag, length); if (extendedTag) { switch(tag) { #if (MMS_FILE_SERVICE == 1) case 0x48: /* file-open-request */ mmsServer_handleFileOpenRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; case 0x49: /* file-read-request */ mmsServer_handleFileReadRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; case 0x4a: /* file-close-request */ mmsServer_handleFileCloseRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; case 0x4b: /* file-rename-request */ mmsServer_handleFileRenameRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; case 0x4c: /* file-delete-request */ mmsServer_handleFileDeleteRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; case 0x4d: /* file-directory-request */ mmsServer_handleFileDirectoryRequest(self, buffer, bufPos, bufPos + length, invokeId, response); 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("invokeId: %i\n", invokeId); self->lastInvokeId = invokeId; break; #if (MMS_STATUS_SERVICE == 1) case 0x80: /* status-request */ mmsServer_handleStatusRequest(self, buffer, bufPos, invokeId, response); break; #endif /* MMS_STATUS_SERVICE == 1 */ #if (MMS_GET_NAME_LIST == 1) case 0xa1: /* get-name-list-request */ mmsServer_handleGetNameListRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #endif /* (MMS_GET_NAME_LIST == 1) */ #if (MMS_IDENTIFY_SERVICE == 1) case 0x82: /* identify */ mmsServer_handleIdentifyRequest(self, invokeId, response); break; #endif /* MMS_IDENTIFY_SERVICE == 1 */ case 0xa4: /* read-request */ mmsServer_handleReadRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #if (MMS_WRITE_SERVICE == 1) case 0xa5: /* write-request */ mmsServer_handleWriteRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #endif /* (MMS_WRITE_SERVICE == 1) */ #if (MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1) case 0xa6: /* get-variable-access-attributes-request */ mmsServer_handleGetVariableAccessAttributesRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #endif /* MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1 */ #if (MMS_DYNAMIC_DATA_SETS == 1) case 0xab: /* define-named-variable-list */ mmsServer_handleDefineNamedVariableListRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ #if (MMS_GET_DATA_SET_ATTRIBUTES == 1) case 0xac: /* get-named-variable-list-attributes-request */ mmsServer_handleGetNamedVariableListAttributesRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #endif /* (MMS_GET_DATA_SET_ATTRIBUTES == 1) */ #if (MMS_DYNAMIC_DATA_SETS == 1) case 0xad: /* delete-named-variable-list-request */ mmsServer_handleDeleteNamedVariableListRequest(self, buffer, bufPos, bufPos + length, invokeId, response); break; #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ default: mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); return; break; } } bufPos += length; } } MmsIndication MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response) { MmsIndication retVal; uint8_t* buffer = message->buffer; if (message->size < 2) return MMS_ERROR; int bufPos = 0; uint8_t pduType = buffer[bufPos++]; int pduLength; bufPos = BerDecoder_decodeLength(buffer, &pduLength, bufPos, message->size); if (bufPos < 0) return MMS_ERROR; if (DEBUG_MMS_SERVER) printf("mms_server: recvd MMS-PDU type: %02x size: %i\n", pduType, pduLength); switch (pduType) { case 0xa8: /* Initiate request PDU */ mmsServer_handleInitiateRequest(self, buffer, bufPos, bufPos + pduLength, response); retVal = MMS_INITIATE; break; case 0xa0: /* Confirmed request PDU */ handleConfirmedRequestPdu(self, buffer, bufPos, bufPos + pduLength, response); retVal = MMS_CONFIRMED_REQUEST; break; case 0x8b: /* Conclude request PDU */ mmsServer_writeConcludeResponsePdu(response); retVal = MMS_CONCLUDE; break; case 0xa4: /* Reject PDU - silently ignore */ if (DEBUG) printf("received reject PDU!\n"); retVal = MMS_OK; break; default: mmsServer_writeMmsRejectPdu(NULL, MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE, response); retVal = MMS_ERROR; break; } return retVal; } static void /* will be called by IsoConnection */ messageReceived(void* parameter, ByteBuffer* message, ByteBuffer* response) { MmsServerConnection self = (MmsServerConnection) parameter; MmsServerConnection_parseMessage(self, message, response); } /********************************************************************************************** * MMS server connection public API functions *********************************************************************************************/ MmsServerConnection MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoConnection isoCon) { MmsServerConnection self; if (connection == NULL) self = (MmsServerConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsServerConnection)); else self = connection; self->maxServOutstandingCalled = 0; self->maxServOutstandingCalling = 0; self->maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; self->dataStructureNestingLevel = 0; self->server = server; self->isoConnection = isoCon; #if (MMS_DYNAMIC_DATA_SETS == 1) self->namedVariableLists = LinkedList_create(); #endif IsoConnection_installListener(isoCon, messageReceived, (void*) self); return self; } void MmsServerConnection_destroy(MmsServerConnection self) { #if (MMS_FILE_SERVICE == 1) int frsmIndex = 0; for (frsmIndex = 0; frsmIndex < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; frsmIndex++) if (self->frsms[frsmIndex].fileHandle != NULL) FileSystem_closeFile(self->frsms[frsmIndex].fileHandle); #endif #if (MMS_DYNAMIC_DATA_SETS == 1) LinkedList_destroyDeep(self->namedVariableLists, (LinkedListValueDeleteFunction) MmsNamedVariableList_destroy); #endif GLOBAL_FREEMEM(self); } #if (MMS_DYNAMIC_DATA_SETS == 1) bool MmsServerConnection_addNamedVariableList(MmsServerConnection self, MmsNamedVariableList variableList) { //TODO check if operation is allowed! LinkedList_add(self->namedVariableLists, variableList); return true; } void MmsServerConnection_deleteNamedVariableList(MmsServerConnection self, char* listName) { mmsServer_deleteVariableList(self->namedVariableLists, listName); } MmsNamedVariableList MmsServerConnection_getNamedVariableList(MmsServerConnection self, const char* variableListName) { return mmsServer_getNamedVariableListWithName(self->namedVariableLists, variableListName); } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ char* MmsServerConnection_getClientAddress(MmsServerConnection self) { return IsoConnection_getPeerAddress(self->isoConnection); } IsoConnection MmsServerConnection_getIsoConnection(MmsServerConnection self) { return self->isoConnection; } #if (MMS_DYNAMIC_DATA_SETS == 1) LinkedList MmsServerConnection_getNamedVariableLists(MmsServerConnection self) { return self->namedVariableLists; } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ uint32_t MmsServerConnection_getLastInvokeId(MmsServerConnection self) { return self->lastInvokeId; }