Feature/safe decode npdu (#84)

* Added safe version to decode npdu

* Calling safe decoder function.

* Added comments and apdu_len checks.

* Fixed signed/unsigned warnings.
This commit is contained in:
Roy Schneider
2020-05-11 15:30:18 +02:00
committed by GitHub
parent ec923e51a3
commit 094ac1db00
5 changed files with 170 additions and 77 deletions
+17 -2
View File
@@ -54,6 +54,14 @@
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
/** @brief Encode application data given by a pointer into the APDU.
* Return the number encoded bytes.
*
* @param apdu Pointer to the buffer to encode to.
* @param value Pointer to the application data.
*
* @return Bytes encoded.
*/
int bacapp_encode_application_data( int bacapp_encode_application_data(
uint8_t *apdu, BACNET_APPLICATION_DATA_VALUE *value) uint8_t *apdu, BACNET_APPLICATION_DATA_VALUE *value)
{ {
@@ -395,8 +403,15 @@ bool bacapp_decode_application_data_safe(uint8_t *new_apdu,
return ret; return ret;
} }
/* Decode the data and /** @brief Decode the data and
return the number of octets consumed. */ * return the number of octets consumed.
*
* @param apdu Pointer to the received data.
* @param tag_data_type Data type to be decoded.
* @param len_value_type Length of the data in bytes.
*
* @return Count of bytes decoded.
*/
int bacapp_decode_data_len( int bacapp_decode_data_len(
uint8_t *apdu, uint8_t tag_data_type, uint32_t len_value_type) uint8_t *apdu, uint8_t tag_data_type, uint32_t len_value_type)
{ {
+1 -1
View File
@@ -76,7 +76,7 @@ void npdu_handler(BACNET_ADDRESS *src, /* source address */
/* only handle the version that we know how to handle */ /* only handle the version that we know how to handle */
if (pdu[0] == BACNET_PROTOCOL_VERSION) { if (pdu[0] == BACNET_PROTOCOL_VERSION) {
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); apdu_offset = bacnet_npdu_decode(&pdu[0], pdu_len, &dest, src, &npdu_data);
if (npdu_data.network_layer_message) { if (npdu_data.network_layer_message) {
/*FIXME: network layer message received! Handle it! */ /*FIXME: network layer message received! Handle it! */
#if PRINT_ENABLED #if PRINT_ENABLED
+49 -3
View File
@@ -286,6 +286,7 @@ void npdu_encode_npdu_data(BACNET_NPDU_DATA *npdu_data,
} }
} }
/** Decode the NPDU portion of a received message, particularly the NCPI byte. /** Decode the NPDU portion of a received message, particularly the NCPI byte.
* The Network Layer Protocol Control Information byte is described * The Network Layer Protocol Control Information byte is described
* in section 6.2.2 of the BACnet standard. * in section 6.2.2 of the BACnet standard.
@@ -313,6 +314,39 @@ int npdu_decode(uint8_t *npdu,
BACNET_ADDRESS *dest, BACNET_ADDRESS *dest,
BACNET_ADDRESS *src, BACNET_ADDRESS *src,
BACNET_NPDU_DATA *npdu_data) BACNET_NPDU_DATA *npdu_data)
{
return bacnet_npdu_decode(npdu, MAX_NPDU, dest, src, npdu_data);
}
/** Decode the NPDU portion of a received message, particularly the NCPI byte.
* The Network Layer Protocol Control Information byte is described
* in section 6.2.2 of the BACnet standard.
* @param npdu [in] Buffer holding the received NPDU header bytes (must be at
* least 2)
* @param pdu_len [in] Length of the received data to prevent overruns.
* @param dest [out] Returned with routing destination information if the NPDU
* has any and if this points to non-null storage for it.
* If dest->net and dest->len are 0 on return, there is no
* routing destination information.
* @param src [out] Returned with routing source information if the NPDU
* has any and if this points to non-null storage for it.
* If src->net and src->len are 0 on return, there is no
* routing source information.
* This src describes the original source of the message when
* it had to be routed to reach this BACnet Device.
* @param npdu_data [out] Returns a filled-out structure with information
* decoded from the NCPI and other NPDU
* bytes.
* @return On success, returns the number of bytes which were decoded from the
* NPDU section; if this is a network layer message, there may
* be more bytes left in the NPDU; if not a network msg, the APDU follows. If 0
* or negative, there were problems with the data or arguments.
*/
int bacnet_npdu_decode(uint8_t *npdu,
uint16_t pdu_len,
BACNET_ADDRESS *dest,
BACNET_ADDRESS *src,
BACNET_NPDU_DATA *npdu_data)
{ {
int len = 0; /* return value - number of octets loaded in this function */ int len = 0; /* return value - number of octets loaded in this function */
uint8_t i = 0; /* counter */ uint8_t i = 0; /* counter */
@@ -322,7 +356,7 @@ int npdu_decode(uint8_t *npdu,
uint8_t dlen = 0; uint8_t dlen = 0;
uint8_t mac_octet = 0; uint8_t mac_octet = 0;
if (npdu && npdu_data) { if (npdu && npdu_data && (pdu_len >= 2)) {
/* Protocol Version */ /* Protocol Version */
npdu_data->protocol_version = npdu[0]; npdu_data->protocol_version = npdu[0];
/* control octet */ /* control octet */
@@ -356,6 +390,7 @@ int npdu_decode(uint8_t *npdu,
/* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */
/* DLEN > 0 specifies length of DADR field */ /* DLEN > 0 specifies length of DADR field */
if (npdu[1] & BIT(5)) { if (npdu[1] & BIT(5)) {
if (pdu_len >= (len + 3)) {
len += decode_unsigned16(&npdu[len], &dest_net); len += decode_unsigned16(&npdu[len], &dest_net);
/* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */
/* DLEN > 0 specifies length of DADR field */ /* DLEN > 0 specifies length of DADR field */
@@ -365,7 +400,7 @@ int npdu_decode(uint8_t *npdu,
dest->len = dlen; dest->len = dlen;
} }
if (dlen) { if (dlen) {
if (dlen > MAX_MAC_LEN) { if ((dlen > MAX_MAC_LEN) || (pdu_len < (len + dlen))) {
/* address is too large could be a malformed message */ /* address is too large could be a malformed message */
return -1; return -1;
} }
@@ -378,6 +413,7 @@ int npdu_decode(uint8_t *npdu,
} }
} }
} }
}
/* zero out the destination address */ /* zero out the destination address */
else if (dest) { else if (dest) {
dest->net = 0; dest->net = 0;
@@ -390,6 +426,7 @@ int npdu_decode(uint8_t *npdu,
/* 0 = SNET, SLEN, and SADR absent */ /* 0 = SNET, SLEN, and SADR absent */
/* 1 = SNET, SLEN, and SADR present */ /* 1 = SNET, SLEN, and SADR present */
if (npdu[1] & BIT(3)) { if (npdu[1] & BIT(3)) {
if (pdu_len >= (len + 3)) {
len += decode_unsigned16(&npdu[len], &src_net); len += decode_unsigned16(&npdu[len], &src_net);
/* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */ /* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */
/* SLEN > 0 specifies length of SADR field */ /* SLEN > 0 specifies length of SADR field */
@@ -399,7 +436,7 @@ int npdu_decode(uint8_t *npdu,
src->len = slen; src->len = slen;
} }
if (slen) { if (slen) {
if (slen > MAX_MAC_LEN) { if ((slen > MAX_MAC_LEN) || (pdu_len < (len + slen))) {
/* address is too large could be a malformed message */ /* address is too large could be a malformed message */
return -1; return -1;
} }
@@ -411,6 +448,7 @@ int npdu_decode(uint8_t *npdu,
} }
} }
} }
}
} else if (src) { } else if (src) {
/* Clear the net number, with one exception: if the receive() /* Clear the net number, with one exception: if the receive()
* function set it to BACNET_BROADCAST_NETWORK, (eg, for * function set it to BACNET_BROADCAST_NETWORK, (eg, for
@@ -428,20 +466,28 @@ int npdu_decode(uint8_t *npdu,
/* destined for a remote network, i.e., if DNET is present. */ /* destined for a remote network, i.e., if DNET is present. */
/* This is a one-octet field that is initialized to a value of 0xff. */ /* This is a one-octet field that is initialized to a value of 0xff. */
if (dest_net) { if (dest_net) {
if (pdu_len > len) {
npdu_data->hop_count = npdu[len++]; npdu_data->hop_count = npdu[len++];
} else { } else {
npdu_data->hop_count = 0; npdu_data->hop_count = 0;
} }
} else {
npdu_data->hop_count = 0;
}
/* Indicates that the NSDU conveys a network layer message. */ /* Indicates that the NSDU conveys a network layer message. */
/* Message Type field is present. */ /* Message Type field is present. */
if (npdu_data->network_layer_message) { if (npdu_data->network_layer_message) {
if (pdu_len > len) {
npdu_data->network_message_type = npdu_data->network_message_type =
(BACNET_NETWORK_MESSAGE_TYPE)npdu[len++]; (BACNET_NETWORK_MESSAGE_TYPE)npdu[len++];
/* Message Type field contains a value in the range 0x80 - 0xFF, */ /* Message Type field contains a value in the range 0x80 - 0xFF, */
/* then a Vendor ID field shall be present */ /* then a Vendor ID field shall be present */
if (npdu_data->network_message_type >= 0x80) { if (npdu_data->network_message_type >= 0x80) {
if (pdu_len >= (len + 2)) {
len += decode_unsigned16(&npdu[len], &npdu_data->vendor_id); len += decode_unsigned16(&npdu[len], &npdu_data->vendor_id);
} }
}
}
} else { } else {
/* Since npdu_data->network_layer_message is false, /* Since npdu_data->network_layer_message is false,
* it doesn't much matter what we set here; this is safe: */ * it doesn't much matter what we set here; this is safe: */
+8
View File
@@ -95,6 +95,14 @@ extern "C" {
BACNET_ADDRESS * src, BACNET_ADDRESS * src,
BACNET_NPDU_DATA * npdu_data); BACNET_NPDU_DATA * npdu_data);
BACNET_STACK_EXPORT
int bacnet_npdu_decode(
uint8_t * npdu,
uint16_t pdu_len,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src,
BACNET_NPDU_DATA * npdu_data);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */
+28 -4
View File
@@ -39,8 +39,12 @@
/** @file whohas.c Encode/Decode Who-Has requests */ /** @file whohas.c Encode/Decode Who-Has requests */
/* encode service - use -1 for limit for unlimited */ /** Encode Who-Hasservice - 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 whohas_encode_apdu(uint8_t *apdu, BACNET_WHO_HAS_DATA *data)
{ {
int len = 0; /* length of each encoding */ int len = 0; /* length of each encoding */
@@ -75,7 +79,13 @@ int whohas_encode_apdu(uint8_t *apdu, BACNET_WHO_HAS_DATA *data)
return apdu_len; return apdu_len;
} }
/* decode the service request only */ /** 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( int whohas_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_WHO_HAS_DATA *data) uint8_t *apdu, unsigned apdu_len, BACNET_WHO_HAS_DATA *data)
{ {
@@ -90,7 +100,9 @@ int whohas_decode_service_request(
if (decode_is_context_tag(&apdu[len], 0)) { if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value( len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value); &apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_unsigned(&apdu[len], len_value, &unsigned_value); len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
if ((unsigned)len < apdu_len) {
if (unsigned_value <= BACNET_MAX_INSTANCE) { if (unsigned_value <= BACNET_MAX_INSTANCE) {
data->low_limit = unsigned_value; data->low_limit = unsigned_value;
} }
@@ -99,33 +111,45 @@ int whohas_decode_service_request(
} }
len += decode_tag_number_and_value( len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value); &apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_unsigned(&apdu[len], len_value, &unsigned_value); len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
if (unsigned_value <= BACNET_MAX_INSTANCE) { if (unsigned_value <= BACNET_MAX_INSTANCE) {
data->high_limit = unsigned_value; data->high_limit = unsigned_value;
} }
}
}
}
} else { } else {
data->low_limit = -1; data->low_limit = -1;
data->high_limit = -1; data->high_limit = -1;
} }
/* object id */ /* object id */
if ((unsigned)len < apdu_len) {
if (decode_is_context_tag(&apdu[len], 2)) { if (decode_is_context_tag(&apdu[len], 2)) {
data->is_object_name = false; data->is_object_name = false;
len += decode_tag_number_and_value( len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value); &apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_object_id( len += decode_object_id(
&apdu[len], &decoded_type, &data->object.identifier.instance); &apdu[len], &decoded_type, &data->object.identifier.instance);
data->object.identifier.type = decoded_type; data->object.identifier.type = decoded_type;
} }
}
/* object name */ /* object name */
else if (decode_is_context_tag(&apdu[len], 3)) { else if (decode_is_context_tag(&apdu[len], 3)) {
data->is_object_name = true; data->is_object_name = true;
len += decode_tag_number_and_value( len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value); &apdu[len], &tag_number, &len_value);
if ((unsigned)len < apdu_len) {
len += decode_character_string( len += decode_character_string(
&apdu[len], len_value, &data->object.name); &apdu[len], len_value, &data->object.name);
} }
} else {
/* missing required parameters */ /* missing required parameters */
else { return -1;
}
} else {
/* message too short */
return -1; return -1;
} }
} }