// // -------------------------------------------------------------------------- // Gurux Ltd // // // // Filename: $HeadURL$ // // Version: $Revision$, // $Date$ // $Author$ // // Copyright (c) Gurux Ltd // //--------------------------------------------------------------------------- // // DESCRIPTION // // This file is a part of Gurux Device Framework. // // Gurux Device Framework is Open Source 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; version 2 of the License. // Gurux Device Framework 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. // // This code is licensed under the GNU General Public License v2. // Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt //--------------------------------------------------------------------------- #include "gxignore.h" #include "serverevents.h" #if defined(_WIN32) || defined(_WIN64) || defined(__linux__) #include #endif #include "gxmem.h" #if _MSC_VER > 1400 #include #endif #include /* memset */ #include "enums.h" #include "dlms.h" #ifndef DLMS_IGNORE_HIGH_GMAC #include "ciphering.h" #endif //DLMS_IGNORE_HIGH_GMAC #include "crc.h" #include "cosem.h" #include "gxobjects.h" #include "date.h" #ifndef DLMS_IGNORE_HIGH_MD5 #include "gxmd5.h" #endif //DLMS_IGNORE_HIGH_MD5 #ifndef DLMS_IGNORE_HIGH_SHA256 #include "gxsha256.h" #endif //DLMS_IGNORE_HIGH_SHA256 #ifndef DLMS_IGNORE_HIGH_SHA1 #include "gxsha1.h" #endif //DLMS_IGNORE_HIGH_SHA1 #ifndef DLMS_IGNORE_HIGH_AES #include "gxaes.h" #endif //DLMS_IGNORE_HIGH_AES unsigned char pduAttributes[PDU_MAX_HEADER_SIZE]; static const unsigned char LLC_SEND_BYTES[3] = { 0xE6, 0xE6, 0x00 }; static const unsigned char LLC_REPLY_BYTES[3] = { 0xE6, 0xE7, 0x00 }; static const unsigned char HDLC_FRAME_START_END = 0x7E; unsigned char dlms_useHdlc(DLMS_INTERFACE_TYPE type) { #ifndef DLMS_IGNORE_HDLC return type == DLMS_INTERFACE_TYPE_HDLC || type == DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E || type == DLMS_INTERFACE_TYPE_PLC_HDLC; #else return 0; #endif //DLMS_IGNORE_HDLC } //Makes sure that the basic settings are set. int dlms_checkInit(dlmsSettings* settings) { if (settings->clientAddress == 0) { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } if (settings->serverAddress == 0) { return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS; } if (settings->maxPduSize < 64) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } return DLMS_ERROR_CODE_OK; } #ifndef DLMS_IGNORE_HIGH_GMAC unsigned char dlms_useDedicatedKey(dlmsSettings* settings) { #ifndef DLMS_IGNORE_MALLOC return settings->cipher.dedicatedKey != NULL; #else return memcmp(settings->cipher.dedicatedKey, EMPTY_KEY, 16) != 0; #endif //DLMS_IGNORE_MALLOC } unsigned char dlms_usePreEstablishedConnection(dlmsSettings* settings) { #ifndef DLMS_IGNORE_MALLOC return settings->preEstablishedSystemTitle != NULL; #else return memcmp(settings->preEstablishedSystemTitle, EMPTY_SYSTEM_TITLE, 8) != 0; #endif //DLMS_IGNORE_MALLOC } #ifndef DLMS_IGNORE_HIGH_GMAC /** * Get used glo message. * * @param command * Executed DLMS command. * @return Integer value of glo message. */ unsigned char dlms_getGloMessage(dlmsSettings* settings, DLMS_COMMAND command, DLMS_COMMAND encryptedCommand) { unsigned char cmd; unsigned glo = settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_PROTECTION || dlms_usePreEstablishedConnection(settings); unsigned ded = dlms_useDedicatedKey(settings) && (settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0; if (encryptedCommand == DLMS_COMMAND_GENERAL_GLO_CIPHERING || encryptedCommand == DLMS_COMMAND_GENERAL_DED_CIPHERING) { cmd = encryptedCommand; } else if (glo && encryptedCommand == DLMS_COMMAND_NONE) { if (ded) { cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING; } else { cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING; } } else { switch (command) { #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_READ_REQUEST: if (ded) { cmd = DLMS_COMMAND_DED_READ_REQUEST; } else { cmd = DLMS_COMMAND_GLO_READ_REQUEST; } break; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_GET_REQUEST: if (ded) { cmd = DLMS_COMMAND_DED_GET_REQUEST; } else { cmd = DLMS_COMMAND_GLO_GET_REQUEST; } break; #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_WRITE_REQUEST: if (ded) { cmd = DLMS_COMMAND_DED_WRITE_REQUEST; } else { cmd = DLMS_COMMAND_GLO_WRITE_REQUEST; } break; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_SET_REQUEST: if (ded) { cmd = DLMS_COMMAND_DED_SET_REQUEST; } else { cmd = DLMS_COMMAND_GLO_SET_REQUEST; } break; case DLMS_COMMAND_METHOD_REQUEST: if (ded) { cmd = DLMS_COMMAND_DED_METHOD_REQUEST; } else { cmd = DLMS_COMMAND_GLO_METHOD_REQUEST; } break; #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_READ_RESPONSE: if (ded) { cmd = DLMS_COMMAND_DED_READ_RESPONSE; } else { cmd = DLMS_COMMAND_GLO_READ_RESPONSE; } break; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_GET_RESPONSE: if (ded) { cmd = DLMS_COMMAND_DED_GET_RESPONSE; } else { cmd = DLMS_COMMAND_GLO_GET_RESPONSE; } break; #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_WRITE_RESPONSE: if (ded) { cmd = DLMS_COMMAND_DED_WRITE_RESPONSE; } else { cmd = DLMS_COMMAND_GLO_WRITE_RESPONSE; } break; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_SET_RESPONSE: if (ded) { cmd = DLMS_COMMAND_DED_SET_RESPONSE; } else { cmd = DLMS_COMMAND_GLO_SET_RESPONSE; } break; case DLMS_COMMAND_METHOD_RESPONSE: if (ded) { cmd = DLMS_COMMAND_DED_METHOD_RESPONSE; } else { cmd = DLMS_COMMAND_GLO_METHOD_RESPONSE; } break; case DLMS_COMMAND_DATA_NOTIFICATION: if (ded) { cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING; } else { cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING; } break; case DLMS_COMMAND_RELEASE_REQUEST: cmd = DLMS_COMMAND_RELEASE_REQUEST; break; case DLMS_COMMAND_RELEASE_RESPONSE: cmd = DLMS_COMMAND_RELEASE_RESPONSE; break; case DLMS_COMMAND_GENERAL_DED_CIPHERING: cmd = DLMS_COMMAND_GENERAL_DED_CIPHERING; break; case DLMS_COMMAND_GENERAL_GLO_CIPHERING: cmd = DLMS_COMMAND_GENERAL_GLO_CIPHERING; break; default: cmd = DLMS_COMMAND_NONE; } } return cmd; } #endif //DLMS_IGNORE_HIGH_GMAC #endif //DLMS_IGNORE_HIGH_GMAC unsigned char dlms_getInvokeIDPriority(dlmsSettings* settings, unsigned char increase) { unsigned char value = 0; if (settings->priority == DLMS_PRIORITY_HIGH) { value |= 0x80; } if (settings->serviceClass == DLMS_SERVICE_CLASS_CONFIRMED) { value |= 0x40; } if (increase) { settings->invokeID = (unsigned char)((1 + settings->invokeID) & 0xF); } value |= settings->invokeID; return value; } /** * Generates Invoke ID and priority. * * @param settings * DLMS settings-> * @return Invoke ID and priority. */ int32_t dlms_getLongInvokeIDPriority(dlmsSettings* settings) { int32_t value = 0; if (settings->priority == DLMS_PRIORITY_HIGH) { value = 0x80000000; } if (settings->serviceClass == DLMS_SERVICE_CLASS_CONFIRMED) { value |= 0x40000000; } value |= (settings->longInvokeID & 0xFFFFFF); ++settings->longInvokeID; return value; } int dlms_getMaxPduSize( dlmsSettings* settings, gxByteBuffer* data, gxByteBuffer* bb) { int offset = 0; int size = data->size - data->position; if (bb != NULL) { offset = bb->size; } if (size + offset > settings->maxPduSize) { size = settings->maxPduSize - offset; size -= hlp_getObjectCountSizeInBytes(size); } else if (size + hlp_getObjectCountSizeInBytes(size) > settings->maxPduSize) { size = (size - hlp_getObjectCountSizeInBytes(size)); } return size; } #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) int dlms_setData2( unsigned char* buff, uint32_t length, DLMS_DATA_TYPE type, dlmsVARIANT* value) #else int dlms_setData2( unsigned char* buff, uint16_t length, DLMS_DATA_TYPE type, dlmsVARIANT* value) #endif { gxByteBuffer bb; bb_attach(&bb, buff, length, length); return dlms_setData(&bb, type, value); } int dlms_setData(gxByteBuffer* buff, DLMS_DATA_TYPE type, dlmsVARIANT* value) { #ifndef DLMS_IGNORE_MALLOC int ret; ret = var_changeType(value, type); if (ret != DLMS_ERROR_CODE_OK) { return ret; } #endif //DLMS_IGNORE_MALLOC return var_getBytes2(value, type, buff); } #ifndef DLMS_IGNORE_MALLOC int getCount(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { if (hlp_getObjectCount2(buff, &info->count) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } value->Arr = (variantArray*)gxmalloc(sizeof(variantArray)); if (value->Arr == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(value->Arr); va_capacity(value->Arr, info->count); value->vt = DLMS_DATA_TYPE_ARRAY; return 0; } /** * Get array from DLMS data. * * buff * Received DLMS data. * info * Data info. * index * starting index. * Returns CGXDLMSVariant array. */ int getArray(gxByteBuffer* buff, gxDataInfo* info, uint16_t index, dlmsVARIANT* value) { dlmsVARIANT* tmp; gxDataInfo info2; uint32_t size; uint16_t pos, startIndex; int ret; if (info->count == 0) { if ((ret = getCount(buff, info, value)) != 0) { return ret; } } size = buff->size - buff->position; if (info->count != 0 && size < 1) { info->complete = 0; return 0; } startIndex = index; // Position where last row was found. Cache uses this info. for (pos = info->index; pos != info->count; ++pos) { tmp = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT)); if (tmp == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(tmp); di_init(&info2); if ((ret = dlms_getData(buff, &info2, tmp)) != 0) { var_clear(tmp); gxfree(tmp); return ret; } if (!info2.complete) { var_clear(tmp); gxfree(tmp); buff->position = startIndex; info->complete = 0; break; } else { if (info2.count == info2.index) { startIndex = (uint16_t)buff->position; va_push(value->Arr, tmp); } } } info->index = pos; return DLMS_ERROR_CODE_OK; } #endif //DLMS_IGNORE_MALLOC /** * Get UInt32 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed UInt32 value. */ int getUInt32(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 4) { info->complete = 0; return 0; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_UINT32; } if ((ret = bb_getUInt32(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->ulVal : value->pulVal)) != 0) { return ret; } return DLMS_ERROR_CODE_OK; } /** * Get Int32 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed Int32 value. */ int getInt32(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 4) { info->complete = 0; return 0; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_INT32; } if ((ret = bb_getInt32(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->lVal : value->plVal)) != 0) { return ret; } return DLMS_ERROR_CODE_OK; } /** * Get bit std::string value from DLMS data. * * buff : Received DLMS data. * info : Data info. * Returns parsed bit std::string value. */ static int getBitString(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { uint16_t cnt = 0; #ifndef DLMS_IGNORE_MALLOC int ret; #endif //DLMS_IGNORE_MALLOC if (hlp_getObjectCount2(buff, &cnt) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } uint16_t byteCnt = ba_getByteCount(cnt); // If there is not enough data available. if (buff->size - buff->position < byteCnt) { info->complete = 0; return 0; } #ifdef DLMS_IGNORE_MALLOC if (value->vt != (DLMS_DATA_TYPE_BIT_STRING | DLMS_DATA_TYPE_BYREF)) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } if (value->capacity < cnt) { return DLMS_ERROR_CODE_INCONSISTENT_CLASS_OR_OBJECT; } value->size = cnt; memcpy(value->pVal, buff->data + buff->position, byteCnt); buff->position += byteCnt; #else value->bitArr = (bitArray*)gxmalloc(sizeof(bitArray)); ba_init(value->bitArr); if ((ret = hlp_add(value->bitArr, buff, cnt)) != 0) { return ret; } #endif //DLMS_IGNORE_MALLOC return 0; } static int getBool(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; unsigned char ch; // If there is not enough data available. if (buff->size - buff->position < 1) { info->complete = 0; return 0; } if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_BOOLEAN; value->boolVal = ch != 0; } else { *value->pboolVal = ch != 0; } return 0; } /** * Get string value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed std::string value. */ int getString(gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value) { int ret = 0; uint16_t len = 0; if (knownType) { len = (uint16_t)buff->size; } else { if (hlp_getObjectCount2(buff, &len) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } // If there is not enough data available. if (buff->size - buff->position < (uint16_t)len) { info->complete = 0; return 0; } } #ifdef DLMS_IGNORE_MALLOC if (value->vt != (DLMS_DATA_TYPE_STRING | DLMS_DATA_TYPE_BYREF)) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } if (value->capacity < len) { return DLMS_ERROR_CODE_OUTOFMEMORY; } value->size = len; memcpy(value->pVal, buff->data + buff->position, len); buff->position += len; #else value->vt = DLMS_DATA_TYPE_STRING; if (len > 0) { value->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); BYTE_BUFFER_INIT(value->strVal); ret = bb_set2(value->strVal, buff, buff->position, len); } #endif //DLMS_IGNORE_MALLOC return ret; } #ifndef DLMS_IGNORE_MALLOC /** * Get UTF std::string value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed UTF std::string value. */ int getUtfString( gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value) { int ret = 0; uint16_t len = 0; var_clear(value); if (knownType) { len = (uint16_t)buff->size; } else { if (hlp_getObjectCount2(buff, &len) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } // If there is not enough data available. if (buff->size - buff->position < len) { info->complete = 0; return 0; } } if (len > 0) { value->strUtfVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); BYTE_BUFFER_INIT(value->strUtfVal); ret = bb_set2(value->strUtfVal, buff, buff->position, len); value->vt = DLMS_DATA_TYPE_STRING_UTF8; } else { value->strUtfVal = NULL; } return ret; } #endif //DLMS_IGNORE_MALLOC /** * Get octet string value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed octet string value. */ int getOctetString(gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value) { uint16_t len; int ret = 0; if (knownType) { len = (uint16_t)buff->size; } else { if (hlp_getObjectCount2(buff, &len) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } // If there is not enough data available. if (buff->size - buff->position < len) { info->complete = 0; return 0; } } #ifdef DLMS_IGNORE_MALLOC if (value->vt != (DLMS_DATA_TYPE_OCTET_STRING | DLMS_DATA_TYPE_BYREF)) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } if (value->capacity < len) { return DLMS_ERROR_CODE_OUTOFMEMORY; } value->size = len; memcpy(value->pVal, buff->data + buff->position, len); buff->position += len; #else if (len == 0) { var_clear(value); } else { ret = var_addBytes(value, buff->data + buff->position, len); #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) buff->position += (uint32_t)len; #else buff->position += (uint16_t)len; #endif } #endif //DLMS_IGNORE_MALLOC return ret; } /** * Get BCD value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed BCD value. */ int getBcd(gxByteBuffer* buff, gxDataInfo* info, unsigned char knownType, dlmsVARIANT* value) { #ifndef DLMS_IGNORE_MALLOC unsigned char idHigh, idLow; #endif //DLMS_IGNORE_MALLOC static const char hexArray[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; int ret = 0, a; uint16_t len; unsigned char ch; if (knownType) { len = (uint16_t)buff->size; } else { if (hlp_getObjectCount2(buff, &len) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } // If there is not enough data available. if ((buff->size - buff->position) < (uint16_t)(2 * len)) { info->complete = 0; return 0; } } #ifdef DLMS_IGNORE_MALLOC value->size = 2 * len; if (value->vt != (DLMS_DATA_TYPE_OCTET_STRING | DLMS_DATA_TYPE_BYREF)) { ret = DLMS_ERROR_CODE_INVALID_PARAMETER; } else if (value->capacity < len) { ret = DLMS_ERROR_CODE_OUTOFMEMORY; } else { unsigned char* p = (unsigned char*)value->pVal; for (a = 0; a != len; ++a) { if ((ret = bb_getUInt8(buff, &ch)) != 0) { break; } *p = hexArray[ch >> 4]; p++; *p = hexArray[ch & 0x0F]; p++; } } #else value->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); BYTE_BUFFER_INIT(value->strVal); value->vt = DLMS_DATA_TYPE_STRING; bb_capacity(value->strVal, (uint16_t)(len * 2)); for (a = 0; a != len; ++a) { if ((ret = bb_getUInt8(buff, &ch)) != 0) { break; } idHigh = ch >> 4; idLow = ch & 0x0F; bb_setInt8(value->strVal, hexArray[idHigh]); bb_setInt8(value->strVal, hexArray[idLow]); } #endif //DLMS_IGNORE_MALLOC return ret; } /** * Get UInt8 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed UInt8 value. */ int getUInt8(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 1) { info->complete = 0; return 0; } if ((ret = bb_getUInt8(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->bVal : value->pbVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_UINT8; } return 0; } /** * Get Int16 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed Int16 value. */ int getInt16(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 2) { info->complete = 0; return 0; } if ((ret = bb_getInt16(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->iVal : value->piVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_INT16; } return 0; } /** * Get Int8 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed Int8 value. */ int getInt8(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 1) { info->complete = 0; return 0; } if ((ret = bb_getInt8(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->cVal : value->pcVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_INT8; } return 0; } /** * Get UInt16 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed UInt16 value. */ int getUInt16(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 2) { info->complete = 0; return 0; } if ((ret = bb_getUInt16(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->uiVal : value->puiVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_UINT16; } return 0; } /** * Get Int64 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed Int64 value. */ int getInt64(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 8) { info->complete = 0; return 0; } if ((ret = bb_getInt64(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->llVal : value->pllVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_INT64; } return 0; } /** * Get UInt64 value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed UInt64 value. */ int getUInt64(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 8) { info->complete = 0; return 0; } if ((ret = bb_getUInt64(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->ullVal : value->pullVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_UINT64; } return 0; } /** * Get enumeration value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns parsed enumeration value. */ int getEnum(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 1) { info->complete = 0; return 0; } if ((ret = bb_getUInt8(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->bVal : value->pbVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_ENUM; } return 0; } #ifndef DLMS_IGNORE_FLOAT64 /** * Get double value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns Parsed double value. */ int getDouble(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 8) { info->complete = 0; return 0; } if ((ret = bb_getDouble(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->dblVal : value->pdblVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_FLOAT64; } return 0; } #endif //#endif //DLMS_IGNORE_FLOAT32 #ifndef DLMS_IGNORE_FLOAT32 /** * Get float value from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns Parsed float value. */ int getFloat(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; // If there is not enough data available. if (buff->size - buff->position < 4) { info->complete = 0; return 0; } if ((ret = bb_getFloat(buff, (value->vt & DLMS_DATA_TYPE_BYREF) == 0 ? &value->fltVal : value->pfltVal)) != 0) { return ret; } if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = DLMS_DATA_TYPE_FLOAT32; } return 0; } #endif //DLMS_IGNORE_FLOAT32 /** * Get time from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns Parsed time. */ int getTime(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; unsigned char ch, hour, minute, second; uint16_t ms = 0xFFFF; if (buff->size - buff->position < 4) { // If there is not enough data available. info->complete = 0; return 0; } // Get time. if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } hour = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } minute = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } second = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } if (ch != 0xFF) { ms = ch * 10; } #if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->dateTime = (gxtime*)gxmalloc(sizeof(gxtime)); } time_init(value->dateTime, (uint16_t)-1, 0xFF, 0xFF, hour, minute, second, ms, 0x8000); value->vt = DLMS_DATA_TYPE_TIME; #else time_init((gxtime*)value->pVal, -1, -1, -1, hour, minute, second, ms, 0x8000); #endif //DLMS_IGNORE_MALLOC return 0; } /** * Get date from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns Parsed date. */ int getDate(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { int ret; unsigned char month, day; uint16_t year; unsigned char ch; if (buff->size - buff->position < 5) { // If there is not enough data available. info->complete = 0; return 0; } // Get year. if ((ret = bb_getUInt16(buff, &year)) != 0) { return ret; } // Get month if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } month = ch; // Get day. if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } day = ch; // Skip week day if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } #if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) if ((value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->dateTime = (gxtime*)gxmalloc(sizeof(gxtime)); } time_init(value->dateTime, year, month, day, 0xFF, 0xFF, 0xFF, 0xFF, 0x8000); if (ch > 7) { value->dateTime->skip |= DATETIME_SKIPS_DAYOFWEEK; } value->vt = DLMS_DATA_TYPE_DATE; #else time_init((gxtime*)value->pVal, year, month, day, -1, -1, -1, -1, 0x8000); if (ch > 7) { ((gxtime*)value->pVal)->skip |= DATETIME_SKIPS_DAYOFWEEK; } #endif //DLMS_IGNORE_MALLOC return 0; } /** * Get date and time from DLMS data. * * buff * Received DLMS data. * info * Data info. * Returns Parsed date and time. */ int getDateTime(gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value) { uint16_t year; #ifdef DLMS_USE_EPOCH_TIME int ret, status; #else int ret, ms, status; #endif // DLMS_USE_EPOCH_TIME unsigned char ch; // If there is not enough data available. if (buff->size - buff->position < 12) { info->complete = 0; return 0; } // Get year. if ((ret = bb_getUInt16(buff, &year)) != 0) { return ret; } gxtime* t; #if defined(DLMS_IGNORE_MALLOC) t = value->pVal; #else if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { t = value->pVal; } else { t = value->dateTime = (gxtime*)gxmalloc(sizeof(gxtime)); value->vt = DLMS_DATA_TYPE_DATETIME; } #endif //DLMS_IGNORE_MALLOC #ifdef DLMS_USE_EPOCH_TIME short deviation; unsigned char mon = 0, day = 0, hour = 0, min = 0, sec = 0, wday = 0, skip = 0; // Get month if ((ret = bb_getUInt8(buff, &mon)) != 0) { return ret; } // Get day if ((ret = bb_getUInt8(buff, &day)) != 0) { return ret; } // Skip week day if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } // Get time. if ((ret = bb_getUInt8(buff, &hour)) != 0) { return ret; } if ((ret = bb_getUInt8(buff, &min)) != 0) { return ret; } if ((ret = bb_getUInt8(buff, &sec)) != 0) { return ret; } //Get ms. if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } if ((ret = bb_getInt16(buff, &deviation)) != 0) { return ret; } if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } status = ch; t->status = (DLMS_CLOCK_STATUS)status; skip = DATETIME_SKIPS_MS; if (year < 1 || year == 0xFFFF) { skip |= DATETIME_SKIPS_YEAR; year = 1970; } if (wday > 7) { wday = 0; skip |= DATETIME_SKIPS_DAYOFWEEK; } if ((mon < 1 || mon > 12) && mon != 0xFE && mon != 0xFD) { skip |= DATETIME_SKIPS_MONTH; mon = 1; } if ((day < 1 || day > 31) && day != 0xFE && day != 0xFD) { skip |= DATETIME_SKIPS_DAY; day = 1; } if (hour > 24) { skip |= DATETIME_SKIPS_HOUR; hour = 0; } if (min > 60) { skip |= DATETIME_SKIPS_MINUTE; min = 0; } if (sec > 60) { skip |= DATETIME_SKIPS_SECOND; sec = 0; } time_init(t, year, mon, day, hour, min, sec, 0, deviation); t->skip = skip; t->status = status; if (status == 0xFF) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_STATUS); t->status = 0; } #else t->value.tm_yday = 0; t->value.tm_year = year; // Get month if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->value.tm_mon = ch; // Get day if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->value.tm_mday = ch; // Skip week day if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->value.tm_wday = ch; // Get time. if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->value.tm_hour = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->value.tm_min = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->value.tm_sec = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } t->extraInfo = DLMS_DATE_TIME_EXTRA_INFO_NONE; t->skip = DATETIME_SKIPS_NONE; ms = ch; if (ms != 0xFF) { ms *= 10; } else { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_MS); } if ((ret = bb_getInt16(buff, &t->deviation)) != 0) { return ret; } if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } if (ch == 0xFF) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_STATUS); ch = 0; } status = ch; t->status = (DLMS_CLOCK_STATUS)status; if (year < 1 || year == 0xFFFF) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_YEAR); t->value.tm_year = 1; } else { t->value.tm_year -= 1900; } if (t->value.tm_wday < 0 || t->value.tm_wday > 7) { t->value.tm_wday = 0; t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_DAYOFWEEK); } if (t->value.tm_mon == 0xFE) { t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_DST_BEGIN; t->value.tm_mon = 0; } else if (t->value.tm_mon == 0xFD) { t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_DST_END; t->value.tm_mon = 0; } else if (t->value.tm_mon < 1 || t->value.tm_mon > 12) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_MONTH); t->value.tm_mon = 0; } else { t->value.tm_mon -= 1; } if (t->value.tm_mday == 0xFD) { // 2nd last day of month. t->value.tm_mday = 1; t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_LAST_DAY2; } else if (t->value.tm_mday == 0xFE) { //Last day of month t->value.tm_mday = 1; t->extraInfo |= DLMS_DATE_TIME_EXTRA_INFO_LAST_DAY; } else if (t->value.tm_mday == -1 || t->value.tm_mday == 0 || t->value.tm_mday > 31) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_DAY); t->value.tm_mday = 1; } if (t->value.tm_hour < 0 || t->value.tm_hour > 24) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_HOUR); t->value.tm_hour = 0; } if (t->value.tm_min < 0 || t->value.tm_min > 60) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_MINUTE); t->value.tm_min = 0; } if (t->value.tm_sec < 0 || t->value.tm_sec > 60) { t->skip = (DATETIME_SKIPS)(t->skip | DATETIME_SKIPS_SECOND); t->value.tm_sec = 0; } t->value.tm_isdst = (status & DLMS_CLOCK_STATUS_DAYLIGHT_SAVE_ACTIVE); mktime(&t->value); #endif //DLMS_USE_EPOCH_TIME return 0; } #ifndef DLMS_IGNORE_MALLOC int getCompactArrayItem( gxByteBuffer* buff, DLMS_DATA_TYPE dt, variantArray* list, uint32_t len) { int ret; gxDataInfo tmp; di_init(&tmp); tmp.type = dt; uint32_t start = buff->position; dlmsVARIANT* value = gxmalloc(sizeof(dlmsVARIANT)); if (value == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(value); if (dt == DLMS_DATA_TYPE_STRING) { while (buff->position - start < len) { var_clear(value); di_init(&tmp); tmp.type = dt; if ((ret = getString(buff, &tmp, 0, value)) != 0) { return ret; } va_push(list, value); if (!tmp.complete) { break; } } } else if (dt == DLMS_DATA_TYPE_OCTET_STRING) { while (buff->position - start < len) { var_clear(value); di_init(&tmp); tmp.type = dt; if ((ret = getOctetString(buff, &tmp, 0, value)) != 0) { return ret; } va_push(list, value); if (!tmp.complete) { break; } } } else { while (buff->position - start < len) { var_clear(value); di_init(&tmp); tmp.type = dt; if ((ret = dlms_getData(buff, &tmp, value)) != 0) { return ret; } va_push(list, value); if (!tmp.complete) { break; } } } return 0; } int getCompactArrayItem2( gxByteBuffer* buff, variantArray* dt, variantArray* list, int len) { int ret, pos; dlmsVARIANT* tmp = gxmalloc(sizeof(dlmsVARIANT)); if (tmp == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } dlmsVARIANT* it; var_init(tmp); tmp->vt = DLMS_DATA_TYPE_ARRAY; tmp->Arr = (variantArray*)gxmalloc(sizeof(variantArray)); if (tmp->Arr == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(tmp->Arr); for (pos = 0; pos != dt->size; ++pos) { if ((ret = va_getByIndex(dt, pos, &it)) != 0) { var_clear(tmp); gxfree(tmp); return ret; } if (it->vt == DLMS_DATA_TYPE_ARRAY || it->vt == DLMS_DATA_TYPE_STRUCTURE) { if ((ret = getCompactArrayItem2(buff, it->Arr, tmp->Arr, 1)) != 0) { var_clear(tmp); gxfree(tmp); return ret; } } else { if ((ret = getCompactArrayItem(buff, (DLMS_DATA_TYPE)it->bVal, tmp->Arr, 1)) != 0) { var_clear(tmp); gxfree(tmp); return ret; } } } va_push(list, tmp); return 0; } int getDataTypes( gxByteBuffer* buff, variantArray* cols, int len) { int ret; unsigned char ch; uint16_t cnt; DLMS_DATA_TYPE dt; if (cols->size == 0) { va_capacity(cols, (uint16_t)len); } for (int pos = 0; pos != len; ++pos) { if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } dt = (DLMS_DATA_TYPE)ch; if (dt == DLMS_DATA_TYPE_ARRAY) { if ((ret = bb_getUInt16(buff, &cnt)) != 0) { return ret; } dlmsVARIANT tmp; dlmsVARIANT* it; var_init(&tmp); tmp.Arr = (variantArray*)gxmalloc(sizeof(variantArray)); tmp.vt = DLMS_DATA_TYPE_ARRAY; if (tmp.Arr == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(tmp.Arr); getDataTypes(buff, tmp.Arr, 1); if ((ret = va_getByIndex(tmp.Arr, 0, &it)) != 0) { va_clear(cols); return ret; } dlmsVARIANT* tmp2 = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT)); if (tmp2 == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(tmp2); tmp2->vt = DLMS_DATA_TYPE_ARRAY; tmp2->Arr = (variantArray*)gxmalloc(sizeof(variantArray)); if (tmp2->Arr == NULL) { var_clear(tmp2); gxfree(tmp2); return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(tmp2->Arr); for (int i = 0; i != cnt; ++i) { dlmsVARIANT* tmp3 = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT)); if (tmp3 == NULL) { var_clear(tmp2); gxfree(tmp2); return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(tmp3); if ((ret = var_copy(tmp3, it)) != 0) { var_clear(tmp3); gxfree(tmp3); var_clear(tmp2); gxfree(tmp2); return ret; } va_push(tmp2->Arr, tmp3); } var_clear(&tmp); va_push(cols, tmp2); } else if (dt == DLMS_DATA_TYPE_STRUCTURE) { dlmsVARIANT* tmp = gxmalloc(sizeof(dlmsVARIANT)); if (tmp == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(tmp); if ((ret = bb_getUInt8(buff, &ch)) != 0) { va_clear(cols); var_clear(tmp); gxfree(tmp); return ret; } tmp->vt = DLMS_DATA_TYPE_STRUCTURE; tmp->Arr = (variantArray*)gxmalloc(sizeof(variantArray)); if (tmp->Arr == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(tmp->Arr); getDataTypes(buff, tmp->Arr, ch); va_push(cols, tmp); } else { dlmsVARIANT* tmp = gxmalloc(sizeof(dlmsVARIANT)); if (tmp == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(tmp); if (cols->size == 0) { va_capacity(cols, 1); } var_setUInt8(tmp, dt); va_push(cols, tmp); } } return 0; } //Get compact array value from DLMS data. int getCompactArray( dlmsSettings* settings, gxByteBuffer* buff, gxDataInfo* info, dlmsVARIANT* value, unsigned char onlyDataTypes) { int ret, pos; uint16_t len; unsigned char ch; var_clear(value); // If there is not enough data available. if (buff->size - buff->position < 2) { info->complete = 0; return 0; } if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } DLMS_DATA_TYPE dt = (DLMS_DATA_TYPE)ch; if (dt == DLMS_DATA_TYPE_ARRAY) { //Invalid compact array data. return DLMS_ERROR_CODE_INVALID_PARAMETER; } if ((ret = hlp_getObjectCount2(buff, &len)) != 0) { return ret; } value->Arr = (variantArray*)gxmalloc(sizeof(variantArray)); value->vt = DLMS_DATA_TYPE_ARRAY; if (value->Arr == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(value->Arr); if (dt == DLMS_DATA_TYPE_STRUCTURE) { // Get data types. variantArray cols; va_init(&cols); if ((ret = getDataTypes(buff, &cols, len)) != 0) { va_clear(&cols); return ret; } if (onlyDataTypes) { va_attach2(value->Arr, &cols); return 0; } if (buff->position == buff->size) { len = 0; } else { if ((ret = hlp_getObjectCount2(buff, &len)) != 0) { va_clear(&cols); return ret; } } int start = buff->position; while (buff->position - start < len) { dlmsVARIANT* it, * it2, * it3, * it4; variantArray* row = (variantArray*)gxmalloc(sizeof(variantArray)); if (row == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } va_init(row); for (pos = 0; pos != cols.size; ++pos) { if ((ret = va_getByIndex(&cols, pos, &it)) != 0) { va_clear(&cols); va_clear(row); gxfree(row); return ret; } if (it->vt == DLMS_DATA_TYPE_STRUCTURE) { if ((ret = getCompactArrayItem2(buff, it->Arr, row, 1)) != 0) { va_clear(&cols); va_clear(row); gxfree(row); return ret; } } else if (it->vt == DLMS_DATA_TYPE_ARRAY) { int pos1; variantArray tmp2; va_init(&tmp2); #ifdef DLMS_ITALIAN_STANDARD //Some Italy meters require that there is a array count in data. if (info->appendAA) { if ((ret = hlp_getObjectCount2(buff, &len)) != 0) { va_clear(&cols); return ret; } if (it->Arr->size != len) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } } #endif //DLMS_ITALIAN_STANDARD if ((ret = getCompactArrayItem2(buff, it->Arr, &tmp2, 1)) != 0 || (ret = va_getByIndex(&tmp2, 0, &it2)) != 0) { va_clear(&tmp2); va_clear(&cols); va_clear(row); gxfree(row); return ret; } for (pos1 = 0; pos1 != it2->Arr->size; ++pos1) { if ((ret = va_getByIndex(it2->Arr, pos1, &it3)) != 0) { va_clear(&tmp2); va_clear(&cols); va_clear(row); gxfree(row); return ret; } it4 = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT)); if (it4 == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(it4); if ((ret = var_copy(it4, it3)) != 0) { return ret; } va_push(row, it4); } va_clear(&tmp2); } else { if ((ret = getCompactArrayItem(buff, (DLMS_DATA_TYPE)it->bVal, row, 1)) != 0) { va_clear(&cols); va_clear(row); gxfree(row); return ret; } } if (buff->position == buff->size) { break; } } //If all columns are read. if (row->size >= cols.size) { dlmsVARIANT* tmp = (dlmsVARIANT*)gxmalloc(sizeof(dlmsVARIANT)); if (tmp == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } var_init(tmp); tmp->Arr = row; tmp->vt = DLMS_DATA_TYPE_ARRAY; va_push(value->Arr, tmp); } else { break; } } va_clear(&cols); return 0; } return getCompactArrayItem(buff, dt, value->Arr, len); } #endif //DLMS_IGNORE_MALLOC int dlms_getData(gxByteBuffer* data, gxDataInfo* info, dlmsVARIANT* value) { unsigned char ch, knownType; int ret = 0; #ifndef DLMS_IGNORE_MALLOC uint32_t startIndex = data->position; var_clear(value); #endif //DLMS_IGNORE_MALLOC if (data->position == data->size) { info->complete = 0; return 0; } info->complete = 1; knownType = info->type != DLMS_DATA_TYPE_NONE; if (!knownType) { ret = bb_getUInt8(data, &ch); if (ret != DLMS_ERROR_CODE_OK) { return ret; } info->type = (DLMS_DATA_TYPE)ch; } if (info->type == DLMS_DATA_TYPE_NONE) { //Do nothing. return DLMS_ERROR_CODE_OK; } if (data->position == data->size) { info->complete = 0; return 0; } switch (info->type & ~DLMS_DATA_TYPE_BYREF) { case DLMS_DATA_TYPE_ARRAY: case DLMS_DATA_TYPE_STRUCTURE: { #if !defined(DLMS_IGNORE_MALLOC) ret = getArray(data, info, (uint16_t)startIndex, value); value->vt = info->type; #else if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { uint16_t count, pos; if ((ret = hlp_getObjectCount2(data, &count)) != 0) { return ret; } //If user try to write too many items. if (va_getCapacity(value->Arr) < count) { return DLMS_ERROR_CODE_INCONSISTENT_CLASS_OR_OBJECT; } value->Arr->size = count; for (pos = 0; pos != count; ++pos) { gxDataInfo info2; di_init(&info2); dlmsVARIANT* value2; if ((ret = va_getByIndex(value->Arr, pos, &value2)) != 0 || (ret = dlms_getData(data, &info2, value2)) != 0) { return ret; } } } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; } return 0; #endif //#!defined(DLMS_IGNORE_MALLOC) break; } case DLMS_DATA_TYPE_BOOLEAN: { ret = getBool(data, info, value); break; } case DLMS_DATA_TYPE_BIT_STRING: #if !defined(DLMS_IGNORE_MALLOC) ret = getBitString(data, info, value); #else if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { ret = getBitString(data, info, value); } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; return 0; } #endif //!defined(DLMS_IGNORE_MALLOC) break; case DLMS_DATA_TYPE_INT32: ret = getInt32(data, info, value); break; case DLMS_DATA_TYPE_UINT32: ret = getUInt32(data, info, value); break; case DLMS_DATA_TYPE_STRING: #if !defined(DLMS_IGNORE_MALLOC) ret = getString(data, info, knownType, value); #else if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { ret = getString(data, info, 0, value); } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; ret = 0; } #endif //!defined(DLMS_IGNORE_MALLOC) break; case DLMS_DATA_TYPE_STRING_UTF8: #if !defined(DLMS_IGNORE_MALLOC) ret = getUtfString(data, info, knownType, value); #else value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; return 0; #endif //!defined(DLMS_IGNORE_MALLOC) break; case DLMS_DATA_TYPE_OCTET_STRING: #if !defined(DLMS_IGNORE_MALLOC) ret = getOctetString(data, info, knownType, value); #else if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { ret = getOctetString(data, info, 0, value); } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; ret = 0; } #endif // DLMS_IGNORE_MALLOC break; case DLMS_DATA_TYPE_BINARY_CODED_DESIMAL: ret = getBcd(data, info, knownType, value); break; case DLMS_DATA_TYPE_INT8: ret = getInt8(data, info, value); break; case DLMS_DATA_TYPE_INT16: ret = getInt16(data, info, value); break; case DLMS_DATA_TYPE_UINT8: ret = getUInt8(data, info, value); break; case DLMS_DATA_TYPE_UINT16: ret = getUInt16(data, info, value); break; case DLMS_DATA_TYPE_COMPACT_ARRAY: #ifndef DLMS_IGNORE_MALLOC ret = getCompactArray(NULL, data, info, value, 0); #endif //DLMS_IGNORE_MALLOC break; case DLMS_DATA_TYPE_INT64: ret = getInt64(data, info, value); break; case DLMS_DATA_TYPE_UINT64: ret = getUInt64(data, info, value); break; case DLMS_DATA_TYPE_ENUM: ret = getEnum(data, info, value); break; #ifndef DLMS_IGNORE_FLOAT32 case DLMS_DATA_TYPE_FLOAT32: ret = getFloat(data, info, value); break; #endif //DLMS_IGNORE_FLOAT32 #ifndef DLMS_IGNORE_FLOAT64 case DLMS_DATA_TYPE_FLOAT64: ret = getDouble(data, info, value); break; #endif //DLMS_IGNORE_FLOAT64 case DLMS_DATA_TYPE_DATETIME: #if defined(DLMS_IGNORE_MALLOC) if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { ret = getDateTime(data, info, value); } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; ret = 0; } #else ret = getDateTime(data, info, value); #endif //DLMS_IGNORE_MALLOC break; case DLMS_DATA_TYPE_DATE: #if defined(DLMS_IGNORE_MALLOC) if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { ret = getDate(data, info, value); } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; ret = 0; } #else ret = getDate(data, info, value); #endif //DLMS_IGNORE_MALLOC break; case DLMS_DATA_TYPE_TIME: #if defined(DLMS_IGNORE_MALLOC) if ((value->vt & DLMS_DATA_TYPE_BYREF) != 0) { ret = getTime(data, info, value); } else { value->vt = DLMS_DATA_TYPE_OCTET_STRING; --data->position; value->byteArr = data; ret = 0; } #else ret = getTime(data, info, value); #endif //DLMS_IGNORE_MALLOC break; default: #ifdef _DEBUG //Assert in debug version. #if defined(_WIN32) || defined(_WIN64) || defined(__linux__) assert(0); #endif #endif ret = DLMS_ERROR_CODE_INVALID_PARAMETER; } #ifdef DLMS_IGNORE_MALLOC if (ret == 0 && (value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = info->type; } #else if (ret == 0 && (value->vt & DLMS_DATA_TYPE_BYREF) == 0) { value->vt = info->type; } #endif //DLMS_IGNORE_MALLOC return ret; } #ifndef DLMS_IGNORE_HDLC //Return DLMS_ERROR_CODE_FALSE if LLC bytes are not included. int dlms_checkLLCBytes(dlmsSettings* settings, gxByteBuffer* data) { if (settings->server) { //Check LLC bytes. if (memcmp(data->data + data->position, LLC_SEND_BYTES, 3) != 0) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } } else { //Check LLC bytes. if (memcmp(data->data + data->position, LLC_REPLY_BYTES, 3) != 0) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } } data->position += 3; return DLMS_ERROR_CODE_OK; } int dlms_getHDLCAddress( gxByteBuffer* buff, uint32_t* address, unsigned char checkClientAddress) { unsigned char ch; uint16_t s, pos; uint32_t l; int ret, size = 0; for (pos = (uint16_t)buff->position; pos != (uint16_t)buff->size; ++pos) { ++size; if ((ret = bb_getUInt8ByIndex(buff, pos, &ch)) != 0) { return ret; } if ((ch & 0x1) == 1) { break; } } //DLMS CCT test requires that client size is one byte. if (checkClientAddress && size != 1) { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } if (size == 1) { if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } *address = ((ch & 0xFE) >> 1); } else if (size == 2) { if ((ret = bb_getUInt16(buff, &s)) != 0) { return ret; } *address = ((s & 0xFE) >> 1) | ((s & 0xFE00) >> 2); } else if (size == 4) { if ((ret = bb_getUInt32(buff, &l)) != 0) { return ret; } *address = ((l & 0xFE) >> 1) | ((l & 0xFE00) >> 2) | ((l & 0xFE0000) >> 3) | ((l & 0xFE000000) >> 4); } else { return DLMS_ERROR_CODE_INVALID_PARAMETER; } return DLMS_ERROR_CODE_OK; } void dlms_getServerAddress(uint32_t address, uint32_t* logical, uint32_t* physical) { if (address < 0x4000) { *logical = address >> 7; *physical = address & 0x7F; } else { *logical = address >> 14; *physical = address & 0x3FFF; } } /** * Check that client and server address match. * * @param server * Is server. * @param settings * DLMS settings. * @param reply * Received data. * @param index * Position. * @return True, if client and server address match. */ int dlms_checkHdlcAddress( unsigned char server, dlmsSettings* settings, gxByteBuffer* reply, uint16_t index) { unsigned char ch; int ret; uint32_t source, target; // Get destination and source addresses. if ((ret = dlms_getHDLCAddress(reply, &target, 0)) != 0) { return ret; } if ((ret = dlms_getHDLCAddress(reply, &source, server)) != 0) { return ret; } if (server) { // Check that server addresses match. if (settings->serverAddress != 0 && settings->serverAddress != target) { // Get frame command. if (bb_getUInt8ByIndex(reply, reply->position, &ch) != 0) { return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS; } return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS; } else { settings->serverAddress = target; } // Check that client addresses match. if (settings->clientAddress != 0 && settings->clientAddress != source) { // Get frame command. if (bb_getUInt8ByIndex(reply, reply->position, &ch) != 0) { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } //If SNRM and client has not call disconnect and changes client ID. if (ch == DLMS_COMMAND_SNRM) { settings->clientAddress = (uint16_t)source; } else { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } } else { settings->clientAddress = (uint16_t)source; } } else { // Check that client addresses match. if (settings->clientAddress != target) { // If echo. if (settings->clientAddress == source && settings->serverAddress == target) { reply->position = index + 1; } return DLMS_ERROR_CODE_FALSE; } // Check that server addresses match. if (settings->serverAddress != source && // If All-station (Broadcast). (settings->serverAddress & 0x7F) != 0x7F && (settings->serverAddress & 0x3FFF) != 0x3FFF) { //Check logical and physical address separately. //This is done because some meters might send four bytes //when only two bytes are needed. uint32_t readLogical, readPhysical, logical, physical; dlms_getServerAddress(source, &readLogical, &readPhysical); dlms_getServerAddress(settings->serverAddress, &logical, &physical); if (readLogical != logical || readPhysical != physical) { return DLMS_ERROR_CODE_FALSE; } } } return DLMS_ERROR_CODE_OK; } int dlms_getAddress(int32_t value, uint32_t* address, int* size) { if (value < 0x80) { *address = (unsigned char)(value << 1 | 1); *size = 1; return 0; } else if (value < 0x4000) { *address = (uint16_t)((value & 0x3F80) << 2 | (value & 0x7F) << 1 | 1); *size = 2; } else if (value < 0x10000000) { *address = (uint32_t)((value & 0xFE00000) << 4 | (value & 0x1FC000) << 3 | (value & 0x3F80) << 2 | (value & 0x7F) << 1 | 1); *size = 4; } else { //Invalid address return DLMS_ERROR_CODE_INVALID_PARAMETER; } return DLMS_ERROR_CODE_OK; } /** * Get HDLC address as bytes. */ int dlms_getAddressBytes( uint32_t value, gxByteBuffer* bytes) { int ret, size; uint32_t address; if ((ret = dlms_getAddress(value, &address, &size)) != 0) { return ret; } if (size == 1) { bb_setUInt8(bytes, (unsigned char)address); } else if (size == 2) { bb_setUInt16(bytes, (uint16_t)address); } else if (size == 4) { bb_setUInt32(bytes, address); } else { return DLMS_ERROR_CODE_INVALID_PARAMETER; } return DLMS_ERROR_CODE_OK; } int dlms_getHdlcFrame( dlmsSettings* settings, int frame, gxByteBuffer* data, gxByteBuffer* reply) { unsigned char tmp[4], tmp2[4]; uint16_t crc; int ret; uint16_t frameSize; #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) uint32_t len = 0; #else uint16_t len = 0; #endif gxByteBuffer primaryAddress, secondaryAddress; bb_clear(reply); bb_attach(&primaryAddress, tmp, 0, 4); bb_attach(&secondaryAddress, tmp2, 0, 4); if (settings->server) { if ((ret = bb_capacity(&primaryAddress, 1)) == 0 && (ret = bb_capacity(&secondaryAddress, 4)) == 0) { if ((frame == 0x13 || frame == 0x3) && ((dlmsServerSettings*)settings)->pushClientAddress != 0) { ret = dlms_getAddressBytes(((dlmsServerSettings*)settings)->pushClientAddress, &primaryAddress); } else { ret = dlms_getAddressBytes(settings->clientAddress, &primaryAddress); } if (ret == 0) { ret = dlms_getAddressBytes(settings->serverAddress, &secondaryAddress); } } } else { ret = bb_capacity(&primaryAddress, 4); if (ret == 0) { ret = bb_capacity(&secondaryAddress, 1); } if (ret == 0 && (ret = dlms_getAddressBytes(settings->serverAddress, &primaryAddress)) == 0) { ret = dlms_getAddressBytes(settings->clientAddress, &secondaryAddress); } } // Add BOP if (ret == 0 && (ret = bb_capacity(reply, 8)) == 0) { ret = bb_setUInt8(reply, HDLC_FRAME_START_END); } frameSize = settings->maxInfoTX; if (data != NULL && data->position == 0) { frameSize -= 3; } // If no data if (ret == 0 && (data == NULL || data->size == 0)) { len = 0; ret = bb_setUInt8(reply, 0xA0); } else if (ret == 0 && data->size - data->position <= frameSize) { // Is last packet. len = bb_available(data); ret = bb_setUInt8(reply, (unsigned char)(0xA0 | (((7 + primaryAddress.size + secondaryAddress.size + len) >> 8) & 0x7))); } else if (ret == 0) { // More data to left. len = frameSize; ret = bb_setUInt8(reply, (unsigned char)(0xA8 | (((7 + primaryAddress.size + secondaryAddress.size + len) >> 8) & 0x7))); } // Frame len. if (ret == 0 && len == 0) { ret = bb_setUInt8(reply, (unsigned char)(5 + primaryAddress.size + secondaryAddress.size + len)); } else if (ret == 0) { if ((ret = bb_capacity(reply, (uint16_t)(11 + len))) == 0) { ret = bb_setUInt8(reply, (unsigned char)(7 + primaryAddress.size + secondaryAddress.size + len)); } } // Add primary address. if (ret == 0 && (ret = bb_set2(reply, &primaryAddress, 0, primaryAddress.size)) == 0) { // Add secondary address. ret = bb_set2(reply, &secondaryAddress, 0, secondaryAddress.size); } // Add frame ID. if (ret == 0 && frame == 0) { ret = bb_setUInt8(reply, getNextSend(settings, 1)); } else if (ret == 0) { ret = bb_setUInt8(reply, (unsigned char)frame); } if (ret == 0) { // Add header CRC. crc = countCRC(reply, 1, (reply->size - 1)); ret = bb_setUInt16(reply, crc); } if (ret == 0 && len != 0) { // Add data. if ((ret = bb_set2(reply, data, data->position, len)) == 0) { // Add data CRC. crc = countCRC(reply, 1, (reply->size - 1)); ret = bb_setUInt16(reply, crc); } } // Add EOP if (ret == 0) { ret = bb_setUInt8(reply, HDLC_FRAME_START_END); } // Remove sent data in server side. if (ret == 0 && settings->server) { if (data != NULL) { //If all data is sent. if (data->size == data->position) { ret = bb_clear(data); } else { //Remove sent data. ret = bb_move(data, data->position, 0, data->size - data->position); data->position = 0; } } } bb_clear(&primaryAddress); bb_clear(&secondaryAddress); return ret; } #endif //DLMS_IGNORE_HDLC #ifndef DLMS_IGNORE_PLC int dlms_getPlcFrame( dlmsSettings* settings, unsigned char creditFields, gxByteBuffer* data, gxByteBuffer* reply) { int frameSize = bb_available(data); //Max frame size is 124 bytes. if (frameSize > 134) { frameSize = 134; } //PAD Length. unsigned char padLen = (unsigned char)((36 - ((11 + frameSize) % 36)) % 36); bb_capacity(reply, 15 + frameSize + padLen); //Add STX bb_setUInt8(reply, 2); //Length. bb_setUInt8(reply, (unsigned char)(11 + frameSize)); //Length. bb_setUInt8(reply, 0x50); //Add Credit fields. bb_setUInt8(reply, creditFields); //Add source and target MAC addresses. bb_setUInt8(reply, (unsigned char)(settings->plcSettings.macSourceAddress >> 4)); int val = settings->plcSettings.macSourceAddress << 12; val |= settings->plcSettings.macDestinationAddress & 0xFFF; bb_setUInt16(reply, (uint16_t)val); bb_setUInt8(reply, padLen); //Control byte. bb_setUInt8(reply, DLMS_PLC_DATA_LINK_DATA_REQUEST); bb_setUInt8(reply, (unsigned char)settings->serverAddress); bb_setUInt8(reply, (unsigned char)settings->clientAddress); bb_set(reply, data->data + data->position, frameSize); data->position += frameSize; //Add padding. while (padLen != 0) { bb_setUInt8(reply, 0); --padLen; } //Checksum. uint16_t crc = countCRC(reply, 0, reply->size); bb_setUInt16(reply, crc); //Remove sent data in server side. if (settings->server) { if (data->size == data->position) { bb_clear(data); } else { bb_move(data, data->position, 0, data->size - data->position); data->position = 0; } } return 0; } // Reserved for internal use. const uint32_t CRCPOLY = 0xD3B6BA00; uint32_t dlms_countFCS24(unsigned char* buff, int index, int count) { unsigned char i, j; uint32_t crcreg = 0; for (j = 0; j < count; ++j) { unsigned char b = buff[index + j]; for (i = 0; i < 8; ++i) { crcreg >>= 1; if ((b & 0x80) != 0) { crcreg |= 0x80000000; } if ((crcreg & 0x80) != 0) { crcreg = crcreg ^ CRCPOLY; } b <<= 1; } } return crcreg >> 8; } int dlms_getMacHdlcFrame( dlmsSettings* settings, unsigned char frame, unsigned char creditFields, gxByteBuffer* data, gxByteBuffer* reply) { if (settings->maxInfoTX > 126) { settings->maxInfoTX = 86; }; int ret; gxByteBuffer tmp; BYTE_BUFFER_INIT(&tmp); //Lenght is updated last. bb_setUInt16(reply, 0); //Add Credit fields. bb_setUInt8(reply, creditFields); //Add source and target MAC addresses. bb_setUInt8(reply, (unsigned char)(settings->plcSettings.macSourceAddress >> 4)); int val = settings->plcSettings.macSourceAddress << 12; val |= settings->plcSettings.macDestinationAddress & 0xFFF; bb_setUInt16(reply, (uint16_t)val); if ((ret = dlms_getHdlcFrame(settings, frame, data, &tmp)) == 0) { unsigned char padLen = (unsigned char)((36 - ((10 + tmp.size) % 36)) % 36); bb_setUInt8(reply, padLen); bb_set(reply, tmp.data, tmp.size); //Add padding. while (padLen != 0) { bb_setUInt8(reply, 0); --padLen; } //Checksum. uint32_t crc = dlms_countFCS24(reply->data, 2, reply->size - 2 - padLen); bb_setUInt8(reply, (unsigned char)(crc >> 16)); bb_setUInt16(reply, (uint16_t)crc); //Add NC val = reply->size / 36; if (reply->size % 36 != 0) { ++val; } if (val == 1) { val = DLMS_PLC_MAC_SUB_FRAMES_ONE; } else if (val == 2) { val = DLMS_PLC_MAC_SUB_FRAMES_TWO; } else if (val == 3) { val = DLMS_PLC_MAC_SUB_FRAMES_THREE; } else if (val == 4) { val = DLMS_PLC_MAC_SUB_FRAMES_FOUR; } else if (val == 5) { val = DLMS_PLC_MAC_SUB_FRAMES_FIVE; } else if (val == 6) { val = DLMS_PLC_MAC_SUB_FRAMES_SIX; } else if (val == 7) { val = DLMS_PLC_MAC_SUB_FRAMES_SEVEN; } else { return DLMS_ERROR_CODE_OUTOFMEMORY; } ret = bb_setUInt16ByIndex(reply, 0, (uint16_t)val); } return ret; } int dlms_getMacFrame( dlmsSettings* settings, unsigned char frame, unsigned char creditFields, gxByteBuffer* data, gxByteBuffer* reply) { if (settings->interfaceType == DLMS_INTERFACE_TYPE_PLC) { return dlms_getPlcFrame(settings, creditFields, data, reply); } return dlms_getMacHdlcFrame(settings, frame, creditFields, data, reply); } #endif //DLMS_IGNORE_PLC int dlms_getDataFromFrame( gxByteBuffer* reply, gxReplyData* data, unsigned char hdlc) { #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) uint32_t offset = data->data.size; uint32_t cnt; #else uint16_t offset = data->data.size; uint16_t cnt; #endif if (data->packetLength < reply->position) { cnt = 0; } else { cnt = data->packetLength - reply->position; } if (cnt != 0) { int ret; if ((ret = bb_capacity(&data->data, offset + cnt)) != 0 || (ret = bb_set2(&data->data, reply, reply->position, cnt)) != 0) { return ret; } if (hdlc) { reply->position += 3; } } // Set position to begin of new data. data->data.position = offset; return 0; } #ifndef DLMS_IGNORE_HDLC int dlms_getHdlcData( unsigned char server, dlmsSettings* settings, gxByteBuffer* reply, gxReplyData* data, unsigned char* frame, unsigned char preEstablished, unsigned char first) { int ret; unsigned char ch; uint16_t eopPos; #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) uint32_t pos, frameLen = 0; uint32_t packetStartID = reply->position; #else uint16_t pos, frameLen = 0; uint16_t packetStartID = (uint16_t)reply->position; #endif uint16_t crc, crcRead; // If whole frame is not received yet. if (reply->size - reply->position < 9) { data->complete = 0; return 0; } data->complete = 1; // Find start of HDLC frame. for (pos = (uint16_t)reply->position; pos < reply->size; ++pos) { if ((ret = bb_getUInt8(reply, &ch)) != 0) { return ret; } if (ch == HDLC_FRAME_START_END) { packetStartID = pos; break; } } // Not a HDLC frame. // Sometimes meters can send some strange data between DLMS frames. if (reply->position == reply->size) { data->complete = 0; // Not enough data to parse; return 0; } if ((ret = bb_getUInt8(reply, frame)) != 0) { return ret; } if ((*frame & 0xF0) != 0xA0) { --reply->position; // If same data. return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); } // Check frame length. if ((*frame & 0x7) != 0) { frameLen = ((*frame & 0x7) << 8); } if ((ret = bb_getUInt8(reply, &ch)) != 0) { return ret; } // If not enough data. frameLen += ch; if ((reply->size - reply->position + 1) < frameLen) { data->complete = 0; reply->position = packetStartID; // Not enough data to parse; return 0; } eopPos = (uint16_t)(frameLen + packetStartID + 1); if ((ret = bb_getUInt8ByIndex(reply, eopPos, &ch)) != 0) { return ret; } if (ch != HDLC_FRAME_START_END) { reply->position -= 2; return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); } // Check addresses. ret = dlms_checkHdlcAddress(server, settings, reply, eopPos); if (ret != 0) { //If pre-established client address has change. if (ret == DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS) { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } else { if (ret == DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS && reply->position + 4 == reply->size) { data->packetLength = 0; bb_clear(reply); return ret; } if (ret == DLMS_ERROR_CODE_FALSE) { // If echo or reply to other meter. return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); } reply->position = packetStartID + 1; ret = dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); return ret; } } // Is there more data available. unsigned char moreData = (*frame & 0x8) != 0; // Get frame type. if ((ret = bb_getUInt8(reply, frame)) != 0) { return ret; } // Is there more data available. if (moreData) { data->moreData |= DLMS_DATA_REQUEST_TYPES_FRAME; } else { data->moreData = ((DLMS_DATA_REQUEST_TYPES)(data->moreData & ~DLMS_DATA_REQUEST_TYPES_FRAME)); } if (!preEstablished #ifndef DLMS_IGNORE_HDLC_CHECK && !checkFrame(settings, *frame) #endif //DLMS_IGNORE_HDLC_CHECK ) { reply->position = eopPos + 1; if (settings->server) { return DLMS_ERROR_CODE_INVALID_FRAME_NUMBER; } return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); } // Check that header CRC is correct. crc = countCRC(reply, packetStartID + 1, reply->position - packetStartID - 1); if ((ret = bb_getUInt16(reply, &crcRead)) != 0) { return ret; } if (crc != crcRead) { if (reply->size - reply->position > 8) { return dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); } #ifdef DLMS_DEBUG svr_notifyTrace("Invalid CRC. ", -1); #endif //DLMS_DEBUG return DLMS_ERROR_CODE_WRONG_CRC; } // Check that packet CRC match only if there is a data part. if (reply->position != packetStartID + frameLen + 1) { crc = countCRC(reply, packetStartID + 1, frameLen - 2); if ((ret = bb_getUInt16ByIndex(reply, packetStartID + frameLen - 1, &crcRead)) != 0) { return ret; } if (crc != crcRead) { #ifdef DLMS_DEBUG svr_notifyTrace("Invalid CRC. ", -1); #endif //DLMS_DEBUG return DLMS_ERROR_CODE_WRONG_CRC; } // Remove CRC and EOP from packet length. data->packetLength = eopPos - 2; } else { data->packetLength = eopPos - 2; } if (*frame != 0x13 && *frame != 0x3 && (*frame & HDLC_FRAME_TYPE_U_FRAME) == HDLC_FRAME_TYPE_U_FRAME) { // Get Eop if there is no data. if (reply->position == packetStartID + frameLen + 1) { // Get EOP. if ((ret = bb_getUInt8(reply, &ch)) != 0) { return ret; } } data->command = (DLMS_COMMAND)*frame; switch (data->command) { case DLMS_COMMAND_SNRM: case DLMS_COMMAND_UA: case DLMS_COMMAND_DISCONNECT_MODE: case DLMS_COMMAND_REJECTED: case DLMS_COMMAND_DISC: break; default: //Unknown command. return DLMS_ERROR_CODE_REJECTED; } } else if (*frame != 0x13 && *frame != 0x3 && (*frame & HDLC_FRAME_TYPE_S_FRAME) == HDLC_FRAME_TYPE_S_FRAME) { // If S-frame int tmp = (*frame >> 2) & 0x3; // If frame is rejected. if (tmp == HDLC_CONTROL_FRAME_REJECT) { return DLMS_ERROR_CODE_REJECTED; } else if (tmp == HDLC_CONTROL_FRAME_RECEIVE_NOT_READY) { return DLMS_ERROR_CODE_REJECTED; } else if (tmp == HDLC_CONTROL_FRAME_RECEIVE_READY) { } // Get Eop if there is no data. if (reply->position == packetStartID + frameLen + 1) { // Get EOP. if ((ret = bb_getUInt8(reply, &ch)) != 0) { return ret; } } } else { // I-frame // Get Eop if there is no data. if (reply->position == packetStartID + frameLen + 1) { // Get EOP. if ((ret = bb_getUInt8(reply, &ch)) != 0) { return ret; } if ((*frame & 0x1) == 0x1) { data->moreData = DLMS_DATA_REQUEST_TYPES_FRAME; } } else { dlms_checkLLCBytes(settings, reply); } } if (settings->server && (first || data->command == DLMS_COMMAND_SNRM)) { #ifndef DLMS_IGNORE_SERVER // Check is data send to this server. if (!svr_isTarget(settings, settings->serverAddress, settings->clientAddress)) { settings->serverAddress = 0; settings->clientAddress = 0; if (reply->size - reply->position > 8) { #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) uint32_t pos = reply->position; #else uint16_t pos = reply->position; #endif //!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__)) ret = dlms_getHdlcData(server, settings, reply, data, frame, preEstablished, first); if (settings->serverAddress != 0 && settings->clientAddress != 0) { reply->position = pos; } return ret; } return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } #endif //DLMS_IGNORE_SERVER } return DLMS_ERROR_CODE_OK; } #endif //DLMS_IGNORE_HDLC #ifndef DLMS_IGNORE_WRAPPER int dlms_checkWrapperAddress(dlmsSettings* settings, gxByteBuffer* buff, gxReplyData* data, gxReplyData* notify, unsigned char* isNotify) { int ret; uint16_t value; *isNotify = 0; if (settings->server) { if ((ret = bb_getUInt16(buff, &value)) != 0) { return ret; } // Check that client addresses match. if (settings->clientAddress != 0 && settings->clientAddress != value) { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } else { settings->clientAddress = value; } if ((ret = bb_getUInt16(buff, &value)) != 0) { return ret; } // Check that server addresses match. if (settings->serverAddress != 0 && settings->serverAddress != value) { return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS; } else { settings->serverAddress = value; } } else { if ((ret = bb_getUInt16(buff, &value)) != 0) { return ret; } // Check that server addresses match. if (settings->serverAddress != 0 && settings->serverAddress != value) { if (notify == NULL) { return DLMS_ERROR_CODE_INVALID_SERVER_ADDRESS; } notify->serverAddress = value; *isNotify = 1; } else { settings->serverAddress = value; } if ((ret = bb_getUInt16(buff, &value)) != 0) { return ret; } // Check that client addresses match. if (settings->clientAddress != 0 && settings->clientAddress != value) { if (notify == NULL) { return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } notify->clientAddress = value; *isNotify = 1; } else { settings->clientAddress = value; } } return DLMS_ERROR_CODE_OK; } #endif //DLMS_IGNORE_WRAPPER #ifndef DLMS_IGNORE_WRAPPER int dlms_getTcpData( dlmsSettings* settings, gxByteBuffer* buff, gxReplyData* data, gxReplyData* notify, unsigned char* isNotify) { int ret, pos; uint16_t value; *isNotify = 0; // If whole frame is not received yet. if (buff->size - buff->position < 8) { data->complete = 0; return DLMS_ERROR_CODE_OK; } pos = buff->position; data->complete = 0; if (notify != NULL) { notify->complete = 0; } while (buff->position != buff->size) { // Get version if ((ret = bb_getUInt16(buff, &value)) != 0) { return ret; } if (value == 1) { // Check TCP/IP addresses. if ((ret = dlms_checkWrapperAddress(settings, buff, data, notify, isNotify)) != 0) { return ret; } //If notify. if (notify != NULL && *isNotify != 0) { data = notify; } // Get length. if ((ret = bb_getUInt16(buff, &value)) != 0) { return ret; } data->complete = !((buff->size - buff->position) < value); if (!data->complete) { buff->position = pos; } else { data->packetLength = buff->position + value; } break; } else { --buff->position; } } return DLMS_ERROR_CODE_OK; } #endif //DLMS_IGNORE_WRAPPER #ifndef DLMS_IGNORE_WIRELESS_MBUS int dlms_getMBusData( dlmsSettings* settings, gxByteBuffer* buff, gxReplyData* data) { int ret; unsigned char len, ch; //L-field. if ((ret = bb_getUInt8(buff, &len)) != 0) { return ret; } //Some meters are counting length to frame size. if (buff->size < (unsigned char)(len - 1)) { data->complete = 0; buff->position = buff->position - 1; } else { //Some meters are counting length to frame size. if (buff->size < len) { --len; } data->packetLength = len; data->complete = 1; //C-field. if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } // DLMS_MBUS_COMMAND cmd = (DLMS_MBUS_COMMAND)ch; //M-Field. uint16_t manufacturerID; if ((ret = bb_getUInt16(buff, &manufacturerID)) != 0) { return ret; } //A-Field. uint32_t id; if ((ret = bb_getUInt32(buff, &id)) != 0) { return ret; } unsigned char meterVersion; if ((ret = bb_getUInt8(buff, &meterVersion)) != 0 || (ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } // DLMS_MBUS_METER_TYPE type = (DLMS_MBUS_METER_TYPE)ch; // CI-Field if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } // DLMS_MBUS_CONTROL_INFO ci = (DLMS_MBUS_CONTROL_INFO)ch; //Access number. unsigned char frameId; if ((ret = bb_getUInt8(buff, &frameId)) != 0) { return ret; } //State of the meter unsigned char state; if ((ret = bb_getUInt8(buff, &state)) != 0) { return ret; } //Configuration word. uint16_t configurationWord; if ((ret = bb_getUInt16(buff, &configurationWord)) != 0) { return ret; } //unsigned char encryptedBlocks = (unsigned char)(configurationWord >> 12); // DLMS_MBUS_ENCRYPTION_MODE encryption = (DLMS_MBUS_ENCRYPTION_MODE)(configurationWord & 7); if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } settings->clientAddress = ch; if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } settings->serverAddress = ch; } return ret; } #endif //DLMS_IGNORE_WIRELESS_MBUS #ifndef DLMS_IGNORE_PLC int dlms_getPlcData( dlmsSettings* settings, gxByteBuffer* buff, gxReplyData* data) { if (bb_available(buff) < 9) { data->complete = 0; return 0; } unsigned char ch; int ret; unsigned short pos; int packetStartID = buff->position; // Find STX. unsigned char stx; for (pos = (unsigned short)buff->position; pos < buff->size; ++pos) { if ((ret = bb_getUInt8(buff, &stx)) != 0) { return ret; } if (stx == 2) { packetStartID = pos; break; } } // Not a PLC frame. if (buff->position == buff->size) { // Not enough data to parse; data->complete = 0; buff->position = packetStartID; return 0; } unsigned char len; if ((ret = bb_getUInt8(buff, &len)) != 0) { return ret; } int index = buff->position; if (bb_available(buff) < len) { data->complete = 0; buff->position = buff->position - 2; } else { if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } //Credit fields. IC, CC, DC unsigned char credit; if ((ret = bb_getUInt8(buff, &credit)) != 0) { return ret; } //MAC Addresses. uint32_t mac; if ((ret = bb_getUInt24(buff, &mac)) != 0) { return ret; } //SA. short macSa = (short)(mac >> 12); //DA. short macDa = (short)(mac & 0xFFF); //PAD length. unsigned char padLen; if ((ret = bb_getUInt8(buff, &padLen)) != 0) { return ret; } if (buff->size < (unsigned short)(len + padLen + 2)) { data->complete = 0; buff->position = buff->position - index - 6; } else { //DL.Data.request if ((ret = bb_getUInt8(buff, &ch)) != 0) { return ret; } if (ch != DLMS_PLC_DATA_LINK_DATA_REQUEST) { //Parsing MAC LLC data failed. Invalid DataLink data request. return DLMS_ERROR_CODE_INVALID_COMMAND; } unsigned char da, sa; if ((ret = bb_getUInt8(buff, &da)) != 0 || (ret = bb_getUInt8(buff, &sa)) != 0) { return ret; } if (settings->server) { data->complete = (macDa == DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL || macDa == settings->plcSettings.macSourceAddress) && (macSa == DLMS_PLC_SOURCE_ADDRESS_INITIATOR || macSa == settings->plcSettings.macDestinationAddress); data->serverAddress = macDa; data->clientAddress = macSa; } else { data->complete = macDa == DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL || macDa == DLMS_PLC_SOURCE_ADDRESS_INITIATOR || macDa == settings->plcSettings.macDestinationAddress; data->clientAddress = macDa; data->serverAddress = macSa; } //Skip padding. if (data->complete) { uint16_t crcCount, crc; crcCount = countCRC(buff, 0, len + padLen); if ((ret = bb_getUInt16ByIndex(buff, len + padLen, &crc)) != 0) { return ret; } //Check CRC. if (crc != crcCount) { //Invalid data checksum. return DLMS_ERROR_CODE_WRONG_CRC; } data->packetLength = len; } } } return ret; } int dlms_getPlcHdlcData( dlmsSettings* settings, gxByteBuffer* buff, gxReplyData* data, unsigned char* frame) { if (bb_available(buff) < 2) { data->complete = 0; return 0; } int ret; *frame = 0; unsigned char frameLen; //SN field. uint16_t ns; if ((ret = bb_getUInt16(buff, &ns)) != 0) { return ret; } switch (ns) { case DLMS_PLC_MAC_SUB_FRAMES_ONE: frameLen = 36; break; case DLMS_PLC_MAC_SUB_FRAMES_TWO: frameLen = 2 * 36; break; case DLMS_PLC_MAC_SUB_FRAMES_THREE: frameLen = 3 * 36; break; case DLMS_PLC_MAC_SUB_FRAMES_FOUR: frameLen = 4 * 36; break; case DLMS_PLC_MAC_SUB_FRAMES_FIVE: frameLen = 5 * 36; break; case DLMS_PLC_MAC_SUB_FRAMES_SIX: frameLen = 6 * 36; break; case DLMS_PLC_MAC_SUB_FRAMES_SEVEN: frameLen = 7 * 36; break; default: return DLMS_ERROR_CODE_INVALID_PARAMETER; } if (bb_available(buff) < (unsigned char)(frameLen - 2)) { data->complete = 0; } else { unsigned long index = buff->position; //Credit fields. IC, CC, DC unsigned char credit; if ((ret = bb_getUInt8(buff, &credit)) != 0) { return ret; } //MAC Addresses. uint32_t mac; if ((ret = bb_getUInt24(buff, &mac)) != 0) { return ret; } //SA. unsigned short sa = (unsigned short)(mac >> 12); //DA. unsigned short da = (unsigned short)(mac & 0xFFF); if (settings->server) { data->complete = (da == DLMS_PLC_DESTINATION_ADDRESS_ALL_PHYSICAL || da == settings->plcSettings.macSourceAddress) && (sa == DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR || sa == settings->plcSettings.macDestinationAddress); data->serverAddress = da; data->clientAddress = sa; } else { data->complete = da == DLMS_PLC_HDLC_SOURCE_ADDRESS_INITIATOR || da == settings->plcSettings.macDestinationAddress; data->serverAddress = da; data->clientAddress = sa; } if (data->complete) { //PAD length. unsigned char padLen; if ((ret = bb_getUInt8(buff, &padLen)) != 0) { return ret; } if ((ret = dlms_getHdlcData(settings->server, settings, buff, data, frame, 0, 1)) != 0) { return ret; } dlms_getDataFromFrame(buff, data, dlms_useHdlc(settings->interfaceType)); buff->position = buff->position + padLen; uint32_t crcCount = dlms_countFCS24(buff->data, index, buff->position - index); uint32_t crc; if ((ret = bb_getUInt24ByIndex(buff, buff->position, &crc)) != 0) { return ret; } //Check CRC. if (crc != crcCount) { //Invalid data checksum. return DLMS_ERROR_CODE_WRONG_CRC; } data->packetLength = (uint16_t)(2 + buff->position - index); } else { buff->position = (uint16_t)(buff->position + frameLen - index - 4); } } return ret; } // Check is this PLC S-FSK message. // buff: Received data. // Returns True, if this is PLC message. unsigned char dlms_isPlcSfskData(gxByteBuffer* buff) { if (bb_available(buff) < 2) { return 0; } int ret; uint16_t len; if ((ret = bb_getUInt16ByIndex(buff, buff->position, &len)) != 0) { return (unsigned char)ret; } switch (len) { case DLMS_PLC_MAC_SUB_FRAMES_ONE: case DLMS_PLC_MAC_SUB_FRAMES_TWO: case DLMS_PLC_MAC_SUB_FRAMES_THREE: case DLMS_PLC_MAC_SUB_FRAMES_FOUR: case DLMS_PLC_MAC_SUB_FRAMES_FIVE: case DLMS_PLC_MAC_SUB_FRAMES_SIX: case DLMS_PLC_MAC_SUB_FRAMES_SEVEN: return 1; default: return 0; } } #endif //DLMS_IGNORE_PLC int dlms_getDataFromBlock(gxByteBuffer* data, uint16_t index) { #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) uint32_t pos, len = data->position - index; #else uint16_t pos, len = data->position - index; #endif if (data->size == data->position) { bb_clear(data); return 0; } pos = data->position; bb_move(data, data->position, data->position - len, data->size - data->position); data->position = pos - len; return 0; } int dlms_receiverReady( dlmsSettings* settings, DLMS_DATA_REQUEST_TYPES type, gxByteBuffer* reply) { int ret; DLMS_COMMAND cmd; message tmp; gxByteBuffer bb; bb_clear(reply); if (type == DLMS_DATA_REQUEST_TYPES_NONE) { return DLMS_ERROR_CODE_INVALID_PARAMETER; } #ifndef DLMS_IGNORE_HDLC // Get next frame. if ((type & DLMS_DATA_REQUEST_TYPES_FRAME) != 0) { unsigned char id = getReceiverReady(settings); switch (settings->interfaceType) { #ifndef DLMS_IGNORE_PLC case DLMS_INTERFACE_TYPE_PLC_HDLC: ret = dlms_getMacHdlcFrame(settings, id, 0, NULL, reply); break; #endif //DLMS_IGNORE_PLC #ifndef DLMS_IGNORE_HDLC case DLMS_INTERFACE_TYPE_HDLC: ret = dlms_getHdlcFrame(settings, id, NULL, reply); break; #endif //DLMS_IGNORE_HDLC default: ret = DLMS_ERROR_CODE_INVALID_PARAMETER; } return ret; } #endif //DLMS_IGNORE_HDLC // Get next block. if (settings->useLogicalNameReferencing) { if (settings->server) { cmd = DLMS_COMMAND_GET_RESPONSE; } else { cmd = DLMS_COMMAND_GET_REQUEST; } } else { #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) if (settings->server) { cmd = DLMS_COMMAND_READ_RESPONSE; } else { cmd = DLMS_COMMAND_READ_REQUEST; } #else return DLMS_ERROR_CODE_INVALID_PARAMETER; #endif //#if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) } #ifdef DLMS_IGNORE_MALLOC unsigned char buff[40]; bb_attach(&bb, buff, 0, sizeof(buff)); #else BYTE_BUFFER_INIT(&bb); #endif //DLMS_IGNORE_MALLOC if (settings->useLogicalNameReferencing) { bb_setUInt32(&bb, settings->blockIndex); } else { bb_setUInt16(&bb, (uint16_t)settings->blockIndex); } ++settings->blockIndex; #ifdef DLMS_IGNORE_MALLOC gxByteBuffer* p[] = { reply }; mes_attach(&tmp, p, 1); #else mes_init(&tmp); #endif //DLMS_IGNORE_MALLOC if (settings->useLogicalNameReferencing) { gxLNParameters p; params_initLN(&p, settings, 0, cmd, DLMS_GET_COMMAND_TYPE_NEXT_DATA_BLOCK, &bb, NULL, 0xFF, DLMS_COMMAND_NONE, 0, 0); #ifdef DLMS_IGNORE_MALLOC p.serializedPdu = &bb; #endif //DLMS_IGNORE_MALLOC ret = dlms_getLnMessages(&p, &tmp); } else { #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) gxSNParameters p; params_initSN(&p, settings, cmd, 1, DLMS_VARIABLE_ACCESS_SPECIFICATION_BLOCK_NUMBER_ACCESS, &bb, NULL, DLMS_COMMAND_NONE); ret = dlms_getSnMessages(&p, &tmp); #else ret = DLMS_ERROR_CODE_INVALID_PARAMETER; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) } #ifndef DLMS_IGNORE_MALLOC if (ret == 0) { ret = bb_set2(reply, (gxByteBuffer*)tmp.data[0], 0, tmp.data[0]->size); } bb_clear(&bb); mes_clear(&tmp); #endif //DLMS_IGNORE_MALLOC return ret; } #ifndef DLMS_IGNORE_WRAPPER int dlms_getWrapperFrame( dlmsSettings* settings, DLMS_COMMAND command, gxByteBuffer* data, gxByteBuffer* reply) { int ret; if ((ret = bb_clear(reply)) == 0 && (ret = bb_capacity(reply, 8 + data->size - data->position)) == 0 && // Add version. (ret = bb_setUInt16(reply, 1)) == 0) { if (settings->server) { if ((ret = bb_setUInt16(reply, (unsigned short)settings->serverAddress)) == 0) { if (((dlmsServerSettings*)settings)->pushClientAddress != 0 && (command == DLMS_COMMAND_DATA_NOTIFICATION || command == DLMS_COMMAND_EVENT_NOTIFICATION)) { ret = bb_setUInt16(reply, ((dlmsServerSettings*)settings)->pushClientAddress); } else { ret = bb_setUInt16(reply, settings->clientAddress); } } } else { if ((ret = bb_setUInt16(reply, settings->clientAddress)) == 0) { ret = bb_setUInt16(reply, (unsigned short)settings->serverAddress); } } // Data length. if (ret == 0 && (ret = bb_setUInt16(reply, (uint16_t)data->size)) == 0) { // Data ret = bb_set2(reply, data, data->position, data->size - data->position); } // Remove sent data in server side. if (ret == 0 && settings->server) { if (data->size == data->position) { ret = bb_clear(data); } else { ret = bb_move(data, data->position, 0, data->size - data->position); data->position = 0; } } } return ret; } #endif //DLMS_IGNORE_WRAPPER int dlms_verifyInvokeId(dlmsSettings* settings, gxReplyData* reply) { if (settings->autoIncreaseInvokeID && reply->invokeId != dlms_getInvokeIDPriority(settings, 0)) { //Invalid invoke ID. return DLMS_ERROR_CODE_INVALID_INVOKE_ID; } return 0; } int dlms_handleGetResponse( dlmsSettings* settings, gxReplyData* reply, uint16_t index) { int ret; uint16_t count; unsigned char ch; uint32_t number; short type; // Get type. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } type = ch; // Get invoke ID and priority. if ((ret = bb_getUInt8(&reply->data, &reply->invokeId)) != 0) { return ret; } if ((ret = dlms_verifyInvokeId(settings, reply)) != 0) { return ret; } // Response normal if (type == 1) { // Result if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } if (ch != 0) { if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } return ch; } ret = dlms_getDataFromBlock(&reply->data, 0); } else if (type == 2) { // GetResponsewithDataBlock // Is Last block. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } if (ch == 0) { reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData | DLMS_DATA_REQUEST_TYPES_BLOCK); } else { reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData & ~DLMS_DATA_REQUEST_TYPES_BLOCK); } // Get Block number. if ((ret = bb_getUInt32(&reply->data, &number)) != 0) { return ret; } // If meter's block index is zero based or Actaris is read. // Actaris SL7000 might return wrong block index sometimes. // It's not reseted to 1. if (number != 1 && settings->blockIndex == 1) { settings->blockIndex = number; } else if (number != settings->blockIndex) { return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID; } // Get status. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } if (ch != 0) { if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } return ch; } else { // Get data size. if ((ret = hlp_getObjectCount2(&reply->data, &count)) != 0) { return ret; } // if whole block is read. if ((reply->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0) { // Check Block length. if (count > (uint16_t)(bb_available(&reply->data))) { return DLMS_ERROR_CODE_OUTOFMEMORY; } reply->command = DLMS_COMMAND_NONE; } if (count == 0) { // If meter sends empty data block. reply->data.size = index; } else { if ((ret = dlms_getDataFromBlock(&reply->data, index)) != 0) { return ret; } } // If last packet and data is not try to peek. if (reply->moreData == DLMS_DATA_REQUEST_TYPES_NONE) { if (!reply->peek) { reply->data.position = 0; resetBlockIndex(settings); } } } } else if (type == 3) { return DLMS_ERROR_CODE_FALSE; } else { //Invalid Get response. return DLMS_ERROR_CODE_INVALID_PARAMETER; } return ret; } int handleWriteResponse(gxReplyData* data) { unsigned char ch; int ret; uint16_t count, pos; if (hlp_getObjectCount2(&data->data, &count) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } for (pos = 0; pos != count; ++pos) { if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } if (ch != 0) { if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } return ch; } } return DLMS_ERROR_CODE_OK; } int dlms_handleWriteResponse( gxReplyData* data) { unsigned char ch; int ret; uint16_t pos, count; if (hlp_getObjectCount2(&data->data, &count) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } for (pos = 0; pos != count; ++pos) { if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } if (ch != 0) { if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } return ch; } } return DLMS_ERROR_CODE_OK; } int dlms_getValueFromData(dlmsSettings* settings, gxReplyData* reply) { uint16_t index; int ret; #if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) int pos; dlmsVARIANT_PTR tmp; #endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) dlmsVARIANT value; gxDataInfo info; di_init(&info); var_init(&value); if (reply->dataValue.vt == DLMS_DATA_TYPE_ARRAY) { info.type = DLMS_DATA_TYPE_ARRAY; info.count = (uint16_t)reply->totalCount; info.index = (uint16_t)reply->data.size; } index = (uint16_t)(reply->data.position); reply->data.position = reply->readPosition; if ((ret = dlms_getData(&reply->data, &info, &value)) != 0) { var_clear(&value); return ret; } // If new data. if (value.vt != DLMS_DATA_TYPE_NONE) { if (value.vt != DLMS_DATA_TYPE_ARRAY && value.vt != DLMS_DATA_TYPE_STRUCTURE) { reply->dataType = info.type; reply->dataValue = value; reply->totalCount = 0; if (reply->command == DLMS_COMMAND_DATA_NOTIFICATION) { reply->readPosition = reply->data.position; } } else { if (reply->dataValue.vt == DLMS_DATA_TYPE_NONE) { reply->dataValue = value; } else { #if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) for (pos = 0; pos != value.Arr->size; ++pos) { if ((ret = va_getByIndex(value.Arr, pos, &tmp)) != 0) { return ret; } va_push(reply->dataValue.Arr, tmp); } #endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) } } reply->readPosition = reply->data.position; // Element count. reply->totalCount = info.count; } else if (info.complete && reply->command == DLMS_COMMAND_DATA_NOTIFICATION) { // If last item is null. This is a special case. reply->readPosition = reply->data.position; } reply->data.position = index; // If last data frame of the data block is read. if (reply->command != DLMS_COMMAND_DATA_NOTIFICATION && info.complete && reply->moreData == DLMS_DATA_REQUEST_TYPES_NONE) { // If all blocks are read. resetBlockIndex(settings); reply->data.position = 0; } return 0; } int dlms_readResponseDataBlockResult( dlmsSettings* settings, gxReplyData* reply, uint16_t index) { int ret; uint16_t number; uint16_t blockLength; unsigned char lastBlock; if ((ret = bb_getUInt8(&reply->data, &lastBlock)) != 0) { return ret; } // Get Block number. if ((ret = bb_getUInt16(&reply->data, &number)) != 0) { return ret; } if (hlp_getObjectCount2(&reply->data, &blockLength) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } // Is Last block. if (!lastBlock) { reply->moreData |= DLMS_DATA_REQUEST_TYPES_BLOCK; } else { reply->moreData &= ~DLMS_DATA_REQUEST_TYPES_BLOCK; } // If meter's block index is zero based. if (number != 1 && settings->blockIndex == 1) { settings->blockIndex = number; } if (number != settings->blockIndex) { //Invalid Block number return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID; } // If whole block is not read. if ((reply->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0) { dlms_getDataFromBlock(&reply->data, index); return DLMS_ERROR_CODE_FALSE; } if (blockLength != bb_available(&reply->data)) { //Invalid block length. return DLMS_ERROR_CODE_DATA_BLOCK_UNAVAILABLE; } reply->command = DLMS_COMMAND_NONE; dlms_getDataFromBlock(&reply->data, index); reply->totalCount = 0; // If last packet and data is not try to peek. if (reply->moreData == DLMS_DATA_REQUEST_TYPES_NONE) { resetBlockIndex(settings); } return ret; } int dlms_handleReadResponse( dlmsSettings* settings, gxReplyData* reply, uint16_t index) { int ret; unsigned char ch; uint16_t number; uint16_t pos, cnt = reply->totalCount; DLMS_SINGLE_READ_RESPONSE type; variantArray values; // If we are reading value first time or block is handed. unsigned char first = reply->totalCount == 0 || reply->commandType == DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT; if (first) { if (hlp_getObjectCount2(&reply->data, &cnt) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } reply->totalCount = cnt; } if (cnt != 1) { //Parse data after all data is received when readlist is used. if (reply->moreData != DLMS_DATA_REQUEST_TYPES_NONE) { if ((ret = dlms_getDataFromBlock(&reply->data, 0)) == 0) { ret = DLMS_ERROR_CODE_FALSE; } return ret; } if (!first) { reply->data.position = 0; first = 1; } } va_init(&values); for (pos = 0; pos != cnt; ++pos) { // Get status code. Status code is begin of each PDU. if (first) { if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } reply->commandType = ch; type = (DLMS_SINGLE_READ_RESPONSE)ch; } else { type = (DLMS_SINGLE_READ_RESPONSE)reply->commandType; } switch (type) { case DLMS_SINGLE_READ_RESPONSE_DATA: if (cnt == 1) { ret = dlms_getDataFromBlock(&reply->data, 0); } else { // If read multiple items. reply->readPosition = reply->data.position; dlms_getValueFromData(settings, reply); reply->data.position = reply->readPosition; va_push(&values, &reply->dataValue); var_clear(&reply->dataValue); } break; case DLMS_SINGLE_READ_RESPONSE_DATA_ACCESS_ERROR: // Get error code. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } return ch; case DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT: if ((ret = dlms_readResponseDataBlockResult(settings, reply, index)) != 0) { return ret; } break; case DLMS_SINGLE_READ_RESPONSE_BLOCK_NUMBER: // Get Block number. if ((ret = bb_getUInt16(&reply->data, &number)) != 0) { return ret; } if (number != settings->blockIndex) { //Invalid Block number return DLMS_ERROR_CODE_DATA_BLOCK_NUMBER_INVALID; } ++settings->blockIndex; reply->moreData |= DLMS_DATA_REQUEST_TYPES_BLOCK; break; default: //HandleReadResponse failed. Invalid tag. return DLMS_ERROR_CODE_INVALID_TAG; } } if (values.size != 0) { #ifndef DLMS_IGNORE_MALLOC reply->dataValue.vt = DLMS_DATA_TYPE_ARRAY; reply->dataValue.Arr = gxmalloc(sizeof(variantArray)); va_init(reply->dataValue.Arr); reply->dataValue.Arr->capacity = values.capacity; va_copyArray(reply->dataValue.Arr, &values); gxfree(values.data); #endif //DLMS_IGNORE_MALLOC } if (cnt != 1) { return DLMS_ERROR_CODE_FALSE; } return 0; } int dlms_handleMethodResponse( dlmsSettings* settings, gxReplyData* data) { int ret; unsigned char ch, type; // Get type. if ((ret = bb_getUInt8(&data->data, &type)) != 0) { return ret; } // Get invoke ID and priority. if ((ret = bb_getUInt8(&data->data, &data->invokeId)) != 0) { return ret; } if ((ret = dlms_verifyInvokeId(settings, data)) != 0) { return ret; } //Action-Response-Normal if (type == 1) { //Get Action-Result if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } if (ch != 0) { return ch; } resetBlockIndex(settings); // Get data if exists. Some meters do not return here anything. if (data->data.position < data->data.size) { //Get-Data-Result. if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } //If data. if (ch == 0) { return dlms_getDataFromBlock(&data->data, 0); } else if (ch == 1) //Data-Access-Result { //Get Data-Access-Result if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } if (ch != 0) { if (ch == 9) { //Handle Texas Instrument missing byte here. if ((ret = bb_getUInt8ByIndex(&data->data, data->data.position, &ch)) != 0) { return ret; } if (ch == 16) { --data->data.position; return dlms_getDataFromBlock(&data->data, 0); } } if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } return ch; } return dlms_getDataFromBlock(&data->data, 0); } else { return DLMS_ERROR_CODE_INVALID_TAG; } } } //Action-Response-With-Pblock else if (type == 2) { return DLMS_ERROR_CODE_INVALID_COMMAND; } // Action-Response-With-List. else if (type == 3) { return DLMS_ERROR_CODE_INVALID_COMMAND; } //Action-Response-Next-Pblock else if (type == 4) { return DLMS_ERROR_CODE_INVALID_COMMAND; } else { return DLMS_ERROR_CODE_INVALID_COMMAND; } return DLMS_ERROR_CODE_OK; } int dlms_handlePush(gxReplyData* reply) { unsigned char ch; uint32_t ul; int ret; uint16_t index = (uint16_t)(reply->data.position - 1); // Is last block if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } if ((ch & 0x80) == 0) { reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData | DLMS_DATA_REQUEST_TYPES_BLOCK); } else { reply->moreData = (DLMS_DATA_REQUEST_TYPES)(reply->moreData & ~DLMS_DATA_REQUEST_TYPES_BLOCK); } // Get block number sent. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } // Get block number acknowledged if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } // Get APU tag. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } // Addl fields if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } // Data-Notification if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } if ((ch & 0x0F) == 0) { //Invalid data. return DLMS_ERROR_CODE_INVALID_PARAMETER; } // Long-Invoke-Id-And-Priority if ((ret = bb_getUInt32(&reply->data, &ul)) != 0) { return ret; } // Get date time and skip it if used. if ((ret = bb_getUInt8(&reply->data, &ch)) != 0) { return ret; } if (ch != 0) { reply->data.position = reply->data.position + ch; } return dlms_getDataFromBlock(&reply->data, index); } int dlms_handleSetResponse( dlmsSettings* settings, gxReplyData* data) { unsigned char ch, type; int ret; if ((ret = bb_getUInt8(&data->data, &type)) != 0) { return ret; } //Invoke ID and priority. if ((ret = bb_getUInt8(&data->data, &data->invokeId)) != 0) { return ret; } if ((ret = dlms_verifyInvokeId(settings, data)) != 0) { return ret; } // SetResponseNormal if (type == DLMS_SET_RESPONSE_TYPE_NORMAL) { ret = bb_getUInt8(&data->data, &ch); if (ret == 0 && ch != 0) { return ch; } } else if (type == DLMS_SET_RESPONSE_TYPE_DATA_BLOCK || type == DLMS_SET_RESPONSE_TYPE_LAST_DATA_BLOCK) { uint32_t tmp; ret = bb_getUInt32(&data->data, &tmp); } else if (type == DLMS_SET_RESPONSE_TYPE_WITH_LIST) { uint16_t pos, cnt; if (hlp_getObjectCount2(&data->data, &cnt) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } for (pos = 0; pos != cnt; ++pos) { if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { break; } if (ch != 0) { ret = ch; } } } else { //Invalid data type. ret = DLMS_ERROR_CODE_INVALID_PARAMETER; } return ret; } int dlms_changeType2( dlmsVARIANT* value, DLMS_DATA_TYPE type, dlmsVARIANT* newValue) { gxByteBuffer bb; if (value->byteArr != NULL) { bb_attach(&bb, value->byteArr->data, value->byteArr->size, value->byteArr->size); bb.position = value->byteArr->position; } return dlms_changeType(&bb, type, newValue); } int dlms_changeType( gxByteBuffer* value, DLMS_DATA_TYPE type, dlmsVARIANT* newValue) { int ret; gxDataInfo info; di_init(&info); #ifndef DLMS_IGNORE_MALLOC var_clear(newValue); #endif //DLMS_IGNORE_MALLOC if (value->size == 0) { if (type == DLMS_DATA_TYPE_STRING || type == DLMS_DATA_TYPE_STRING_UTF8) { newValue->vt = type; } return DLMS_ERROR_CODE_OK; } if (type == DLMS_DATA_TYPE_NONE) { #if !defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC) char* tmp = bb_toHexString(value); newValue->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); BYTE_BUFFER_INIT(newValue->strVal); bb_addString(newValue->strVal, tmp); gxfree(tmp); newValue->vt = DLMS_DATA_TYPE_STRING; return DLMS_ERROR_CODE_OK; #else return DLMS_ERROR_CODE_INVALID_PARAMETER; #endif //!defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC) } info.type = type; if ((ret = dlms_getData(value, &info, newValue)) != 0) { return ret; } value->position = 0; if (!info.complete) { return DLMS_ERROR_CODE_OUTOFMEMORY; } if (type == DLMS_DATA_TYPE_OCTET_STRING && newValue->vt == DLMS_DATA_TYPE_OCTET_STRING) { #if !defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC) char* tmp = bb_toHexString(value); newValue->strVal = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); BYTE_BUFFER_INIT(newValue->strVal); bb_addString(newValue->strVal, tmp); gxfree(tmp); newValue->vt = DLMS_DATA_TYPE_STRING; #else return DLMS_ERROR_CODE_INVALID_PARAMETER; #endif //!defined(GX_DLMS_MICROCONTROLLER) && !defined(DLMS_IGNORE_MALLOC) } return 0; } /** * Handle data notification get data from block and/or update error status. * * @param reply * Received data from the client. */ int dlms_handleDataNotification( dlmsSettings* settings, gxReplyData* reply) { uint32_t id; int ret; unsigned char len; gxByteBuffer tmp; dlmsVARIANT t; uint16_t start = (uint16_t)(reply->data.position - 1); // Get invoke id. if ((ret = bb_getUInt32(&reply->data, &id)) != 0) { return ret; } // Get date time. #ifdef DLMS_USE_EPOCH_TIME reply->time = 0; #else memset(&reply->time, 0, sizeof(struct tm)); #endif //DLMS_USE_EPOCH_TIME if ((ret = bb_getUInt8(&reply->data, &len)) != 0) { return ret; } if (len != 0) { #ifndef DLMS_IGNORE_MALLOC var_init(&t); #else gxtime tm; GX_DATETIME(t) = &tm; #endif //DLMS_IGNORE_MALLOC unsigned char buff[12]; bb_attach(&tmp, buff, 0, sizeof(buff)); if ((ret = bb_set2(&tmp, &reply->data, reply->data.position, len)) != 0 || (ret = dlms_changeType(&tmp, DLMS_DATA_TYPE_DATETIME, &t)) != 0) { return ret; } #ifdef DLMS_USE_EPOCH_TIME #ifndef DLMS_IGNORE_MALLOC reply->time = t.dateTime->value; #else reply->time = tm.value; #endif //DLMS_IGNORE_MALLOC #else #ifndef DLMS_IGNORE_MALLOC reply->time = t.dateTime->value; #else reply->time = ((gxtime*)t.pVal)->value; #endif //DLMS_IGNORE_MALLOC #endif // DLMS_USE_EPOCH_TIME } if ((ret = dlms_getDataFromBlock(&reply->data, start)) != 0) { return ret; } #ifndef DLMS_IGNORE_MALLOC return dlms_getValueFromData(settings, reply); #else //Client app must do the data parsing if malloc is not used. return 0; #endif //DLMS_IGNORE_MALLOC } /** * Handle General block transfer message. * * @param settings * DLMS settings. * @param data * received data. */ int dlms_handleGbt( dlmsSettings* settings, gxReplyData* data) { int ret; unsigned char bc; uint16_t bn, bna; uint16_t index = (uint16_t)(data->data.position - 1); if ((ret = bb_getUInt8(&data->data, &bc)) != 0) { return ret; } // Is streaming active. data->streaming = (bc & 0x40) != 0; data->windowSize = (bc & 0x3F); // Block number. if ((ret = bb_getUInt16(&data->data, &bn)) != 0) { return ret; } // Block number acknowledged. if ((ret = bb_getUInt16(&data->data, &bna)) != 0) { return ret; } // Remove existing data when first block is received. if (bn == 1) { index = 0; } else if (bna != settings->blockIndex - 1) { // If this block is already received. data->data.size = index; data->command = DLMS_COMMAND_NONE; return 0; } data->blockNumber = bn; // Block number acknowledged. data->blockNumberAck = bna; data->command = DLMS_COMMAND_NONE; uint16_t len; if (hlp_getObjectCount2(&data->data, &len) != 0) { return DLMS_ERROR_CODE_OUTOFMEMORY; } if (len != (data->data.size - data->data.position)) { data->complete = 0; return 0; } if ((ret = dlms_getDataFromBlock(&data->data, index)) != 0) { return ret; } // Is Last block. if ((bc & 0x80) == 0) { data->moreData |= DLMS_DATA_REQUEST_TYPES_GBT; } else { data->moreData &= ~DLMS_DATA_REQUEST_TYPES_GBT; if (data->data.size != 0) { data->data.position = 0; if ((ret = dlms_getPdu(settings, data, 0)) != 0) { return ret; } // Get data if all data is read or we want to peek data. if (data->data.position != data->data.size && ( #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) data->command == DLMS_COMMAND_READ_RESPONSE || #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) data->command == DLMS_COMMAND_GET_RESPONSE) && (data->moreData == DLMS_DATA_REQUEST_TYPES_NONE || data->peek)) { data->data.position = 0; ret = dlms_getValueFromData(settings, data); } } } return ret; } #if !defined(DLMS_IGNORE_SERVER) int dlms_handleGloDedRequest(dlmsSettings* settings, gxReplyData* data) { int ret = 0; #ifdef DLMS_IGNORE_HIGH_GMAC ret = DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else DLMS_SECURITY_SUITE suite; uint64_t invocationCounter; // If all frames are read. if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0) { unsigned char ch; DLMS_SECURITY security; --data->data.position; unsigned char emptySourceSystemTile; emptySourceSystemTile = memcmp(settings->sourceSystemTitle, EMPTY_SYSTEM_TITLE, 8) == 0; if (dlms_useDedicatedKey(settings) && (settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0) { if ((ret = cip_decrypt(&settings->cipher, settings->sourceSystemTitle, settings->cipher.dedicatedKey, &data->data, &security, &suite, &invocationCounter)) != 0) { return ret; } } //If pre-set connection is made. else if (dlms_usePreEstablishedConnection(settings) && emptySourceSystemTile) { #ifndef DLMS_IGNORE_SERVER if (settings->server && settings->connected == DLMS_CONNECTION_STATE_NONE && !data->preEstablished) { // Check is data send to this server. if (!svr_isTarget(settings, settings->serverAddress, settings->clientAddress)) { if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) == 0) { settings->serverAddress = settings->clientAddress = 0; } return DLMS_ERROR_CODE_INVALID_CLIENT_ADDRESS; } if ((ret = svr_connected((dlmsServerSettings*)settings)) != 0) { return ret; } } #endif //DLMS_IGNORE_SERVER if ((ret = cip_decrypt(&settings->cipher, #ifndef DLMS_IGNORE_MALLOC settings->preEstablishedSystemTitle->data, &settings->cipher.blockCipherKey, #else settings->preEstablishedSystemTitle, settings->cipher.blockCipherKey, #endif //DLMS_IGNORE_MALLOC & data->data, &security, &suite, &invocationCounter)) != 0) { return ret; } if (data->preEstablished == 0) { data->preEstablished = 1; } } else { if ((ret = cip_decrypt(&settings->cipher, #ifndef DLMS_IGNORE_MALLOC settings->sourceSystemTitle, &settings->cipher.blockCipherKey, #else settings->sourceSystemTitle, settings->cipher.blockCipherKey, #endif //DLMS_IGNORE_MALLOC & data->data, &security, &suite, &invocationCounter)) != 0) { return ret; } } //If IC value is wrong. if (settings->expectedInvocationCounter != NULL) { if (invocationCounter < *settings->expectedInvocationCounter) { return DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL; } //Update IC. #ifdef DLMS_COSEM_INVOCATION_COUNTER_SIZE64 * settings->expectedInvocationCounter = (1 + invocationCounter); #else * settings->expectedInvocationCounter = (uint32_t)(1 + invocationCounter); #endif //DLMS_COSEM_INVOCATION_COUNTER_SIZE64 } // Get command. if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } data->encryptedCommand = data->command; data->command = (DLMS_COMMAND)ch; } else { data->data.position -= 1; } #endif //DLMS_IGNORE_HIGH_GMAC return ret; } #endif // !defined(DLMS_IGNORE_SERVER) #if !defined(DLMS_IGNORE_CLIENT) int dlms_handleGloDedResponse(dlmsSettings* settings, gxReplyData* data, uint32_t index) { #ifdef DLMS_IGNORE_HIGH_GMAC return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else int ret = 0; DLMS_SECURITY_SUITE suite; uint64_t invocationCounter; if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0) { DLMS_SECURITY security; --data->data.position; data->data.position = index; gxByteBuffer bb; bb_attach(&bb, data->data.data + index, bb_available(&data->data), bb_getCapacity(&data->data)); if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0 && dlms_useDedicatedKey(settings)) { if ((ret = cip_decrypt(&settings->cipher, settings->sourceSystemTitle, settings->cipher.dedicatedKey, &bb, &security, &suite, &invocationCounter)) != 0) { return ret; } } else { if ((ret = cip_decrypt(&settings->cipher, settings->sourceSystemTitle, #ifndef DLMS_IGNORE_MALLOC & settings->cipher.blockCipherKey, #else settings->cipher.blockCipherKey, #endif //DLMS_IGNORE_MALLOC & bb, &security, &suite, &invocationCounter)) != 0) { return ret; } } data->data.size = bb.size + index; //If target is sending data ciphered using different security policy. if (settings->cipher.security != security) { return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR; } if (settings->expectedInvocationCounter != NULL) { //If data is ciphered using invalid invocation counter value. if (invocationCounter != *settings->expectedInvocationCounter) { return DLMS_ERROR_CODE_INVOCATION_COUNTER_TOO_SMALL; } #ifdef DLMS_COSEM_INVOCATION_COUNTER_SIZE64 * settings->expectedInvocationCounter = (1 + invocationCounter); #else * settings->expectedInvocationCounter = (uint32_t)(1 + invocationCounter); #endif //DLMS_COSEM_INVOCATION_COUNTER_SIZE64 } data->command = DLMS_COMMAND_NONE; ret = dlms_getPdu(settings, data, 0); data->cipherIndex = (uint16_t)data->data.size; } return ret; #endif //DLMS_IGNORE_HIGH_GMAC } #endif //!defined(DLMS_IGNORE_CLIENT) #if !defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC) int dlms_handleGeneralCiphering( dlmsSettings* settings, gxReplyData* data) { #ifdef DLMS_IGNORE_HIGH_GMAC return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else unsigned char ch; int ret; // If all frames are read. if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0) { --data->data.position; DLMS_SECURITY security; DLMS_SECURITY_SUITE suite; uint64_t invocationCounter; if ((ret = cip_decrypt(&settings->cipher, settings->sourceSystemTitle, #ifndef DLMS_IGNORE_MALLOC & settings->cipher.blockCipherKey, #else settings->cipher.blockCipherKey, #endif //DLMS_IGNORE_MALLOC & data->data, &security, &suite, &invocationCounter)) != 0) { return ret; } // Get command if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } data->command = DLMS_COMMAND_NONE; if (security != DLMS_SECURITY_NONE) { if ((ret = dlms_getPdu(settings, data, 0)) != 0) { return ret; } } } return 0; #endif //DLMS_IGNORE_HIGH_GMAC } #endif //!defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC) #if !defined(DLMS_IGNORE_SERVER) int32_t dlms_handleConfirmedServiceError(gxByteBuffer* data) { int32_t ret; unsigned char ch; if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } DLMS_CONFIRMED_SERVICE_ERROR service = (DLMS_CONFIRMED_SERVICE_ERROR)ch; if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } DLMS_SERVICE_ERROR type = (DLMS_SERVICE_ERROR)ch; if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } ret = service; ret <<= 16; ret |= DLMS_ERROR_TYPE_CONFIRMED_SERVICE_ERROR; ret |= type << 8; ret |= ch; return ret; } int dlms_handleExceptionResponse(gxByteBuffer* data) { int ret; unsigned char ch; // DLMS_EXCEPTION_STATE_ERROR state; DLMS_EXCEPTION_SERVICE_ERROR error; if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } // state = (DLMS_EXCEPTION_STATE_ERROR)ch; if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } error = (DLMS_EXCEPTION_SERVICE_ERROR)ch; uint32_t value = 0; if (error == DLMS_EXCEPTION_SERVICE_ERROR_INVOCATION_COUNTER_ERROR && bb_available(data) > 3) { bb_getUInt32(data, &value); } return DLMS_ERROR_TYPE_EXCEPTION_RESPONSE | value << 8 | error; } #endif //!defined(DLMS_IGNORE_SERVER) int dlms_getPdu( dlmsSettings* settings, gxReplyData* data, unsigned char first) { int ret = DLMS_ERROR_CODE_OK; #if !defined(DLMS_IGNORE_CLIENT) uint32_t index; #endif //!defined(DLMS_IGNORE_CLIENT) unsigned char ch; DLMS_COMMAND cmd = data->command; // If header is not read yet or GBT message. if (cmd == DLMS_COMMAND_NONE) { // If PDU is missing. if (bb_available(&data->data) == 0) { // Invalid PDU. return DLMS_ERROR_CODE_INVALID_PARAMETER; } #if !defined(DLMS_IGNORE_CLIENT) index = data->data.position; #endif //!defined(DLMS_IGNORE_CLIENT) // Get command. if ((ret = bb_getUInt8(&data->data, &ch)) != 0) { return ret; } cmd = (DLMS_COMMAND)ch; data->command = cmd; switch (cmd) { #if !defined(DLMS_IGNORE_CLIENT) #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_READ_RESPONSE: if ((ret = dlms_handleReadResponse(settings, data, (uint16_t)index)) != 0) { if (ret == DLMS_ERROR_CODE_FALSE) { return DLMS_ERROR_CODE_OK; } return ret; } break; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_GET_RESPONSE: if ((ret = dlms_handleGetResponse(settings, data, (uint16_t)index)) != 0) { if (ret == DLMS_ERROR_CODE_FALSE) { return DLMS_ERROR_CODE_OK; } return ret; } break; case DLMS_COMMAND_SET_RESPONSE: ret = dlms_handleSetResponse(settings, data); break; #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_WRITE_RESPONSE: ret = dlms_handleWriteResponse(data); break; #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_METHOD_RESPONSE: ret = dlms_handleMethodResponse(settings, data); break; case DLMS_COMMAND_GENERAL_BLOCK_TRANSFER: ret = dlms_handleGbt(settings, data); break; #endif //!defined(DLMS_IGNORE_CLIENT) case DLMS_COMMAND_AARQ: case DLMS_COMMAND_AARE: // This is parsed later. data->data.position -= 1; break; case DLMS_COMMAND_RELEASE_RESPONSE: break; #if !defined(DLMS_IGNORE_SERVER) case DLMS_COMMAND_CONFIRMED_SERVICE_ERROR: ret = dlms_handleConfirmedServiceError(&data->data); break; case DLMS_COMMAND_EXCEPTION_RESPONSE: ret = dlms_handleExceptionResponse(&data->data); break; case DLMS_COMMAND_GET_REQUEST: #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_READ_REQUEST: case DLMS_COMMAND_WRITE_REQUEST: #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) && !defined(DLMS_IGNORE_MALLOC) case DLMS_COMMAND_SET_REQUEST: case DLMS_COMMAND_METHOD_REQUEST: case DLMS_COMMAND_RELEASE_REQUEST: // Server handles this. if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0) { break; } break; #endif //!defined(DLMS_IGNORE_SERVER) #ifndef DLMS_IGNORE_HIGH_GMAC #if !defined(DLMS_IGNORE_SERVER) case DLMS_COMMAND_GLO_READ_REQUEST: case DLMS_COMMAND_GLO_WRITE_REQUEST: case DLMS_COMMAND_GLO_GET_REQUEST: case DLMS_COMMAND_GLO_SET_REQUEST: case DLMS_COMMAND_GLO_METHOD_REQUEST: case DLMS_COMMAND_DED_GET_REQUEST: case DLMS_COMMAND_DED_SET_REQUEST: case DLMS_COMMAND_DED_METHOD_REQUEST: ret = dlms_handleGloDedRequest(settings, data); // Server handles this. break; #endif //!defined(DLMS_IGNORE_SERVER) #if !defined(DLMS_IGNORE_CLIENT) case DLMS_COMMAND_GLO_READ_RESPONSE: case DLMS_COMMAND_GLO_WRITE_RESPONSE: case DLMS_COMMAND_GLO_GET_RESPONSE: case DLMS_COMMAND_GLO_SET_RESPONSE: case DLMS_COMMAND_GLO_METHOD_RESPONSE: case DLMS_COMMAND_DED_GET_RESPONSE: case DLMS_COMMAND_DED_SET_RESPONSE: case DLMS_COMMAND_DED_EVENT_NOTIFICATION: case DLMS_COMMAND_DED_METHOD_RESPONSE: // If all frames are read. ret = dlms_handleGloDedResponse(settings, data, index); break; #endif // !defined(DLMS_IGNORE_CLIENT) case DLMS_COMMAND_GENERAL_GLO_CIPHERING: case DLMS_COMMAND_GENERAL_DED_CIPHERING: #if !defined(DLMS_IGNORE_SERVER) if (settings->server) { if ((settings->connected & DLMS_CONNECTION_STATE_DLMS) == 0) { return DLMS_ERROR_CODE_INVALID_DECIPHERING_ERROR; } ret = dlms_handleGloDedRequest(settings, data); } #endif// !defined(DLMS_IGNORE_CLIENT) #if !defined(DLMS_IGNORE_CLIENT) #if !defined(DLMS_IGNORE_SERVER) else #endif // !defined(DLMS_IGNORE_SERVER) { ret = dlms_handleGloDedResponse(settings, data, index); } #endif //!defined(DLMS_IGNORE_CLIENT) break; #if !defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC) case DLMS_COMMAND_GENERAL_CIPHERING: ret = dlms_handleGeneralCiphering(settings, data); break; #endif //!defined(DLMS_IGNORE_GENERAL_CIPHERING) && !defined(DLMS_IGNORE_HIGH_GMAC) #endif //DLMS_IGNORE_HIGH_GMAC case DLMS_COMMAND_DATA_NOTIFICATION: ret = dlms_handleDataNotification(settings, data); // Client handles this. break; case DLMS_COMMAND_EVENT_NOTIFICATION: // Client handles this. break; case DLMS_COMMAND_INFORMATION_REPORT: // Client handles this. break; default: // Invalid command. return DLMS_ERROR_CODE_INVALID_PARAMETER; } } else if ((data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) == 0) { // Is whole block is read and if last packet and data is not try to // peek. if (!data->peek && data->moreData == DLMS_DATA_REQUEST_TYPES_NONE) { if (!settings->server || data->command == DLMS_COMMAND_AARE || data->command == DLMS_COMMAND_AARQ) { data->data.position = 0; } else { data->data.position = 1; } } if (cmd == DLMS_COMMAND_GENERAL_BLOCK_TRANSFER) { data->data.position = data->cipherIndex + 1; ret = dlms_handleGbt(settings, data); data->cipherIndex = (uint16_t)data->data.size; data->command = DLMS_COMMAND_NONE; } // Get command if operating as a server. #ifndef DLMS_IGNORE_SERVER if (settings->server) { #ifndef DLMS_IGNORE_HIGH_GMAC // Ciphered messages are handled after whole PDU is received. switch (cmd) { case DLMS_COMMAND_GLO_READ_REQUEST: case DLMS_COMMAND_GLO_WRITE_REQUEST: case DLMS_COMMAND_GLO_GET_REQUEST: case DLMS_COMMAND_GLO_SET_REQUEST: case DLMS_COMMAND_GLO_METHOD_REQUEST: data->command = DLMS_COMMAND_NONE; data->data.position = (data->cipherIndex); ret = dlms_getPdu(settings, data, 0); break; default: break; } #endif //DLMS_IGNORE_HIGH_GMAC } else #endif //DLMS_IGNORE_SERVER { // Client do not need a command any more. data->command = DLMS_COMMAND_NONE; #ifndef DLMS_IGNORE_HIGH_GMAC // Ciphered messages are handled after whole PDU is received. switch (cmd) { case DLMS_COMMAND_GLO_READ_RESPONSE: case DLMS_COMMAND_GLO_WRITE_RESPONSE: case DLMS_COMMAND_GLO_GET_RESPONSE: case DLMS_COMMAND_GLO_SET_RESPONSE: case DLMS_COMMAND_GLO_METHOD_RESPONSE: case DLMS_COMMAND_DED_GET_RESPONSE: case DLMS_COMMAND_DED_SET_RESPONSE: case DLMS_COMMAND_DED_METHOD_RESPONSE: case DLMS_COMMAND_GENERAL_GLO_CIPHERING: case DLMS_COMMAND_GENERAL_DED_CIPHERING: data->data.position = data->cipherIndex; ret = dlms_getPdu(settings, data, 0); break; default: break; } #endif //DLMS_IGNORE_HIGH_GMAC } } #if !defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) // Get data only blocks if SN is used. This is faster. if (ret == 0 && cmd == DLMS_COMMAND_READ_RESPONSE && data->commandType == DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT && (data->moreData & DLMS_DATA_REQUEST_TYPES_FRAME) != 0) { return 0; } #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) // Get data if all data is read or we want to peek data. if (ret == 0 && !data->ignoreValue && data->data.position != data->data.size && ( #if !defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) cmd == DLMS_COMMAND_READ_RESPONSE || #endif //!defined(DLMS_IGNORE_ASSOCIATION_SHORT_NAME) cmd == DLMS_COMMAND_GET_RESPONSE) && (data->moreData == DLMS_DATA_REQUEST_TYPES_NONE || data->peek)) { ret = dlms_getValueFromData(settings, data); } #else data->dataValue.byteArr = &data->data; data->dataValue.vt = DLMS_DATA_TYPE_BYREF | DLMS_DATA_TYPE_OCTET_STRING; #endif //!defined(DLMS_IGNORE_MALLOC) && !defined(DLMS_COSEM_EXACT_DATA_TYPES) return ret; } #ifndef DLMS_IGNORE_HDLC /** * Add LLC bytes to generated message. * * @param settings * DLMS settings. * @param data * Data where bytes are added. */ int dlms_addLLCBytes( dlmsSettings* settings, gxByteBuffer* data) { int ret; if (settings->server) { ret = bb_insert(LLC_REPLY_BYTES, 3, data, 0); } else { ret = bb_insert(LLC_SEND_BYTES, 3, data, 0); } return ret; } #endif //DLMS_IGNORE_HDLC #ifdef DLMS_USE_EPOCH_TIME int dlms_updateSendTime(uint32_t value, gxByteBuffer* reply) #else int dlms_updateSendTime(struct tm* value, gxByteBuffer* reply) #endif // DLMS_USE_EPOCH_TIME { #ifdef DLMS_USE_EPOCH_TIME if (value == 0) #else if (value == NULL) #endif // DLMS_USE_EPOCH_TIME { return bb_setUInt8(reply, (unsigned char)DLMS_DATA_TYPE_NONE); } // Data is send in octet string. Remove data type. int ret; uint16_t pos = (uint16_t)reply->size; dlmsVARIANT tmp; gxtime t; #ifndef DLMS_IGNORE_MALLOC tmp.dateTime = &t; tmp.vt = DLMS_DATA_TYPE_DATETIME; #else GX_DATETIME(tmp) = &t; #endif // DLMS_IGNORE_MALLOC #ifdef DLMS_USE_EPOCH_TIME time_initUnix(&t, value); #else time_initUnix(&t, 0); t.value = *value; #endif //DLMS_USE_EPOCH_TIME if ((ret = dlms_setData(reply, DLMS_DATA_TYPE_OCTET_STRING, &tmp)) != 0) { return ret; } //Remove data type. return bb_move(reply, pos + 1, pos, reply->size - pos - 1); } #ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME int dlms_appendMultipleSNBlocks( gxSNParameters* p, gxByteBuffer* reply) { uint32_t maxSize; #ifndef DLMS_IGNORE_HIGH_GMAC unsigned char ciphering = p->settings->cipher.security != DLMS_SECURITY_NONE; #else unsigned char ciphering = 0; #endif //DLMS_IGNORE_HIGH_GMAC uint32_t hSize = reply->size + 3; // Add LLC bytes. if (p->command == DLMS_COMMAND_WRITE_REQUEST || p->command == DLMS_COMMAND_READ_REQUEST) { hSize += 1 + hlp_getObjectCountSizeInBytes(p->count); } maxSize = p->settings->maxPduSize - hSize; if (ciphering) { maxSize -= CIPHERING_HEADER_SIZE; #ifndef DLMS_IGNORE_HDLC if (dlms_useHdlc(p->settings->interfaceType)) { maxSize -= 3; } #endif //DLMS_IGNORE_HDLC } maxSize -= hlp_getObjectCountSizeInBytes(maxSize); if ((uint16_t)(p->data->size - p->data->position) > maxSize) { // More blocks. bb_setUInt8(reply, 0); } else { // Last block. bb_setUInt8(reply, p->lastBlock); maxSize = p->data->size - p->data->position; } // Add block index. bb_setUInt16(reply, p->blockIndex); if (p->command == DLMS_COMMAND_WRITE_REQUEST) { ++p->blockIndex; hlp_setObjectCount(p->count, reply); bb_setUInt8(reply, DLMS_DATA_TYPE_OCTET_STRING); } else if (p->command == DLMS_COMMAND_READ_REQUEST) { ++p->blockIndex; } hlp_setObjectCount(maxSize, reply); return maxSize; } int dlms_getSNPdu( gxSNParameters* p, gxByteBuffer* reply) { int ret = 0, cnt = 0; unsigned char cipherSize = 0; #ifndef DLMS_IGNORE_HIGH_GMAC unsigned char ciphering = p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE && p->settings->cipher.security != DLMS_SECURITY_NONE; #else unsigned char ciphering = 0; #endif //DLMS_IGNORE_HIGH_GMAC gxByteBuffer* h; if (p->settings->server) { gxByteBuffer header; bb_attach(&header, pduAttributes, 0, sizeof(pduAttributes)); h = &header; } else { h = reply; } if (ciphering) { cipherSize = CIPHERING_HEADER_SIZE; } if (p->data != NULL) { cnt = p->data->size - p->data->position; } // Add command. if (p->command == DLMS_COMMAND_INFORMATION_REPORT) { bb_setUInt8(h, (unsigned char)p->command); // Add date time. if ((ret = dlms_updateSendTime(p->time, reply)) != 0) { return ret; } hlp_setObjectCount(p->count, reply); bb_set2(reply, p->attributeDescriptor, 0, p->attributeDescriptor->size); } else if (p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE) { bb_setUInt8(h, (unsigned char)p->command); if (p->count != 0xFF) { hlp_setObjectCount(p->count, h); } if (!p->multipleBlocks) { if (p->requestType != 0xFF) { bb_setUInt8(h, p->requestType); } if (p->attributeDescriptor != NULL) { bb_set2(h, p->attributeDescriptor, 0, p->attributeDescriptor->size); } p->multipleBlocks = h->size + cipherSize + cnt > p->settings->maxPduSize; // If reply data is not fit to one PDU. if (p->multipleBlocks) { reply->size = 0; if (p->command == DLMS_COMMAND_WRITE_REQUEST) { p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_WRITE_DATA_BLOCK_ACCESS; } else if (p->command == DLMS_COMMAND_READ_REQUEST) { p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_READ_DATA_BLOCK_ACCESS; } else if (p->command == DLMS_COMMAND_READ_RESPONSE) { p->requestType = DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT; } else { //Invalid command. return DLMS_ERROR_CODE_INVALID_COMMAND; } bb_setUInt8(reply, (unsigned char)p->command); // Set object count. bb_setUInt8(reply, 1); if (p->requestType != 0xFF) { bb_setUInt8(reply, p->requestType); } cnt = dlms_appendMultipleSNBlocks(p, h); } } else { if (p->command == DLMS_COMMAND_WRITE_REQUEST) { p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_WRITE_DATA_BLOCK_ACCESS; } else if (p->command == DLMS_COMMAND_READ_REQUEST) { p->requestType = DLMS_VARIABLE_ACCESS_SPECIFICATION_READ_DATA_BLOCK_ACCESS; } else if (p->command == DLMS_COMMAND_READ_RESPONSE) { p->requestType = DLMS_SINGLE_READ_RESPONSE_DATA_BLOCK_RESULT; } else { //Invalid command. return DLMS_ERROR_CODE_INVALID_COMMAND; } if (p->requestType != 0xFF) { bb_setUInt8(h, p->requestType); } if (p->attributeDescriptor != NULL) { bb_set2(h, p->attributeDescriptor, 0, p->attributeDescriptor->size); } cnt = dlms_appendMultipleSNBlocks(p, h); } } // Add data. if (p->settings->server) { bb_insert(h->data, h->size, reply, 0); } else if (p->data != NULL) { bb_set2(reply, p->data, p->data->position, cnt); } #ifndef DLMS_IGNORE_HIGH_GMAC // If Ciphering is used. if (ciphering && p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE) { ret = cip_encrypt( &p->settings->cipher, p->settings->cipher.security, DLMS_COUNT_TYPE_PACKET, p->settings->cipher.invocationCounter, dlms_getGloMessage(p->settings, p->command, p->encryptedCommand), #ifndef DLMS_IGNORE_MALLOC p->settings->cipher.systemTitle.data, &p->settings->cipher.blockCipherKey, #else p->settings->cipher.systemTitle, p->settings->cipher.blockCipherKey, #endif //DLMS_IGNORE_MALLOC reply); if (ret != 0) { return ret; } reply->position = reply->size = 0; } #endif //DLMS_IGNORE_HIGH_GMAC #ifndef DLMS_IGNORE_HDLC if (ret == 0 && dlms_useHdlc(p->settings->interfaceType)) { ret = dlms_addLLCBytes(p->settings, reply); } #endif //DLMS_IGNORE_HDLC return 0; } #endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME /** * Check is all data fit to one data block. * * @param p * LN parameters. * @param reply * Generated reply. */ void dlms_multipleBlocks( gxLNParameters* p, gxByteBuffer* reply, unsigned char ciphering) { // Check is all data fit to one message if data is given. int len = bb_available(p->data); if (p->attributeDescriptor != NULL) { len += p->attributeDescriptor->size; } if (ciphering) { len += CIPHERING_HEADER_SIZE; } if (!p->multipleBlocks) { // Add command type and invoke and priority. p->multipleBlocks = 2 + reply->size + len > p->settings->maxPduSize; } if (p->lastBlock) { // Add command type and invoke and priority. p->lastBlock = !(8 + reply->size + len > p->settings->maxPduSize); } } int dlms_getLNPdu( gxLNParameters* p, gxByteBuffer* reply) { int ret = 0; #ifndef DLMS_IGNORE_HIGH_GMAC unsigned char ciphering = (p->command != DLMS_COMMAND_AARQ && p->command != DLMS_COMMAND_AARE && p->settings->cipher.security != DLMS_SECURITY_NONE) || p->encryptedCommand != DLMS_COMMAND_NONE; #else unsigned char ciphering = 0; #endif //DLMS_IGNORE_HIGH_GMAC #if defined(GX_DLMS_BYTE_BUFFER_SIZE_32) || (!defined(GX_DLMS_MICROCONTROLLER) && (defined(_WIN32) || defined(_WIN64) || defined(__linux__))) uint32_t len = 0; #else uint16_t len = 0; #endif if (p->command == DLMS_COMMAND_AARQ) { //Data is already added to reply when malloc is not used. #ifndef DLMS_IGNORE_MALLOC if ((ret = bb_set2(reply, p->attributeDescriptor, 0, p->attributeDescriptor->size)) != 0) { return ret; } #endif //DLMS_IGNORE_MALLOC } else { gxByteBuffer header; gxByteBuffer* h; if (p->settings->server) { bb_attach(&header, pduAttributes, 0, sizeof(pduAttributes)); h = &header; } else { #ifdef DLMS_IGNORE_MALLOC bb_attach(&header, pduAttributes, 0, sizeof(pduAttributes)); h = &header; #else h = reply; #endif //DLMS_IGNORE_MALLOC } // Add command. if (p->command != DLMS_COMMAND_GENERAL_BLOCK_TRANSFER) { ret = bb_setUInt8(h, (unsigned char)p->command); } if (p->command == DLMS_COMMAND_EVENT_NOTIFICATION || p->command == DLMS_COMMAND_DATA_NOTIFICATION || p->command == DLMS_COMMAND_ACCESS_REQUEST || p->command == DLMS_COMMAND_ACCESS_RESPONSE) { // Add Long-Invoke-Id-And-Priority if (p->command != DLMS_COMMAND_EVENT_NOTIFICATION) { if (p->invokeId != 0) { ret = bb_setUInt32(h, p->invokeId); } else { ret = bb_setUInt32(h, dlms_getLongInvokeIDPriority(p->settings)); } } // Add date time. ret = dlms_updateSendTime(p->time, h); } else if (p->command != DLMS_COMMAND_RELEASE_REQUEST) { // Get request size can be bigger than PDU size. if (p->command != DLMS_COMMAND_GET_REQUEST && p->data != NULL && p->data->size != 0) { dlms_multipleBlocks(p, h, ciphering); } // Change Request type if Set request and multiple blocks is // needed. if (p->command == DLMS_COMMAND_SET_REQUEST) { if (p->multipleBlocks) { if (p->requestType == 1) { p->requestType = 2; } else if (p->requestType == 2) { p->requestType = 3; } } } // Change request type If get response and multiple blocks is // needed. if (p->command == DLMS_COMMAND_GET_RESPONSE) { if (p->multipleBlocks) { if (p->requestType == 1) { p->requestType = 2; } } } ret = bb_setUInt8(h, p->requestType); // Add Invoke Id And Priority. if (p->invokeId != 0) { ret = bb_setUInt8(h, p->invokeId); } else { ret = bb_setUInt8(h, dlms_getInvokeIDPriority(p->settings, p->settings->autoIncreaseInvokeID)); } } #ifndef DLMS_IGNORE_MALLOC // Add attribute descriptor. if (ret == 0 && p->attributeDescriptor != NULL) { ret = bb_set(reply, p->attributeDescriptor->data, p->attributeDescriptor->size); } #endif //DLMS_IGNORE_MALLOC if (ret == 0 && p->command != DLMS_COMMAND_EVENT_NOTIFICATION && p->command != DLMS_COMMAND_DATA_NOTIFICATION && (p->settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_BLOCK_TRANSFER) == 0) { int totalLength; // If multiple blocks. if (p->multipleBlocks) { // Is last block. if (p->lastBlock) { ret = bb_setUInt8(h, 1); } else { ret = bb_setUInt8(h, 0); } // Block index. ret = bb_setUInt32(h, p->blockIndex); p->blockIndex = p->blockIndex + 1; // Add status if reply. if (p->status != 0xFF) { if (p->status != 0 && p->command == DLMS_COMMAND_GET_RESPONSE) { ret = bb_setUInt8(h, 1); } ret = bb_setUInt8(h, p->status); } // Block size. if (p->data != NULL) { len = (uint16_t)(p->data->size - p->data->position); } else { len = 0; } totalLength = len + h->size; #ifndef DLMS_IGNORE_HIGH_GMAC if (ciphering) { totalLength += CIPHERING_HEADER_SIZE; } #endif //DLMS_IGNORE_HIGH_GMAC if (totalLength > p->settings->maxPduSize) { len = (uint16_t)(p->settings->maxPduSize - h->size); #ifndef DLMS_IGNORE_HIGH_GMAC if (ciphering) { len -= CIPHERING_HEADER_SIZE; } #endif //DLMS_IGNORE_HIGH_GMAC len -= hlp_getObjectCountSizeInBytes(len); } ret = hlp_setObjectCount(len, h); if (ret == 0) { #ifdef DLMS_IGNORE_MALLOC if ((ret = bb_insert(h->data, h->size, reply, 0)) != 0) { //If this fails PDU buffer size must be bigger. return ret; } #else if (p->settings->server) { if ((ret = bb_insert(h->data, h->size, reply, 0)) != 0) { //If this fails PDU buffer size must be bigger. return ret; } } else if (p->data != NULL) { ret = bb_set2(reply, p->data, p->data->position, len); } #endif //DLMS_IGNORE_MALLOC } } } // Add data that fits to one block. if (ret == 0 && len == 0) { // Add status if reply. if (p->status != 0xFF) { if (p->status != 0 && (p->command == DLMS_COMMAND_GET_RESPONSE)) { ret = bb_setUInt8(h, 1); } ret = bb_setUInt8(h, p->status); } if (ret == 0 && p->data != NULL && p->data->size != 0) { len = bb_available(p->data); if (len + reply->size > p->settings->maxPduSize) { len = (uint16_t)(p->settings->maxPduSize - h->size - p->data->size - p->data->position); } #ifdef DLMS_IGNORE_MALLOC ret = bb_insert(h->data, h->size, reply, 0); if (!p->settings->server) { p->data->position += len; } #else if (p->settings->server) { ret = bb_insert(h->data, h->size, reply, 0); } else { ret = bb_set2(reply, p->data, p->data->position, len); } #endif //DLMS_IGNORE_MALLOC } else { #ifdef DLMS_IGNORE_MALLOC ret = bb_insert(h->data, h->size, reply, 0); #else if (p->settings->server) { ret = bb_insert(h->data, h->size, reply, 0); } #endif //DLMS_IGNORE_MALLOC } } #ifndef DLMS_IGNORE_HIGH_GMAC if (ret == 0 && ciphering && reply->size != 0 && p->command != DLMS_COMMAND_RELEASE_REQUEST) { #ifndef DLMS_IGNORE_MALLOC gxByteBuffer* key; #else unsigned char* key; #endif //DLMS_IGNORE_MALLOC if (p->settings->cipher.broacast) { #ifndef DLMS_IGNORE_MALLOC key = &p->settings->cipher.broadcastBlockCipherKey; #else key = p->settings->cipher.broadcastBlockCipherKey; #endif //DLMS_IGNORE_MALLOC } else if (dlms_useDedicatedKey(p->settings) && (p->settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0) { key = p->settings->cipher.dedicatedKey; } else { #ifndef DLMS_IGNORE_MALLOC key = &p->settings->cipher.blockCipherKey; #else key = p->settings->cipher.blockCipherKey; #endif //DLMS_IGNORE_MALLOC } ret = cip_encrypt( &p->settings->cipher, p->settings->cipher.security, DLMS_COUNT_TYPE_PACKET, p->settings->cipher.invocationCounter, dlms_getGloMessage(p->settings, p->command, p->encryptedCommand), #ifndef DLMS_IGNORE_MALLOC p->settings->cipher.systemTitle.data, #else p->settings->cipher.systemTitle, #endif //DLMS_IGNORE_MALLOC key, reply); } #endif //DLMS_IGNORE_HIGH_GMAC1 if (p->command == DLMS_COMMAND_GENERAL_BLOCK_TRANSFER || (p->multipleBlocks && (p->settings->negotiatedConformance & DLMS_CONFORMANCE_GENERAL_BLOCK_TRANSFER) != 0)) { bb_setUInt8(h, DLMS_COMMAND_GENERAL_BLOCK_TRANSFER); dlms_multipleBlocks(p, h, ciphering); // Is last block if (!p->lastBlock) { bb_setUInt8(h, 0); } else { bb_setUInt8(h, 0x80); } // Set block number sent. bb_setUInt8(h, 0); // Set block number acknowledged bb_setUInt8(h, (unsigned char)p->blockIndex); p->blockIndex = p->blockIndex + 1; // Add APU tag. bb_setUInt8(h, 0); // Add Addl fields bb_setUInt8(h, 0); } } #ifndef DLMS_IGNORE_HDLC if (ret == 0 && dlms_useHdlc(p->settings->interfaceType)) { ret = dlms_addLLCBytes(p->settings, reply); } #endif //DLMS_IGNORE_HDLC return ret; } int dlms_getLnMessages( gxLNParameters* p, message* messages) { int ret; gxByteBuffer* pdu; gxByteBuffer* it; #ifndef DLMS_IGNORE_HDLC unsigned char frame = 0; if (p->command == DLMS_COMMAND_DATA_NOTIFICATION || p->command == DLMS_COMMAND_EVENT_NOTIFICATION) { frame = 0x13; } #endif //DLMS_IGNORE_HDLC #ifdef DLMS_IGNORE_MALLOC pdu = p->serializedPdu; #else gxByteBuffer reply; if (p->serializedPdu == NULL) { BYTE_BUFFER_INIT(&reply); pdu = &reply; } else { pdu = p->serializedPdu; } #endif //DLMS_IGNORE_MALLOC do { if ((ret = dlms_getLNPdu(p, pdu)) == 0) { p->lastBlock = 1; if (p->attributeDescriptor == NULL) { ++p->settings->blockIndex; } } while (ret == 0 && pdu->position != pdu->size) { #ifdef DLMS_IGNORE_MALLOC if (!(messages->size < messages->capacity)) { ret = DLMS_ERROR_CODE_INVALID_PARAMETER; break; } it = messages->data[messages->size]; ++messages->size; bb_clear(it); #else if (messages->attached) { if (messages->size < messages->capacity) { it = messages->data[messages->size]; ++messages->size; bb_clear(it); } else { return DLMS_ERROR_CODE_OUTOFMEMORY; } } else { it = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); if (it == NULL) { return DLMS_ERROR_CODE_OUTOFMEMORY; } BYTE_BUFFER_INIT(it); mes_push(messages, it); } #endif //DLMS_IGNORE_MALLOC switch (p->settings->interfaceType) { #ifndef DLMS_IGNORE_WRAPPER case DLMS_INTERFACE_TYPE_WRAPPER: ret = dlms_getWrapperFrame(p->settings, p->command, pdu, it); break; #endif //DLMS_IGNORE_WRAPPER #ifndef DLMS_IGNORE_HDLC case DLMS_INTERFACE_TYPE_HDLC: case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E: ret = dlms_getHdlcFrame(p->settings, frame, pdu, it); if (ret == 0 && pdu->position != pdu->size) { frame = getNextSend(p->settings, 0); } break; #endif //DLMS_IGNORE_HDLC case DLMS_INTERFACE_TYPE_PDU: ret = bb_set2(it, pdu, 0, pdu->size); break; #ifndef DLMS_IGNORE_PLC case DLMS_INTERFACE_TYPE_PLC: ret = dlms_getPlcFrame(p->settings, 0x90, pdu, it); break; case DLMS_INTERFACE_TYPE_PLC_HDLC: ret = dlms_getMacHdlcFrame(p->settings, frame, 0, pdu, it); break; #endif //DLMS_IGNORE_PLC default: ret = DLMS_ERROR_CODE_INVALID_PARAMETER; } if (ret != 0) { break; } } bb_clear(pdu); #ifndef DLMS_IGNORE_HDLC frame = 0; #endif //DLMS_IGNORE_HDLC } while (ret == 0 && p->data != NULL && p->data->position != p->data->size); return ret; } #ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME int dlms_getSnMessages( gxSNParameters* p, message* messages) { int ret; gxByteBuffer data; gxByteBuffer* it; unsigned char frame = 0x0; if (p->command == DLMS_COMMAND_INFORMATION_REPORT) { if ((p->settings->connected & DLMS_CONNECTION_STATE_DLMS) != 0) { //If connection is established. frame = 0x13; } else { frame = 0x3; } } else if (p->command == DLMS_COMMAND_NONE) { frame = getNextSend(p->settings, 1); } BYTE_BUFFER_INIT(&data); mes_clear(messages); do { ret = dlms_getSNPdu(p, &data); // Command is not add to next PDUs. while (ret == 0 && data.position != data.size) { #ifndef DLMS_IGNORE_MALLOC it = (gxByteBuffer*)gxmalloc(sizeof(gxByteBuffer)); #else if (!(messages->size < messages->capacity)) { return DLMS_ERROR_CODE_OUTOFMEMORY; } it = messages->data[messages->size]; ++messages->size; #endif //DLMS_IGNORE_MALLOC BYTE_BUFFER_INIT(it); if (p->settings->interfaceType == DLMS_INTERFACE_TYPE_WRAPPER) { #ifndef DLMS_IGNORE_WRAPPER ret = dlms_getWrapperFrame(p->settings, p->command, &data, it); #else ret = DLMS_ERROR_CODE_INVALID_PARAMETER; #endif //DLMS_IGNORE_WRAPPER } else { #ifndef DLMS_IGNORE_HDLC ret = dlms_getHdlcFrame(p->settings, frame, &data, it); if (data.position != data.size) { frame = getNextSend(p->settings, 0); } #else ret = DLMS_ERROR_CODE_INVALID_PARAMETER; #endif //DLMS_IGNORE_HDLC } if (ret != 0) { break; } #ifndef DLMS_IGNORE_MALLOC mes_push(messages, it); #endif //DLMS_IGNORE_MALLOC } bb_clear(&data); frame = 0; } while (ret == 0 && p->data != NULL && p->data->position != p->data->size); return 0; } #endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME int dlms_getData2( dlmsSettings* settings, gxByteBuffer* reply, gxReplyData* data, unsigned char first) { unsigned char isNotify; return dlms_getData3(settings, reply, data, NULL, first, &isNotify); } int dlms_getData3( dlmsSettings* settings, gxByteBuffer* reply, gxReplyData* data, gxReplyData* notify, unsigned char first, unsigned char* isNotify) { int ret; unsigned char frame = 0; if (isNotify != NULL) { *isNotify = 0; } switch (settings->interfaceType) { #ifndef DLMS_IGNORE_HDLC case DLMS_INTERFACE_TYPE_HDLC: case DLMS_INTERFACE_TYPE_HDLC_WITH_MODE_E: ret = dlms_getHdlcData(settings->server, settings, reply, data, &frame, data->preEstablished, first); break; #endif //DLMS_IGNORE_HDLC #ifndef DLMS_IGNORE_WRAPPER case DLMS_INTERFACE_TYPE_WRAPPER: ret = dlms_getTcpData(settings, reply, data, notify, isNotify); break; #endif //DLMS_IGNORE_WRAPPER #ifndef DLMS_IGNORE_WIRELESS_MBUS case DLMS_INTERFACE_TYPE_WIRELESS_MBUS: ret = dlms_getMBusData(settings, reply, data); break; #endif //DLMS_IGNORE_WIRELESS_MBUS case DLMS_INTERFACE_TYPE_PDU: data->packetLength = reply->size; data->complete = reply->size != 0; ret = 0; break; #ifndef DLMS_IGNORE_PLC case DLMS_INTERFACE_TYPE_PLC: ret = dlms_getPlcData(settings, reply, data); break; case DLMS_INTERFACE_TYPE_PLC_HDLC: ret = dlms_getPlcHdlcData(settings, reply, data, &frame); #endif //DLMS_IGNORE_PLC break; default: // Invalid Interface type. ret = DLMS_ERROR_CODE_INVALID_PARAMETER; break; } if (ret != 0) { return ret; } if (*isNotify && notify != NULL) { if (!notify->complete) { // If all data is not read yet. return 0; } data = notify; } else if (!data->complete) { // If all data is not read yet. return 0; } if (settings->interfaceType != DLMS_INTERFACE_TYPE_PLC_HDLC) { if ((ret = dlms_getDataFromFrame(reply, data, dlms_useHdlc(settings->interfaceType))) != 0) { return ret; } } // If keepalive or get next frame request. if (((frame != 0x13 && frame != 0x3) || data->moreData != DLMS_DATA_REQUEST_TYPES_NONE) && (frame & 0x1) != 0) { if (dlms_useHdlc(settings->interfaceType) && data->data.size != 0) { if (reply->position != reply->size) { reply->position += 3; } } if (data->command == DLMS_COMMAND_REJECTED) { return DLMS_ERROR_CODE_REJECTED; } return DLMS_ERROR_CODE_OK; } ret = dlms_getPdu(settings, data, first); if (ret == 0 && notify != NULL && !isNotify) { //Check command to make sure it's not notify message. switch (data->command) { case DLMS_COMMAND_DATA_NOTIFICATION: case DLMS_COMMAND_GLO_EVENT_NOTIFICATION_REQUEST: case DLMS_COMMAND_INFORMATION_REPORT: case DLMS_COMMAND_EVENT_NOTIFICATION: case DLMS_COMMAND_DED_EVENT_NOTIFICATION: *isNotify = 1; notify->complete = data->complete; notify->moreData = data->moreData; notify->command = data->command; data->command = DLMS_COMMAND_NONE; notify->time = data->time; #ifdef DLMS_USE_EPOCH_TIME data->time = 0; #else memset(&data->time, 0, sizeof(data->time)); #endif // DLMS_USE_EPOCH_TIME bb_set2(¬ify->data, &data->data, data->data.position, bb_available(&data->data)); bb_clear(&data->data); notify->dataValue = data->dataValue; data->dataValue.vt = DLMS_DATA_TYPE_NONE; break; default: break; } } return ret; } int dlms_generateChallenge( gxByteBuffer* challenge) { // Random challenge is 8 to 64 bytes. // Texas Instruments accepts only 16 byte int32_t challenge. // For this reason challenge size is 16 bytes at the moment. int ret = 0, pos, len = 16;//hlp_rand() % 58 + 8; bb_clear(challenge); for (pos = 0; pos != len; ++pos) { if ((ret = bb_setUInt8(challenge, hlp_rand())) != 0) { break; } } return ret; } #ifndef DLMS_IGNORE_ASSOCIATION_SHORT_NAME int dlms_getActionInfo( DLMS_OBJECT_TYPE objectType, unsigned char* value, unsigned char* count) { switch (objectType) { case DLMS_OBJECT_TYPE_DATA: case DLMS_OBJECT_TYPE_ACTION_SCHEDULE: case DLMS_OBJECT_TYPE_AUTO_ANSWER: case DLMS_OBJECT_TYPE_AUTO_CONNECT: case DLMS_OBJECT_TYPE_MAC_ADDRESS_SETUP: case DLMS_OBJECT_TYPE_GPRS_SETUP: case DLMS_OBJECT_TYPE_IEC_HDLC_SETUP: case DLMS_OBJECT_TYPE_IEC_LOCAL_PORT_SETUP: case DLMS_OBJECT_TYPE_IEC_TWISTED_PAIR_SETUP: case DLMS_OBJECT_TYPE_MODEM_CONFIGURATION: case DLMS_OBJECT_TYPE_PPP_SETUP: case DLMS_OBJECT_TYPE_REGISTER_MONITOR: case DLMS_OBJECT_TYPE_ZIG_BEE_SAS_STARTUP: case DLMS_OBJECT_TYPE_ZIG_BEE_SAS_JOIN: case DLMS_OBJECT_TYPE_ZIG_BEE_SAS_APS_FRAGMENTATION: case DLMS_OBJECT_TYPE_ZIG_BEE_NETWORK_CONTROL: case DLMS_OBJECT_TYPE_SCHEDULE: case DLMS_OBJECT_TYPE_SMTP_SETUP: case DLMS_OBJECT_TYPE_STATUS_MAPPING: case DLMS_OBJECT_TYPE_TCP_UDP_SETUP: case DLMS_OBJECT_TYPE_UTILITY_TABLES: *value = 0; *count = 0; break; case DLMS_OBJECT_TYPE_MBUS_DIAGNOSTIC: *value = 48; *count = 1; break; case DLMS_OBJECT_TYPE_IMAGE_TRANSFER: *value = 0x40; *count = 4; break; case DLMS_OBJECT_TYPE_ACTIVITY_CALENDAR: *value = 0x50; *count = 1; break; case DLMS_OBJECT_TYPE_ASSOCIATION_LOGICAL_NAME: *value = 0x60; *count = 4; break; case DLMS_OBJECT_TYPE_ASSOCIATION_SHORT_NAME: *value = 0x20; *count = 8; break; case DLMS_OBJECT_TYPE_CLOCK: *value = 0x60; *count = 6; break; case DLMS_OBJECT_TYPE_DEMAND_REGISTER: *value = 0x48; *count = 2; break; case DLMS_OBJECT_TYPE_EXTENDED_REGISTER: *value = 0x38; *count = 1; break; case DLMS_OBJECT_TYPE_IP4_SETUP: *value = 0x60; *count = 3; break; case DLMS_OBJECT_TYPE_MBUS_SLAVE_PORT_SETUP: *value = 0x60; *count = 8; break; case DLMS_OBJECT_TYPE_PROFILE_GENERIC: *value = 0x58; *count = 4; break; case DLMS_OBJECT_TYPE_REGISTER: *value = 0x28; *count = 1; break; case DLMS_OBJECT_TYPE_REGISTER_ACTIVATION: *value = 0x30; *count = 3; break; case DLMS_OBJECT_TYPE_REGISTER_TABLE: *value = 0x28; *count = 2; break; case DLMS_OBJECT_TYPE_SAP_ASSIGNMENT: case DLMS_OBJECT_TYPE_SCRIPT_TABLE: *value = 0x20; *count = 1; break; case DLMS_OBJECT_TYPE_SPECIAL_DAYS_TABLE: *value = 0x10; *count = 2; break; case DLMS_OBJECT_TYPE_DISCONNECT_CONTROL: *value = 0x20; *count = 2; break; case DLMS_OBJECT_TYPE_PUSH_SETUP: *value = 0x38; *count = 1; break; default: *count = *value = 0; break; } return DLMS_ERROR_CODE_OK; } #endif //DLMS_IGNORE_ASSOCIATION_SHORT_NAME int dlms_secure( dlmsSettings* settings, int32_t ic, gxByteBuffer* data, gxByteBuffer* secret, gxByteBuffer* reply) { int ret = 0; gxByteBuffer challenge; bb_clear(reply); #ifdef DLMS_IGNORE_MALLOC bb_attach(&challenge, pduAttributes, 0, sizeof(pduAttributes)); #else BYTE_BUFFER_INIT(&challenge); #endif //DLMS_IGNORE_MALLOC if (settings->authentication == DLMS_AUTHENTICATION_HIGH) { #ifndef DLMS_IGNORE_AES unsigned char tmp[16]; gxByteBuffer s; uint16_t len = (uint16_t)data->size; bb_attach(&s, tmp, 0, sizeof(tmp)); if (len % 16 != 0) { len += (16 - (data->size % 16)); } if (secret->size > data->size) { len = (uint16_t)secret->size; if (len % 16 != 0) { len += (16 - (secret->size % 16)); } } if ((ret = bb_set(&s, secret->data, secret->size)) == 0 && (ret = bb_zero(&s, s.size, len - s.size)) == 0 && (ret = bb_set(&challenge, data->data, data->size)) == 0 && (ret = bb_zero(&challenge, challenge.size, len - challenge.size)) == 0 && (ret = bb_capacity(reply, challenge.size)) == 0) { gxaes_ecb_encrypt(challenge.data, s.data, reply->data, s.size); } reply->size = s.size; #ifndef DLMS_IGNORE_MALLOC bb_clear(&s); bb_clear(&challenge); #endif //DLMS_IGNORE_MALLOC return ret; #else return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #endif //DLMS_IGNORE_AES } // Get server Challenge. // Get shared secret #ifndef DLMS_IGNORE_HIGH_GMAC if (settings->authentication != DLMS_AUTHENTICATION_HIGH_GMAC) #endif //DLMS_IGNORE_HIGH_GMAC { if (settings->authentication == DLMS_AUTHENTICATION_HIGH_SHA256) { //If SHA256 is not used. #ifdef DLMS_IGNORE_HIGH_SHA256 return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else #ifndef DLMS_IGNORE_HIGH_GMAC #ifdef DLMS_IGNORE_MALLOC if ((ret = bb_set(&challenge, secret->data, secret->size)) != 0 || (ret = bb_set(&challenge, settings->cipher.systemTitle, 8)) != 0 || (ret = bb_set(&challenge, settings->sourceSystemTitle, 8)) != 0) { return ret; } #else if ((ret = bb_set(&challenge, secret->data, secret->size)) != 0 || (ret = bb_set(&challenge, settings->cipher.systemTitle.data, settings->cipher.systemTitle.size)) != 0 || (ret = bb_set(&challenge, settings->sourceSystemTitle, 8)) != 0) { return ret; } #endif //DLMS_IGNORE_MALLOC if (settings->server) { if ((ret = bb_set(&challenge, settings->ctoSChallenge.data, settings->ctoSChallenge.size)) != 0 || (ret = bb_set(&challenge, settings->stoCChallenge.data, settings->stoCChallenge.size)) != 0) { return ret; } } else { if ((ret = bb_set(&challenge, settings->stoCChallenge.data, settings->stoCChallenge.size)) != 0 || (ret = bb_set(&challenge, settings->ctoSChallenge.data, settings->ctoSChallenge.size)) != 0) { return ret; } } #endif //DLMS_IGNORE_HIGH_GMAC #endif //DLMS_IGNORE_HIGH_SHA256 } else { if ((ret = bb_set(&challenge, data->data, data->size)) != 0 || (ret = bb_set(&challenge, secret->data, secret->size)) != 0) { return ret; } } } if (settings->authentication == DLMS_AUTHENTICATION_HIGH_MD5) { //If MD5 is not used. #ifdef DLMS_IGNORE_HIGH_MD5 return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else ret = gxmd5_encrypt(&challenge, reply); bb_clear(&challenge); return ret; #endif //DLMS_IGNORE_HIGH_MD5 } else if (settings->authentication == DLMS_AUTHENTICATION_HIGH_SHA1) { //If SHA1 is not used. #ifdef DLMS_IGNORE_HIGH_SHA1 return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else ret = gxsha1_encrypt(&challenge, reply); bb_clear(&challenge); return ret; #endif //DLMS_IGNORE_HIGH_SHA1 } else if (settings->authentication == DLMS_AUTHENTICATION_HIGH_SHA256) { //If SHA256 is not used. #ifdef DLMS_IGNORE_HIGH_SHA256 return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else ret = gxsha256_encrypt(&challenge, reply); bb_clear(&challenge); return ret; #endif //DLMS_IGNORE_HIGH_SHA256 } #ifndef DLMS_IGNORE_HIGH_GMAC else if (settings->authentication == DLMS_AUTHENTICATION_HIGH_GMAC) { //If GMAC is not used. #ifdef DLMS_IGNORE_HIGH_GMAC bb_clear(&challenge); return DLMS_ERROR_CODE_NOT_IMPLEMENTED; #else ret = cip_encrypt( &settings->cipher, DLMS_SECURITY_AUTHENTICATION, DLMS_COUNT_TYPE_TAG, ic, 0, secret->data, #ifdef DLMS_IGNORE_MALLOC settings->cipher.blockCipherKey, #else & settings->cipher.blockCipherKey, #endif //DLMS_IGNORE_MALLOC data); if (ret == 0) { if ((ret = bb_setUInt8(reply, DLMS_SECURITY_AUTHENTICATION | settings->cipher.suite)) != 0 || (ret = bb_setUInt32(reply, ic)) != 0 || (ret = bb_set2(reply, data, data->size - 12, 12)) != 0) { } } bb_clear(&challenge); #endif //DLMS_IGNORE_HIGH_GMAC } #endif //DLMS_IGNORE_HIGH_GMAC return ret; } int dlms_parseSnrmUaResponse( dlmsSettings* settings, gxByteBuffer* data) { uint32_t value; unsigned char ch, id, len; uint16_t tmp; int ret; //If default settings are used. if (data->size - data->position == 0) { return 0; } // Skip FromatID if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } // Skip Group ID. if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } // Skip Group len if ((ret = bb_getUInt8(data, &ch)) != 0) { return ret; } while (data->position < data->size) { if ((ret = bb_getUInt8(data, &id)) != 0 || (ret = bb_getUInt8(data, &len)) != 0) { return ret; } switch (len) { case 1: ret = bb_getUInt8(data, &ch); value = ch; break; case 2: ret = bb_getUInt16(data, &tmp); value = tmp; break; case 4: ret = bb_getUInt32(data, &value); break; default: ret = DLMS_ERROR_CODE_INVALID_PARAMETER; } if (ret != DLMS_ERROR_CODE_OK) { return ret; } // RX / TX are delivered from the partner's point of view => // reversed to ours switch (id) { case HDLC_INFO_MAX_INFO_TX: if (value < settings->maxInfoRX) { settings->maxInfoRX = (uint16_t)value; } break; case HDLC_INFO_MAX_INFO_RX: if (value < settings->maxInfoTX) { settings->maxInfoTX = (uint16_t)value; } break; case HDLC_INFO_WINDOW_SIZE_TX: if (value < settings->windowSizeRX) { settings->windowSizeRX = (unsigned char)value; } break; case HDLC_INFO_WINDOW_SIZE_RX: if (value < settings->windowSizeTX) { settings->windowSizeTX = (unsigned char)value; } break; default: ret = DLMS_ERROR_CODE_INVALID_PARAMETER; break; } } return ret; } int dlms_appendHdlcParameter(gxByteBuffer* data, uint16_t value) { if (value < 0x100) { bb_setUInt8(data, 1); bb_setUInt8(data, (unsigned char)value); } else { bb_setUInt8(data, 2); bb_setUInt16(data, value); } return 0; } int dlms_isPduFull(dlmsSettings* settings, gxByteBuffer* data, uint16_t* size) { unsigned char ret; if (bb_isAttached(data)) { uint16_t len = 0; if (size != NULL) { if (*size == 0) { *size = (uint16_t)data->size; } len = *size; } #ifndef DLMS_IGNORE_HIGH_GMAC if (settings->cipher.security != DLMS_SECURITY_NONE) { len += 20 + CIPHERING_HEADER_SIZE + (uint16_t)data->size; } else #endif //DLMS_IGNORE_HIGH_GMAC { len += 20 + (uint16_t)data->size; } ret = settings->maxPduSize < len; } else { ret = 0; } return ret; }