Secured the WhoHas codec and improved unit test coverage. (#649)

This commit is contained in:
Steve Karg
2024-05-20 11:39:10 -05:00
committed by GitHub
parent cf00e9e094
commit 53fd7a2e1f
3 changed files with 271 additions and 192 deletions
+186 -122
View File
@@ -1,36 +1,10 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/**
* @file
* @brief BACnet WhoHas-Request encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2006
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
@@ -38,122 +12,212 @@
#include "bacnet/bacdcode.h"
#include "bacnet/whohas.h"
/** @file whohas.c Encode/Decode Who-Has requests */
/** Encode Who-Hasservice - use -1 for limit for unlimited
/**
* @brief Encode Who-Has-Request - use -1 as limit for unlimited
* Who-Has-Request ::= SEQUENCE {
* limits SEQUENCE {
* device-instance-range-low-limit [0] Unsigned (0..4194303),
* device-instance-range-high-limit [1] Unsigned (0..4194303)
* } OPTIONAL,
* object CHOICE {
* object-identifier [2] BACnetObjectIdentifier,
* object-name [3] CharacterString
* }
* }
*
* @param apdu Pointer to the APDU.
* @param data Pointer to the Who-Has application data.
*
* @return Bytes encoded. */
int whohas_encode_apdu(uint8_t *apdu, BACNET_WHO_HAS_DATA *data)
* @param apdu application data unit buffer
* @param data application data to be encoded
* @return number of bytes encoded
*/
int bacnet_who_has_request_encode(uint8_t *apdu, BACNET_WHO_HAS_DATA *data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && data) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_WHO_HAS; /* service choice */
apdu_len = 2;
/* optional limits - must be used as a pair */
if ((data->low_limit >= 0) &&
(data->low_limit <= BACNET_MAX_INSTANCE) &&
(data->high_limit >= 0) &&
(data->high_limit <= BACNET_MAX_INSTANCE)) {
len = encode_context_unsigned(&apdu[apdu_len], 0, data->low_limit);
apdu_len += len;
len = encode_context_unsigned(&apdu[apdu_len], 1, data->high_limit);
apdu_len += len;
if (!data) {
return 0;
}
/* optional limits - must be used as a pair */
if ((data->low_limit >= 0) && (data->low_limit <= BACNET_MAX_INSTANCE) &&
(data->high_limit >= 0) && (data->high_limit <= BACNET_MAX_INSTANCE)) {
len = encode_context_unsigned(apdu, 0, data->low_limit);
apdu_len += len;
if (apdu) {
apdu += len;
}
if (data->is_object_name) {
len = encode_context_character_string(
&apdu[apdu_len], 3, &data->object.name);
apdu_len += len;
} else {
len = encode_context_object_id(&apdu[apdu_len], 2,
data->object.identifier.type, data->object.identifier.instance);
apdu_len += len;
len = encode_context_unsigned(apdu, 1, data->high_limit);
apdu_len += len;
if (apdu) {
apdu += len;
}
}
if (data->is_object_name) {
len = encode_context_character_string(apdu, 3, &data->object.name);
apdu_len += len;
} else {
len = encode_context_object_id(
apdu, 2, data->object.identifier.type,
data->object.identifier.instance);
apdu_len += len;
}
return apdu_len;
}
/** Decode the Who-Has service request only.
*
* @param apdu Pointer to the received data.
* @param apdu_len Bytes valid in the receive buffer.
* @param data Pointer to the application dta to be filled in.
*
* @return Bytes decoded. */
int whohas_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_WHO_HAS_DATA *data)
/**
* @brief Encode Who-Has-Request service
* @param apdu application data unit buffer
* @param apdu_size number of bytes available in the buffer
* @param data application data to be encoded
* @return number of bytes encoded, or zero if unable to encode or too large
*/
size_t bacnet_who_has_service_request_encode(
uint8_t *apdu, size_t apdu_size, BACNET_WHO_HAS_DATA *data)
{
size_t apdu_len = 0; /* total length of the apdu, return value */
apdu_len = bacnet_who_has_request_encode(NULL, data);
if (apdu_len > apdu_size) {
apdu_len = 0;
} else {
apdu_len = bacnet_who_has_request_encode(apdu, data);
}
return apdu_len;
}
/**
* @brief Encode Who-Has service - use -1 for limit for unlimited
* @param apdu Pointer to the APDU.
* @param data Pointer to the Who-Has application data.
* @return Bytes encoded.
*/
int whohas_encode_apdu(uint8_t *apdu, BACNET_WHO_HAS_DATA *data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_WHO_HAS; /* service choice */
}
len = 2;
apdu_len += len;
if (apdu) {
apdu += len;
}
len = bacnet_who_has_request_encode(apdu, data);
if (len > 0) {
apdu_len += len;
} else {
apdu_len = 0;
}
return apdu_len;
}
/**
* @brief Decode the Who-Has service request only.
* @param apdu application protocol data unit buffer
* @param apdu_size number of valid bytes in the receive buffer.
* @param data application data to be decoded
* @return number of bytes decoded, or BACNET_STATUS_ERROR on error
*/
int whohas_decode_service_request(
uint8_t *apdu, unsigned apdu_size, BACNET_WHO_HAS_DATA *data)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE;
uint32_t decoded_instance = 0;
BACNET_CHARACTER_STRING *char_string = NULL;
if (apdu_len && data) {
if (apdu_size == 0) {
/* message too short */
return BACNET_STATUS_ERROR;
}
/* If the 'Device Instance Range Low Limit' parameter is present, then
the 'Device Instance Range High Limit' parameter shall also be present*/
/* device-instance-range-low-limit [0] Unsigned OPTIONAL */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &unsigned_value);
if (len > 0) {
apdu_len += len;
/* optional limits - must be used as a pair */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
if ((unsigned)len < apdu_len) {
if (unsigned_value <= BACNET_MAX_INSTANCE) {
data->low_limit = unsigned_value;
}
if (!decode_is_context_tag(&apdu[len], 1)) {
return -1;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_unsigned(
&apdu[len], len_value, &unsigned_value);
if (unsigned_value <= BACNET_MAX_INSTANCE) {
data->high_limit = unsigned_value;
}
}
}
if (unsigned_value <= BACNET_MAX_INSTANCE) {
/* (0..4194303) */
if (data) {
data->low_limit = unsigned_value;
}
} else {
/* parameter out of range */
return BACNET_STATUS_ERROR;
}
/* device-instance-range-high-limit [1] Unsigned OPTIONAL */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value);
if (len > 0) {
apdu_len += len;
if (unsigned_value <= BACNET_MAX_INSTANCE) {
/* (0..4194303) */
if (data) {
data->high_limit = unsigned_value;
}
} else {
/* parameter out of range */
return BACNET_STATUS_ERROR;
}
} else {
/* missing required parameters */
return BACNET_STATUS_ERROR;
}
} else if (len == 0) {
/* If the 'Device Instance Range Low Limit' and
'Device Instance Range High Limit' parameters are omitted,
then all devices that receive this message are
qualified to respond with an I-Have service request. */
/* use -1 as limit for unlimited */
if (data) {
data->low_limit = -1;
data->high_limit = -1;
}
/* object id */
if ((unsigned)len < apdu_len) {
if (decode_is_context_tag(&apdu[len], 2)) {
data->is_object_name = false;
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_object_id(&apdu[len], &decoded_type,
&data->object.identifier.instance);
data->object.identifier.type = decoded_type;
}
}
/* object name */
else if (decode_is_context_tag(&apdu[len], 3)) {
} else {
/* malformed */
return BACNET_STATUS_ERROR;
}
/* object-identifier [2] BACnetObjectIdentifier, CHOICE */
len = bacnet_object_id_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 2, &decoded_type,
&decoded_instance);
if (len > 0) {
apdu_len += len;
if (data) {
data->is_object_name = false;
data->object.identifier.type = decoded_type;
data->object.identifier.instance = decoded_instance;
}
} else if (len == 0) {
/* object-name [3] CharacterString, CHOICE */
if (data) {
char_string = &data->object.name;
}
len = bacnet_character_string_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 3, char_string);
if (len > 0) {
apdu_len += len;
if (data) {
data->is_object_name = true;
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_character_string(
&apdu[len], len_value, &data->object.name);
}
} else {
/* missing required parameters */
return -1;
}
} else {
/* message too short */
return -1;
/* malformed */
return BACNET_STATUS_ERROR;
}
} else {
/* missing required parameters */
return BACNET_STATUS_ERROR;
}
return len;
return apdu_len;
}