Feature/app data buffer check (#79)

* Added comments and buffer overflow checks

* Removed backslashs from C-code.
This commit is contained in:
Roy Schneider
2020-04-28 15:45:03 +02:00
committed by GitHub
parent 89929ee802
commit 0abcbea971
20 changed files with 1588 additions and 635 deletions
+17
View File
@@ -62,6 +62,15 @@ PROP_TAG_DATA bacnet_object_device_property_tag_map[] = {
{ -1, -1 }
};
/**
* Search for the given index in the data list.
*
* @param data_list Pointer to the list.
* @param index Index to search for.
* @param default_ret Default return value.
*
* @return Value found or the default value.
*/
signed bacprop_tag_by_index_default(
PROP_TAG_DATA *data_list, signed index, signed default_ret)
{
@@ -80,6 +89,14 @@ signed bacprop_tag_by_index_default(
return pUnsigned ? pUnsigned : default_ret;
}
/**
* Return the value of the given property, if available.
*
* @param type Object type
* @param prop Property
*
* @return Value found or -1
*/
signed bacprop_property_tag(BACNET_OBJECT_TYPE type, signed prop)
{
switch (type) {
+6 -5
View File
@@ -723,22 +723,23 @@ bool octetstring_init(
{
bool status = false; /* return value */
size_t i; /* counter */
uint8_t *pb = NULL;
if (octet_string && (length <= MAX_OCTET_STRING_BYTES)) {
octet_string->length = 0;
if (value) {
pb = octet_string->value;
for (i = 0; i < MAX_OCTET_STRING_BYTES; i++) {
if (i < length) {
octet_string->value[i] = value[i];
*pb = value[i];
} else {
octet_string->value[i] = 0;
*pb = 0;
}
pb++;
}
octet_string->length = length;
} else {
for (i = 0; i < MAX_OCTET_STRING_BYTES; i++) {
octet_string->value[i] = 0;
}
memset(octet_string->value, 0, MAX_OCTET_STRING_BYTES);
}
status = true;
}
+199 -79
View File
@@ -84,11 +84,23 @@ static struct Address_Cache_Entry {
#define BAC_ADDR_SHORT_TIME BAC_ADDR_SECS_1HOUR
#define BAC_ADDR_FOREVER 0xFFFFFFFF /* Permenant entry */
/**
* @brief Set the index of the first (top) address being protected.
*
* @param top_protected_entry_index top protected index [0..n-1]
*/
void address_protected_entry_index_set(uint32_t top_protected_entry_index)
{
Top_Protected_Entry = top_protected_entry_index;
if (top_protected_entry_index <= (MAX_ADDRESS_CACHE - 1)) {
Top_Protected_Entry = top_protected_entry_index;
}
}
/**
* @brief Set the address of our own device.
*
* @param own_id Own device id
*/
void address_own_device_id_set(uint32_t own_id)
{
Own_Device_ID = own_id;
@@ -182,9 +194,8 @@ void address_remove_device(uint32_t device_id)
* entry available to free up. Does not check for free entries as it is
* assumed we are calling this due to the lack of those.
*
* @return Candidate for deletation.
* @return Pointer to the entry that has been removed or NULL.
*/
static struct Address_Cache_Entry *address_remove_oldest(void)
{
struct Address_Cache_Entry *pMatch;
@@ -383,11 +394,10 @@ static void address_file_init(const char *pFilename)
}
#endif
/****************************************************************************
* Clear down the cache and make sure the full complement of entries are *
* available. Assume no persistance of memory. *
****************************************************************************/
/**
* Clear down the cache and make sure the full complement of entries are
* available. Assume no persistance of memory.
*/
void address_init(void)
{
struct Address_Cache_Entry *pMatch;
@@ -405,14 +415,13 @@ void address_init(void)
return;
}
/****************************************************************************
* Clear down the cache of any non bound, expired or reserved entries. *
* Leave static and unexpired bound entries alone. For use where the cache *
* is held in persistant memory which can survive a reset or power cycle. *
* This reduces the network traffic on restarts as the cache will have much *
* of its entries intact. *
****************************************************************************/
/**
* Clear down the cache of any non bound, expired or reserved entries.
* Leave static and unexpired bound entries alone. For use where the cache
* is held in persistant memory which can survive a reset or power cycle.
* This reduces the network traffic on restarts as the cache will have much
* of its entries intact.
*/
void address_init_partial(void)
{
struct Address_Cache_Entry *pMatch;
@@ -441,13 +450,16 @@ void address_init_partial(void)
return;
}
/****************************************************************************
* Set the TTL info for the given device entry. If it is a bound entry we *
* set it to static or normal and can change the TTL. If it is unbound we *
* can only set the TTL. This is done as a seperate function at the moment *
* to avoid breaking the current API. *
****************************************************************************/
/**
* Set the TTL info for the given device entry. If it is a bound entry we
* set it to static or normal and can change the TTL. If it is unbound we
* can only set the TTL. This is done as a seperate function at the moment
* to avoid breaking the current API.
*
* @param device_id Device-Id
* @param TimeOut Timeout in seconds.
* @param StaticFlag Flag indicating if being a static address.
*/
void address_set_device_TTL(
uint32_t device_id, uint32_t TimeOut, bool StaticFlag)
{
@@ -476,6 +488,13 @@ void address_set_device_TTL(
}
}
/**
* Return the cached address for the given device-id
*
* @param device_id Device-Id
* @param max_apdu Pointer to a variable, taking the maximum APDU size.
* @param src Pointer to address structure for return.
*/
bool address_get_by_device(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
{
@@ -500,8 +519,14 @@ bool address_get_by_device(
return found;
}
/* find a device id from a given MAC address */
/**
* Find a device id from a given MAC address.
*
* @param src Pointer to address structure to search for.
* @param device_id Pointer to the device id variable for return.
*
* @return true/false
*/
bool address_get_device_id(BACNET_ADDRESS *src, uint32_t *device_id)
{
struct Address_Cache_Entry *pMatch;
@@ -525,6 +550,13 @@ bool address_get_device_id(BACNET_ADDRESS *src, uint32_t *device_id)
return found;
}
/**
* Add a device using the given id, max_apdu and address.
*
* @param device_id Pointer to the device id variable for return.
* @param max_apdu Maximum APDU size.
* @param src Pointer to address structure to add.
*/
void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
{
bool found = false; /* return value */
@@ -543,6 +575,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
/* existing device or bind request outstanding - update address */
pMatch = Address_Cache;
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
/* Device already in the list, then update the values. */
if (((pMatch->Flags & BAC_ADDR_IN_USE) != 0) &&
(pMatch->device_id == device_id)) {
bacnet_address_copy(&pMatch->address, src);
@@ -572,7 +605,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
pMatch++;
}
/* new device - add to cache if there is room */
/* New device - add to cache if there is room. */
if (!found) {
pMatch = Address_Cache;
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
@@ -591,7 +624,7 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
}
}
/* See if we can squeeze it in */
/* If adding has failed, see if we can squeeze it in by removed the oldest entry. */
if (!found) {
pMatch = address_remove_oldest();
if (pMatch != NULL) {
@@ -606,8 +639,19 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
return;
}
/* returns true if device is already bound */
/* also returns the address and max apdu if already bound */
/**
* Check if the device is in the list. If yes, return the values.
* Otherwise add the device to the list.
* Returns true if device is already bound.
* Also returns the address and max apdu if already bound.
*
* @param device_id Pointer to the device id variable for return.
* @param device_ttl Pointer to a variable taking the time to live in seconds.
* @param max_apdu Max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true if device is already bound
*/
bool address_device_bind_request(uint32_t device_id,
uint32_t *device_ttl,
unsigned *max_apdu,
@@ -675,14 +719,31 @@ bool address_device_bind_request(uint32_t device_id,
return (false);
}
/* returns true if device is already bound */
/* also returns the address and max apdu if already bound */
/**
* Check if the device is in the list. If yes, return the values.
* Otherwise add the device to the list.
* Returns true if device is already bound.
* Also returns the address and max apdu if already bound.
*
* @param device_id Pointer to the device id variable for return.
* @param max_apdu Max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true if device is already bound
*/
bool address_bind_request(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
{
return address_device_bind_request(device_id, NULL, max_apdu, src);
}
/**
* For an existing device add a binding.
*
* @param device_id Pointer to the device id variable for return.
* @param max_apdu Max APDU size of the device.
* @param src Pointer to the BACnet address.
*/
void address_add_binding(
uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
{
@@ -709,6 +770,17 @@ void address_add_binding(
return;
}
/**
* Return the device information from the given index in the table.
*
* @param index Table index [0..MAX_ADDRESS_CACHE-1]
* @param device_id Pointer to the variable taking the device id.
* @param device_ttl Pointer to the variable taking the Time To Life for the device.
* @param max_apdu Pointer to the variable taking the max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true/false
*/
bool address_device_get_by_index(unsigned index,
uint32_t *device_id,
uint32_t *device_ttl,
@@ -741,6 +813,16 @@ bool address_device_get_by_index(unsigned index,
return found;
}
/**
* Return the device information from the given index in the table.
*
* @param index Table index [0..MAX_ADDRESS_CACHE-1]
* @param device_id Pointer to the variable taking the device id.
* @param max_apdu Pointer to the variable taking the max APDU size of the device.
* @param src Pointer to the BACnet address.
*
* @return true/false
*/
bool address_get_by_index(unsigned index,
uint32_t *device_id,
unsigned *max_apdu,
@@ -749,6 +831,11 @@ bool address_get_by_index(unsigned index,
return address_device_get_by_index(index, device_id, NULL, max_apdu, src);
}
/**
* Return the count of cached addresses.
*
* @return A value between zero and MAX_ADDRESS_CACHE.
*/
unsigned address_count(void)
{
struct Address_Cache_Entry *pMatch;
@@ -768,43 +855,55 @@ unsigned address_count(void)
return count;
}
/****************************************************************************
* Build a list of the current bindings for the device address binding *
* property. *
****************************************************************************/
/**
* Build a list of the current bindings for the device address binding
* property. Basically encode the address list to be send out.
*
* @param apdu Pointer to the APDU
* @param apdu_len Remaining buffer size.
*
* @return Count of encoded bytes.
*/
int address_list_encode(uint8_t *apdu, unsigned apdu_len)
{
int iLen = 0;
struct Address_Cache_Entry *pMatch;
BACNET_OCTET_STRING MAC_Address;
/* FIXME: I really shouild check the length remaining here but it is
fairly pointless until we have the true length remaining in
the packet to work with as at the moment it is just MAX_APDU */
(void)apdu_len;
/* look for matching address */
/* Look for matching address. */
pMatch = Address_Cache;
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) ==
BAC_ADDR_IN_USE) {
iLen += encode_application_object_id(
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
iLen +=
encode_application_unsigned(&apdu[iLen], pMatch->address.net);
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
iLen += encode_application_unsigned(&apdu[iLen], pMatch->address.net);
if ((unsigned)iLen >= apdu_len) {
break;
}
/* pick the appropriate type of entry from the cache */
if (pMatch->address.len != 0) {
/* BAC */
if ((unsigned)(iLen + pMatch->address.len) >= apdu_len) {
break;
}
octetstring_init(
&MAC_Address, pMatch->address.adr, pMatch->address.len);
iLen +=
encode_application_octet_string(&apdu[iLen], &MAC_Address);
iLen += encode_application_octet_string(&apdu[iLen], &MAC_Address);
} else {
/* MAC*/
if ((unsigned)(iLen + pMatch->address.mac_len) >= apdu_len) {
break;
}
octetstring_init(
&MAC_Address, pMatch->address.mac, pMatch->address.mac_len);
iLen +=
encode_application_octet_string(&apdu[iLen], &MAC_Address);
iLen += encode_application_octet_string(&apdu[iLen], &MAC_Address);
}
/* Any space left? */
if ((unsigned)iLen >= apdu_len) {
break;
}
}
pMatch++;
@@ -813,29 +912,33 @@ int address_list_encode(uint8_t *apdu, unsigned apdu_len)
return (iLen);
}
/****************************************************************************
* Build a list of the current bindings for the device address binding *
* property as required for the ReadsRange functionality. *
* We assume we only get called for "Read All" or "By Position" requests. *
* *
* We need to treat the address cache as a contiguous array but in reality *
* it could be sparsely populated. We can get the count but we can only *
* extract entries by doing a linear scan starting from the first entry in *
* the cache and picking them off one by one. *
* *
* We do assume the list cannot change whilst we are accessing it so would *
* not be multithread safe if there are other tasks that change the cache. *
* *
/**
* Build a list of the current bindings for the device address binding
* property as required for the ReadsRange functionality.
* We assume we only get called for "Read All" or "By Position" requests.
*
* We need to treat the address cache as a contiguous array but in reality
* it could be sparsely populated. We can get the count but we can only
* extract entries by doing a linear scan starting from the first entry in
* the cache and picking them off one by one.
*
* We do assume the list cannot change whilst we are accessing it so would
* not be multithread safe if there are other tasks that change the cache.
*
* We take the simple approach here to filling the buffer by taking a max *
* size for a single entry and then stopping if there is less than that *
* left in the buffer. You could build each entry in a seperate buffer and *
* determine the exact length before copying but this is time consuming, *
* requires more memory and would probably only let you sqeeeze one more *
* entry in on occasion. The value is calculated as 5 bytes for the device *
* ID + 3 bytes for the network number and nine bytes for the MAC address *
* oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10). *
****************************************************************************/
* size for a single entry and then stopping if there is less than that
* left in the buffer. You could build each entry in a seperate buffer and
* determine the exact length before copying but this is time consuming,
* requires more memory and would probably only let you sqeeeze one more
* entry in on occasion. The value is calculated as 5 bytes for the device
* ID + 3 bytes for the network number and nine bytes for the MAC address
* oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10).
*
* @param apdu Pointer to the encode buffer.
* @param pRequest Pointer to the read_range request structure.
*
* @return Bytes encoded.
*/
#define ACACHE_MAX_ENC 17 /* Maximum size of encoded cache entry, see above */
int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
@@ -851,6 +954,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
uint32_t uiTarget = 0; /* Last entry we are required to encode */
uint32_t uiRemaining = 0; /* Amount of unused space in packet */
if ((!pRequest) || (!apdu)) {
return 0;
}
/* Initialise result flags to all false */
bitstring_init(&pRequest->ResultFlags);
bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false);
@@ -861,7 +968,7 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
pRequest->ItemCount = 0; /* Start out with nothing */
uiTotal = address_count(); /* What do we have to work with here ? */
if (uiTotal == 0) { /* Bail out now if nowt */
if (uiTotal == 0) { /* Bail out now if not. */
return (0);
}
@@ -921,6 +1028,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
BAC_ADDR_IN_USE) { /* Find first bound entry */
pMatch++;
/* Shall not happen as the count has been checked first. */
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
return(0); /* Issue with the table. */
}
}
/* Seek to start position */
@@ -932,6 +1043,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
} else {
pMatch++;
}
/* Shall not happen as the count has been checked first. */
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
return(0); /* Issue with the table. */
}
}
uiFirst = uiIndex; /* Record where we started from */
@@ -976,6 +1091,10 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
BAC_ADDR_IN_USE) { /* Find next bound entry */
pMatch++;
/* Can normally not happen. */
if (pMatch > &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
return(0); /* Issue with the table. */
}
}
}
@@ -991,15 +1110,16 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
return (iLen);
}
/****************************************************************************
* Scan the cache and eliminate any expired entries. Should be called *
* periodically to ensure the cache is managed correctly. If this function *
* is never called at all the whole cache is effectivly rendered static and *
* entries never expire unless explictely deleted. *
****************************************************************************/
/**
* Scan the cache and eliminate any expired entries. Should be called
* periodically to ensure the cache is managed correctly. If this function
* is never called at all the whole cache is effectivly rendered static and
* entries never expire unless explictely deleted.
*
* @param uSeconds Approximate number of seconds since last call to this function
*/
void address_cache_timer(uint16_t uSeconds)
{ /* Approximate number of seconds since last call to this function */
{
struct Address_Cache_Entry *pMatch;
pMatch = Address_Cache;
+1 -1
View File
@@ -422,7 +422,7 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) || \
if ((wp_data->application_data == NULL) ||
(wp_data->application_data_len == 0)) {
return false;
}
+2 -2
View File
@@ -384,7 +384,7 @@ int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
if (rpdata == NULL) {
return 0;
}
if ((rpdata->application_data == NULL) || \
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
@@ -488,7 +488,7 @@ bool CharacterString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
if (wp_data == NULL) {
return false;
}
if ((wp_data->application_data == NULL) || \
if ((wp_data->application_data == NULL) ||
(wp_data->application_data_len == 0)) {
return false;
}
+236 -211
View File
@@ -182,6 +182,7 @@ void handler_read_property_multiple(uint8_t *service_request,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
bool berror = false;
int len = 0;
uint16_t copy_len = 0;
uint16_t decode_len = 0;
@@ -194,234 +195,258 @@ void handler_read_property_multiple(uint8_t *service_request,
int npdu_len = 0;
int error = 0;
/* jps_debug - see if we are utilizing all the buffer */
/* memset(&Handler_Transmit_Buffer[0], 0xff,
* sizeof(Handler_Transmit_Buffer)); */
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len = npdu_encode_pdu(
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
if (service_data->segmented_message) {
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
#endif
goto RPM_FAILURE;
}
/* decode apdu request & encode apdu reply
encode complex ack, invoke id, service choice */
apdu_len = rpm_ack_encode_apdu_init(
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id);
for (;;) {
/* Start by looking for an object ID */
len = rpm_decode_object_id(
&service_request[decode_len], service_len - decode_len, &rpmdata);
if (len >= 0) {
/* Got one so skip to next stage */
decode_len += len;
} else {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
goto RPM_FAILURE;
}
if (service_data && (service_len > 0)) {
/* jps_debug - see if we are utilizing all the buffer */
/* memset(&Handler_Transmit_Buffer[0], 0xff,
* sizeof(Handler_Transmit_Buffer)); */
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
npdu_len = npdu_encode_pdu(
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
/* Test for case of indefinite Device object instance */
if ((rpmdata.object_type == OBJECT_DEVICE) &&
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
rpmdata.object_instance = Device_Object_Instance_Number();
}
/* Stick this object id into the reply - if it will fit */
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Response too big!\r\n");
#endif
if (service_data->segmented_message) {
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
#if PRINT_ENABLED
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
#endif
} else {
/* decode apdu request & encode apdu reply
encode complex ack, invoke id, service choice */
apdu_len = rpm_ack_encode_apdu_init(
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id);
apdu_len += copy_len;
/* do each property of this object of the RPM request */
for (;;) {
/* Fetch a property */
len = rpm_decode_object_property(&service_request[decode_len],
service_len - decode_len, &rpmdata);
if (len < 0) {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
goto RPM_FAILURE;
}
decode_len += len;
/* handle the special properties */
if ((rpmdata.object_property == PROP_ALL) ||
(rpmdata.object_property == PROP_REQUIRED) ||
(rpmdata.object_property == PROP_OPTIONAL)) {
struct special_property_list_t property_list;
unsigned property_count = 0;
unsigned index = 0;
BACNET_PROPERTY_ID special_object_property;
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
/* No array index options for this special property.
Encode error for this object property response */
len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
rpmdata.object_property, rpmdata.array_index);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full to encode property!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += len;
len = rpm_ack_encode_apdu_object_property_error(
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode error!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
}
apdu_len += len;
for (;;) {
/* Start by looking for an object ID */
len = rpm_decode_object_id(
&service_request[decode_len], service_len - decode_len, &rpmdata);
if (len >= 0) {
/* Got one so skip to next stage */
decode_len += len;
} else {
special_object_property = rpmdata.object_property;
Device_Objects_Property_List(rpmdata.object_type,
rpmdata.object_instance, &property_list);
property_count = RPM_Object_Property_Count(
&property_list, special_object_property);
if (property_count == 0) {
/* this only happens with the OPTIONAL property */
/* 135-2016bl-2. Clarify ReadPropertyMultiple
response on OPTIONAL when empty. */
/* If no optional properties are supported then
an empty 'List of Results' shall be returned
for the specified property.*/
} else {
for (index = 0; index < property_count; index++) {
rpmdata.object_property = RPM_Object_Property(
&property_list, special_object_property, index);
len = RPM_Encode_Property(
&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
berror = true;
break;
}
/* Test for case of indefinite Device object instance */
if ((rpmdata.object_type == OBJECT_DEVICE) &&
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
rpmdata.object_instance = Device_Object_Instance_Number();
}
/* Stick this object id into the reply - if it will fit */
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
copy_len =
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Response too big!\r\n");
#endif
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break;
}
apdu_len += copy_len;
/* do each property of this object of the RPM request */
for (;;) {
/* Fetch a property */
len = rpm_decode_object_property(&service_request[decode_len],
service_len - decode_len, &rpmdata);
if (len < 0) {
/* bad encoding - skip to error/reject/abort handling */
#if PRINT_ENABLED
fprintf(stderr, "RPM: Bad Encoding.\n");
#endif
error = len;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
decode_len += len;
/* handle the special properties */
if ((rpmdata.object_property == PROP_ALL) ||
(rpmdata.object_property == PROP_REQUIRED) ||
(rpmdata.object_property == PROP_OPTIONAL)) {
struct special_property_list_t property_list;
unsigned property_count = 0;
unsigned index = 0;
BACNET_PROPERTY_ID special_object_property;
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
/* No array index options for this special property.
Encode error for this object property response */
len = rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
rpmdata.object_property, rpmdata.array_index);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for property!\r\n");
#endif
error = len;
goto RPM_FAILURE;
stderr, "RPM: Too full to encode property!\r\n");
#endif
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
apdu_len += len;
len = rpm_ack_encode_apdu_object_property_error(
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode error!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
apdu_len += len;
} else {
special_object_property = rpmdata.object_property;
Device_Objects_Property_List(rpmdata.object_type,
rpmdata.object_instance, &property_list);
property_count = RPM_Object_Property_Count(
&property_list, special_object_property);
if (property_count == 0) {
/* This only happens with the OPTIONAL property */
/* 135-2016bl-2. Clarify ReadPropertyMultiple
response on OPTIONAL when empty. */
/* If no optional properties are supported then
an empty 'List of Results' shall be returned
for the specified property.*/
} else {
for (index = 0; index < property_count; index++) {
rpmdata.object_property = RPM_Object_Property(
&property_list, special_object_property, index);
len = RPM_Encode_Property(
&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for property!\r\n");
#endif
error = len;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
}
}
}
} else {
/* handle an individual property */
len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for individual property!\r\n");
#endif
error = len;
berror = true;
break; // The berror flag ensures that both loops will be broken!
}
}
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
/* Reached end of property list so cap the result list */
decode_len++;
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
berror = true;
break; // The berror flag ensures that both loops will be broken!
} else {
apdu_len += copy_len;
}
break; /* finished with this property list */
}
} // for(;;)
if (berror) {
break;
}
} else {
/* handle an individual property */
len = RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
if (len > 0) {
apdu_len += len;
} else {
#if PRINT_ENABLED
fprintf(
stderr, "RPM: Too full for individual property!\r\n");
#endif
error = len;
goto RPM_FAILURE;
if (decode_len >= service_len) {
/* Reached the end so finish up */
break;
}
}
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
/* Reached end of property list so cap the result list */
decode_len++;
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
copy_len = memcopy(&Handler_Transmit_Buffer[npdu_len],
&Temp_Buf[0], apdu_len, len, MAX_APDU);
if (copy_len == 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
#endif
rpmdata.error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} // for(;;)
/* If not having an error so far, check the remaining space. */
if (!berror) {
if (apdu_len > service_data->max_resp) {
/* too big for the sender - send an abort */
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
goto RPM_FAILURE;
} else {
apdu_len += copy_len;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
#endif
}
break; /* finished with this property list */
}
}
if (decode_len >= service_len) {
/* Reached the end so finish up */
break;
/* Error fallback. */
if (error) {
if (error == BACNET_STATUS_ABORT) {
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(rpmdata.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Abort!\n");
#endif
} else if (error == BACNET_STATUS_ERROR) {
apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
rpmdata.error_class, rpmdata.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Error!\n");
#endif
} else if (error == BACNET_STATUS_REJECT) {
apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(rpmdata.error_code));
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Reject!\n");
#endif
}
}
}
if (apdu_len > service_data->max_resp) {
/* too big for the sender - send an abort */
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
error = BACNET_STATUS_ABORT;
#if PRINT_ENABLED
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
#endif
goto RPM_FAILURE;
}
RPM_FAILURE:
if (error) {
if (error == BACNET_STATUS_ABORT) {
apdu_len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
abort_convert_error_code(rpmdata.error_code), true);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Abort!\n");
#endif
} else if (error == BACNET_STATUS_ERROR) {
apdu_len = bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
rpmdata.error_class, rpmdata.error_code);
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Error!\n");
#endif
} else if (error == BACNET_STATUS_REJECT) {
apdu_len = reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
service_data->invoke_id,
reject_convert_error_code(rpmdata.error_code));
#if PRINT_ENABLED
fprintf(stderr, "RPM: Sending Reject!\n");
#endif
pdu_len = apdu_len + npdu_len;
bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
}
pdu_len = apdu_len + npdu_len;
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
#endif
}
}
+1 -1
View File
@@ -136,7 +136,7 @@ void handler_write_property(uint8_t *service_request,
/* Send PDU */
pdu_len += len;
bytes_sent = datalink_send_pdu( \
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
if (bytes_sent <= 0) {
#if PRINT_ENABLED
+192 -82
View File
@@ -56,21 +56,33 @@
/* Generic node routines */
/******************************************************************** */
/* grab memory for a node */
/** Grab memory for a node (Keylist_Node).
*
* @return Pointer to the allocated memory or
* NULL under an Out Of Memory situation.
*/
static struct Keylist_Node *NodeCreate(void)
{
return calloc(1, sizeof(struct Keylist_Node));
}
/* grab memory for a list */
/** Grab memory for a list (Keylist).
*
* @return Pointer to the allocated memory or
* NULL under an Out Of Memory situation.
*/
static struct Keylist *KeylistCreate(void)
{
return calloc(1, sizeof(struct Keylist));
}
/* check to see if the array is big enough for an addition */
/* or is too big when we are deleting and we can shrink */
/* returns TRUE if success, FALSE if failed */
/** Check to see if the array is big enough for an addition
* or is too big when we are deleting and we can shrink.
*
* @param list Pointer to the list to be tested.
*
* @return Returns TRUE if success, FALSE if failed
*/
static int CheckArraySize(OS_Keylist list)
{
int new_size = 0; /* set it up so that no size change is the default */
@@ -112,13 +124,20 @@ static int CheckArraySize(OS_Keylist list)
return TRUE;
}
/* find the index of the key that we are looking for */
/* since it is sorted, we can optimize the search */
/* returns TRUE if found, and FALSE not found */
/* returns the found key and the index where it was found in parameters */
/* If the key is not found, the nearest index from the bottom will be returned,
/** Find the index of the key that we are looking for.
* Since it is sorted, we can optimize the search.
* returns TRUE if found, and FALSE not found.
* Returns the found key and the index where it was found in parameters.
* If the key is not found, the nearest index from the bottom will be returned,
* allowing the ability to find where an key should go into the list.
*
* @param list Pointer to the list
* @param key Key to search for
* @param pIndex Pointer to the variable taking the index were the key
* had been found.
*
* @return TRUE if found, and FALSE if not
*/
/* allowing the ability to find where an key should go into the list. */
static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
{
struct Keylist_Node *node; /* holds the new node */
@@ -127,7 +146,12 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
int index = 0; /* our current search place in the array */
KEY current_key = 0; /* place holder for current node key */
int status = FALSE; /* return value */
if (!list || !list->array || !list->count) {
if (!list) {
*pIndex = 0;
return (FALSE);
}
if (!list->array || !list->count) {
*pIndex = 0;
return (FALSE);
}
@@ -148,12 +172,12 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
left = index + 1;
}
} while ((key != current_key) && (left <= right));
if (key == current_key) {
status = TRUE;
*pIndex = index;
}
else {
} else {
/* where the index should be */
if (key > current_key) {
*pIndex = index + 1;
@@ -168,7 +192,15 @@ static int FindIndex(OS_Keylist list, KEY key, int *pIndex)
/******************************************************************** */
/* list data functions */
/******************************************************************** */
/* inserts a node into its sorted position */
/** Inserts a node into its sorted position.
*
* @param list Pointer to the list
* @param key Key to be inserted
* @param data Pointer to the data hold by the key.
* This pointer needs to be poiting to static memory
* as it will be stored in the list and later used
* by retrieving the key again.
*/
int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
{
struct Keylist_Node *node; /* holds the new node */
@@ -191,9 +223,7 @@ int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
for (i = list->count; i > index; i--) {
list->array[i] = list->array[i - 1];
}
}
else {
} else {
index = 0;
}
@@ -209,138 +239,199 @@ int Keylist_Data_Add(OS_Keylist list, KEY key, void *data)
return index;
}
/* deletes a node specified by its index */
/* returns the data from the node */
/** Deletes a node specified by its index
* returns the data from the node
*
* @param list Pointer to the list
* @param index Index of the key to be deleted
*
* @returns Pointer to the data of the deleted key or NULL.
*/
void *Keylist_Data_Delete_By_Index(OS_Keylist list, int index)
{
struct Keylist_Node *node;
void *data = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
data = node->data;
/* move the nodes to account for the deleted one */
if (list->count == 1) {
/* There is no node shifting to do */
} else if (index == (list->count - 1)) {
/* We are the last one */
/* There is no node shifting to do */
} else {
/* Move all the nodes down one */
int i; /* counter */
int count = list->count - 1;
for (i = index; i < count; i++) {
list->array[i] = list->array[i + 1];
if (list) {
if (list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node) {
data = node->data;
}
/* move the nodes to account for the deleted one */
if (list->count == 1) {
/* There is no node shifting to do */
} else if (index == (list->count - 1)) {
/* We are the last one */
/* There is no node shifting to do */
} else {
/* Move all the nodes down one */
int i; /* counter */
int count = list->count - 1;
for (i = index; i < count; i++) {
list->array[i] = list->array[i + 1];
}
}
list->count--;
if (node) {
free(node);
}
}
list->count--;
if (node) {
free(node);
}
/* potentially reduce the size of the array */
(void)CheckArraySize(list);
/* potentially reduce the size of the array */
(void)CheckArraySize(list);
}
}
return (data);
}
/* deletes a node specified by its key */
/* returns the data from the node */
/** Deletes a node specified by its key/
* returns the data from the node
*
* @param list Pointer to the list
* @param key Key to be deleted
*
* @returns Pointer to the data of the deleted key or NULL.
*/
void *Keylist_Data_Delete(OS_Keylist list, KEY key)
{
void *data = NULL; /* return value */
int index; /* where the node is in the array */
if (list) {
if (FindIndex(list, key, &index))
if (FindIndex(list, key, &index)) {
data = Keylist_Data_Delete_By_Index(list, index);
}
}
return data;
}
/* returns the data from last node, and removes it from the list */
/** Returns the data from last node, and
* removes it from the list.
*
* @param list Pointer to the list
*
* @return Pointer to the data that might be NULL.
*/
void *Keylist_Data_Pop(OS_Keylist list)
{
void *data = NULL; /* return value */
int index; /* position in the array */
if (list && list->count) {
index = list->count - 1;
data = Keylist_Data_Delete_By_Index(list, index);
if (list) {
if (list->count) {
index = list->count - 1;
data = Keylist_Data_Delete_By_Index(list, index);
}
}
return data;
}
/* returns the data from the node specified by key */
/** Returns the data from the node specified by key.
*
* @param list Pointer to the list
* @param key Key to be deleted
*
* @returns Pointer to the data, that might be NULL.
*/
void *Keylist_Data(OS_Keylist list, KEY key)
{
struct Keylist_Node *node = NULL;
int index = 0; /* used to look up the index of node */
if (list && list->array && list->count) {
if (FindIndex(list, key, &index))
node = list->array[index];
if (list) {
if (list->array && list->count) {
if (FindIndex(list, key, &index)) {
node = list->array[index];
}
}
}
return node ? node->data : NULL;
}
/* returns the index from the node specified by key */
/** Returns the index from the node specified by key.
*
* @param list Pointer to the list
* @param key Key whose index shall be retrieved.
*
* @return Index of the key or -1, if not found.
*/
int Keylist_Index(OS_Keylist list, KEY key)
{
int index = -1; /* used to look up the index of node */
if (list && list->array && list->count) {
if (!FindIndex(list, key, &index)) {
index = -1;
if (list) {
if (list->array && list->count) {
if (!FindIndex(list, key, &index)) {
index = -1;
}
}
}
return index;
}
/* returns the data specified by index */
/** Returns the data specified by index
*
* @param list Pointer to the list
* @param index Key whose data shall be retrieved.
*
* @return Pointer to the data that might be NULL.
*/
void *Keylist_Data_Index(OS_Keylist list, int index)
{
struct Keylist_Node *node = NULL;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count))
node = list->array[index];
if (list) {
if (list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
}
}
return node ? node->data : NULL;
}
/* return the key at the given index */
/** Return the key at the given index.
*
* @param list Pointer to the list
* @param index Index that shall be returned
*
* @return Key for the index or 0.
*/
KEY Keylist_Key(OS_Keylist list, int index)
{
KEY key = 0; /* return value */
struct Keylist_Node *node;
if (list && list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node)
key = node->key;
if (list) {
if (list->array && list->count && (index >= 0) &&
(index < list->count)) {
node = list->array[index];
if (node) {
key = node->key;
}
}
}
return key;
}
/* returns the next empty key from the list */
/** Returns the next empty key from the list.
*
* @param list Pointer to the list
* @param key Key whose index shall be retrieved.
*
* @return Next empty key or 'key=key' if there is none.
*/
KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
{
int index;
if (list) {
while (FindIndex(list, key, &index)) {
if (KEY_LAST(key))
if (KEY_LAST(key)) {
break;
}
key++;
}
}
@@ -348,29 +439,47 @@ KEY Keylist_Next_Empty_Key(OS_Keylist list, KEY key)
return key;
}
/* return the number of nodes in this list */
/** Return the number of nodes in this list.
*
* @param list Pointer to the list
*
* @return Count of key in the list.
*/
int Keylist_Count(OS_Keylist list)
{
return list->count;
int cnt = 0;
if (list) {
cnt = list->count;
}
return(cnt);
}
/******************************************************************** */
/* Public List functions */
/******************************************************************** */
/* returns head of the list or NULL on failure. */
/** Returns head of the list or NULL on failure.
*
* @return Pointer to the key list or NUL if creation failed.
*/
OS_Keylist Keylist_Create(void)
{
struct Keylist *list;
list = KeylistCreate();
if (list)
if (list) {
CheckArraySize(list);
}
return list;
}
/* delete specified list */
/** Delete specified list.
*
* @param list Pointer to the list
*/
void Keylist_Delete(OS_Keylist list)
{ /* list number to be deleted */
if (list) {
@@ -378,8 +487,9 @@ void Keylist_Delete(OS_Keylist list)
while (list->count) {
(void)Keylist_Data_Delete_By_Index(list, 0);
}
if (list->array)
if (list->array) {
free(list->array);
}
free(list);
}
+139 -17
View File
@@ -48,6 +48,15 @@ COV Subscribe Property
COV Notification
Unconfirmed COV Notification
*/
/**
* Encode APDU for notification.
*
* @param apdu Pointer to the buffer.
* @param data Pointer to the data to encode.
*
* @return bytes encoded or zero on error.
*/
static int notify_encode_apdu(
uint8_t *apdu, unsigned max_apdu_len, BACNET_COV_DATA *data)
{
@@ -122,6 +131,16 @@ static int notify_encode_apdu(
return apdu_len;
}
/**
* Encode APDU for confirmed notification.
*
* @param apdu Pointer to the buffer.
* @param max_apdu_len Buffer size.
* @param invoke_id ID to invoke for notification
* @param data Pointer to the data to encode.
*
* @return bytes encoded or zero on error.
*/
int ccov_notify_encode_apdu(uint8_t *apdu,
unsigned max_apdu_len,
uint8_t invoke_id,
@@ -149,6 +168,15 @@ int ccov_notify_encode_apdu(uint8_t *apdu,
return apdu_len;
}
/**
* Encode APDU for unconfirmed notification.
*
* @param apdu Pointer to the buffer.
* @param max_apdu_len Buffer size.
* @param data Pointer to the data to encode.
*
* @return bytes encoded or zero on error.
*/
int ucov_notify_encode_apdu(
uint8_t *apdu, unsigned max_apdu_len, BACNET_COV_DATA *data)
{
@@ -172,8 +200,16 @@ int ucov_notify_encode_apdu(
return apdu_len;
}
/* decode the service request only */
/* COV and Unconfirmed COV are the same */
/**
* Decode the COV-service request only.
* Note: COV and Unconfirmed COV are the same.
*
* @param apdu Pointer to the buffer.
* @param apdu_len Count of valid bytes in the buffer.
* @param data Pointer to the data to store the decoded values.
*
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
*/
int cov_notify_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_COV_DATA *data)
{
@@ -187,7 +223,7 @@ int cov_notify_decode_service_request(
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
if (apdu_len && data) {
if ((apdu_len > 2) && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
@@ -198,6 +234,9 @@ int cov_notify_decode_service_request(
return BACNET_STATUS_ERROR;
}
/* tag 1 - initiatingDeviceIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -210,6 +249,9 @@ int cov_notify_decode_service_request(
return BACNET_STATUS_ERROR;
}
/* tag 2 - monitoredObjectIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 2)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -220,6 +262,9 @@ int cov_notify_decode_service_request(
return BACNET_STATUS_ERROR;
}
/* tag 3 - timeRemaining */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -242,6 +287,9 @@ int cov_notify_decode_service_request(
}
while (value != NULL) {
/* tag 0 - propertyIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -251,6 +299,9 @@ int cov_notify_decode_service_request(
return BACNET_STATUS_ERROR;
}
/* tag 1 - propertyArrayIndex OPTIONAL */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -260,6 +311,9 @@ int cov_notify_decode_service_request(
value->propertyArrayIndex = BACNET_ARRAY_ALL;
}
/* tag 2: opening context tag - value */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
return BACNET_STATUS_ERROR;
}
@@ -283,6 +337,9 @@ int cov_notify_decode_service_request(
/* a tag number of 2 is not extended so only one octet */
len++;
/* tag 3 - priority OPTIONAL */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -330,6 +387,16 @@ SubscribeCOV-Request ::= SEQUENCE {
}
*/
/**
* Encode the COV-service request.
* Note: COV and Unconfirmed COV are the same.
*
* @param apdu Pointer to the buffer.
* @param invoke_id Invoke ID
* @param data Pointer to the data to store the decoded values.
*
* @return Bytes encoded or zero on error.
*/
int cov_subscribe_encode_apdu(uint8_t *apdu,
unsigned max_apdu_len,
uint8_t invoke_id,
@@ -372,7 +439,15 @@ int cov_subscribe_encode_apdu(uint8_t *apdu,
return apdu_len;
}
/* decode the service request only */
/**
* Decode the subscribe-service request only.
*
* @param apdu Pointer to the buffer.
* @param apdu_len Count of valid bytes in the buffer.
* @param data Pointer to the data to store the decoded values.
*
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
*/
int cov_subscribe_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA *data)
{
@@ -382,7 +457,7 @@ int cov_subscribe_decode_service_request(
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE;
if (apdu_len && data) {
if ((apdu_len > 2) && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
@@ -394,6 +469,9 @@ int cov_subscribe_decode_service_request(
return BACNET_STATUS_REJECT;
}
/* tag 1 - monitoredObjectIdentifier */
if ((unsigned) len >= apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -418,11 +496,15 @@ int cov_subscribe_decode_service_request(
data->cancellationRequest = true;
}
/* tag 3 - lifetime - optional */
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
data->lifetime = unsigned_value;
if ((unsigned) len < apdu_len) {
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
data->lifetime = unsigned_value;
} else {
data->lifetime = 0;
}
} else {
data->lifetime = 0;
}
@@ -453,6 +535,16 @@ BACnetPropertyReference ::= SEQUENCE {
*/
/**
* Encode the properties for subscription into the APDU.
*
* @param apdu Pointer to the buffer.
* @param max_apdu_len Buffer size.
* @param invoke_id Invoke Id.
* @param data Pointer to the data to encode.
*
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
*/
int cov_subscribe_property_encode_apdu(uint8_t *apdu,
unsigned max_apdu_len,
uint8_t invoke_id,
@@ -509,7 +601,15 @@ int cov_subscribe_property_encode_apdu(uint8_t *apdu,
return apdu_len;
}
/* decode the service request only */
/**
* Decode the COV-service property request only.
*
* @param apdu Pointer to the buffer.
* @param apdu_len Count of valid bytes in the buffer.
* @param data Pointer to the data to store the decoded values.
*
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
*/
int cov_subscribe_property_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA *data)
{
@@ -520,7 +620,7 @@ int cov_subscribe_property_decode_service_request(
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE; /* for decoding */
uint32_t property = 0; /* for decoding */
if (apdu_len && data) {
if ((apdu_len > 2) && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
@@ -532,6 +632,9 @@ int cov_subscribe_property_decode_service_request(
return BACNET_STATUS_REJECT;
}
/* tag 1 - monitoredObjectIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -543,6 +646,9 @@ int cov_subscribe_property_decode_service_request(
return BACNET_STATUS_REJECT;
}
/* tag 2 - issueConfirmedNotifications - optional */
if (len >= (int)apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 2)) {
data->cancellationRequest = false;
len += decode_tag_number_and_value(
@@ -554,6 +660,9 @@ int cov_subscribe_property_decode_service_request(
data->cancellationRequest = true;
}
/* tag 3 - lifetime - optional */
if (len >= (int)apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -563,6 +672,9 @@ int cov_subscribe_property_decode_service_request(
data->lifetime = 0;
}
/* tag 4 - monitoredPropertyIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_REJECT;
}
if (!decode_is_opening_tag_number(&apdu[len], 4)) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
@@ -570,6 +682,9 @@ int cov_subscribe_property_decode_service_request(
/* a tag number of 4 is not extended so only one octet */
len++;
/* the propertyIdentifier is tag 0 */
if (len >= (int)apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -581,6 +696,9 @@ int cov_subscribe_property_decode_service_request(
return BACNET_STATUS_REJECT;
}
/* the optional array index is tag 1 */
if (len >= (int)apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
@@ -597,11 +715,15 @@ int cov_subscribe_property_decode_service_request(
/* a tag number of 4 is not extended so only one octet */
len++;
/* tag 5 - covIncrement - optional */
if (decode_is_context_tag(&apdu[len], 5)) {
data->covIncrementPresent = true;
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_real(&apdu[len], &data->covIncrement);
if (len < (int)apdu_len) {
if (decode_is_context_tag(&apdu[len], 5)) {
data->covIncrementPresent = true;
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_real(&apdu[len], &data->covIncrement);
} else {
data->covIncrementPresent = false;
}
} else {
data->covIncrementPresent = false;
}
+30 -9
View File
@@ -288,9 +288,16 @@ bool datetime_is_valid(BACNET_DATE *bdate, BACNET_TIME *btime)
return datetime_date_is_valid(bdate) && datetime_time_is_valid(btime);
}
/* if the date1 is the same as date2, return is 0
if date1 is after date2, returns positive
if date1 is before date2, returns negative */
/**
* If the date1 is the same as date2, return is 0.
* If date1 is after date2, returns positive.
* if date1 is before date2, returns negative.
*
* @param date1 - Pointer to a BACNET_DATE structure
* @param date2 - Pointer to a BACNET_DATE structure
*
* @return -/0/+
*/
int datetime_compare_date(BACNET_DATE *date1, BACNET_DATE *date2)
{
int diff = 0;
@@ -308,9 +315,16 @@ int datetime_compare_date(BACNET_DATE *date1, BACNET_DATE *date2)
return diff;
}
/* if the time1 is the same as time2, return is 0
if time1 is after time2, returns positive
if time1 is before time2, returns negative */
/**
* If the time1 is the same as time2, return is 0.
* If time1 is after time2, returns positive.
* if time1 is before time2, returns negative.
*
* @param time1 - Pointer to a BACNET_TIME structure
* @param time2 - Pointer to a BACNET_TIME structure
*
* @return -/0/+
*/
int datetime_compare_time(BACNET_TIME *time1, BACNET_TIME *time2)
{
int diff = 0;
@@ -331,9 +345,16 @@ int datetime_compare_time(BACNET_TIME *time1, BACNET_TIME *time2)
return diff;
}
/* if the datetime1 is the same as datetime2, return is 0
if datetime1 is before datetime2, returns negative
if datetime1 is after datetime2, returns positive */
/**
* If the datetime1 is the same datetime2, return is 0.
* If datetime1 is after datetime2, returns positive.
* if datetime1 is before datetime2, returns negative.
*
* @param datetime1 - Pointer to a BACNET_DATE_TIME structure
* @param datetime2 - Pointer to a BACNET_DATE_TIME structure
*
* @return -/0/+
*/
int datetime_compare(BACNET_DATE_TIME *datetime1, BACNET_DATE_TIME *datetime2)
{
int diff = 0;
+77 -21
View File
@@ -49,46 +49,73 @@ static BACNET_COMMUNICATION_ENABLE_DISABLE DCC_Enable_Disable =
COMMUNICATION_ENABLE;
/* password is optionally supported */
/**
* Returns, the network communications enable/disable status.
*
* @return BACnet communication status
*/
BACNET_COMMUNICATION_ENABLE_DISABLE dcc_enable_status(void)
{
return DCC_Enable_Disable;
}
/**
* Returns, if network communications is enabled.
*
* @return true, if communication has been enabled.
*/
bool dcc_communication_enabled(void)
{
return (DCC_Enable_Disable == COMMUNICATION_ENABLE);
}
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated.*/
/**
* When network communications are completely disabled,
* only DeviceCommunicationControl and ReinitializeDevice APDUs
* shall be processed and no messages shall be initiated.
*
* @return true, if communication has been disabled, false otherwise.
*/
bool dcc_communication_disabled(void)
{
return (DCC_Enable_Disable == COMMUNICATION_DISABLE);
}
/* When the initiation of communications is disabled,
all APDUs shall be processed and responses returned as
required and no messages shall be initiated with the
exception of I-Am requests, which shall be initiated only in
response to Who-Is messages. In this state, a device that
supports I-Am request initiation shall send one I-Am request
for any Who-Is request that is received if and only if
the Who-Is request does not contain an address range or
the device is included in the address range. */
/**
* When the initiation of communications is disabled,
* all APDUs shall be processed and responses returned as
* required and no messages shall be initiated with the
* exception of I-Am requests, which shall be initiated only in
* response to Who-Is messages. In this state, a device that
* supports I-Am request initiation shall send one I-Am request
* for any Who-Is request that is received if and only if
* the Who-Is request does not contain an address range or
* the device is included in the address range.
*
* @return true, if disabling initiation is set, false otherwise.
*/
bool dcc_communication_initiation_disabled(void)
{
return (DCC_Enable_Disable == COMMUNICATION_DISABLE_INITIATION);
}
/* note: 0 indicates either expired, or infinite duration */
/**
* Returns the time duration in seconds.
* Note: 0 indicates either expired, or infinite duration.
*
* @return time in seconds
*/
uint32_t dcc_duration_seconds(void)
{
return DCC_Time_Duration_Seconds;
}
/* called every second or so. If more than one second,
then seconds should be the number of seconds to tick away */
/**
* Called every second or so. If more than one second,
* then seconds should be the number of seconds to tick away.
*
* @param seconds Time passed in seconds, since last call.
*/
void dcc_timer_seconds(uint32_t seconds)
{
if (DCC_Time_Duration_Seconds) {
@@ -104,6 +131,14 @@ void dcc_timer_seconds(uint32_t seconds)
}
}
/**
* Set DCC status using duration.
*
* @param status Enable/disable communication
* @param minutes Duration in minutes
*
* @return true/false
*/
bool dcc_set_status_duration(
BACNET_COMMUNICATION_ENABLE_DISABLE status, uint16_t minutes)
{
@@ -124,7 +159,17 @@ bool dcc_set_status_duration(
}
#if BACNET_SVC_DCC_A
/* encode service */
/**
* Encode service
*
* @param apdu Pointer to the APDU buffer used for encoding.
* @param invoke_id Invoke-Id
* @param timeDuration Optional time duration in minutes.
* @param enable_disable Enable/disable communication
* @param password Pointer to an optional password.
*
* @return Bytes encoded or zero on an error.
*/
int dcc_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
uint16_t timeDuration, /* 0=optional */
@@ -150,9 +195,10 @@ int dcc_encode_apdu(uint8_t *apdu,
apdu_len += len;
/* optional password */
if (password) {
/* FIXME: must be at least 1 character, limited to 20 characters */
len = encode_context_character_string(&apdu[apdu_len], 2, password);
apdu_len += len;
if ((password->length >= 1) && (password->length <= 20)) {
len = encode_context_character_string(&apdu[apdu_len], 2, password);
apdu_len += len;
}
}
}
@@ -160,7 +206,17 @@ int dcc_encode_apdu(uint8_t *apdu,
}
#endif
/* decode the service request only */
/**
* Decode the service request only
*
* @param apdu Pointer to the received request.
* @param apdu_len_max Valid count of bytes in the buffer.
* @param timeDuration Pointer to the duration given in minutes [optional]
* @param enable_disable Pointer to the variable takingthe communication enable/disable.
* @param password Pointer to the password [optional]
*
* @return Bytes decoded.
*/
int dcc_decode_service_request(uint8_t *apdu,
unsigned apdu_len_max,
uint16_t *timeDuration,
@@ -174,7 +230,7 @@ int dcc_decode_service_request(uint8_t *apdu,
BACNET_UNSIGNED_INTEGER decoded_unsigned = 0;
uint32_t decoded_enum = 0;
if (apdu_len_max) {
if (apdu && apdu_len_max) {
/* Tag 0: timeDuration, in minutes --optional-- */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_len_max - apdu_len, 0, &decoded_unsigned);
+20 -4
View File
@@ -39,7 +39,14 @@
/** @file getevent.c Encode/Decode GetEvent services */
/* encode service */
/** Encode service
*
* @param apdu APDU buffer to encode to.
* @param invoke_id Invoke ID
* @param lastReceivedObjectIdentifier Object identifier
*
* @return Bytes encoded.
*/
int getevent_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
BACNET_OBJECT_ID *lastReceivedObjectIdentifier)
@@ -65,7 +72,14 @@ int getevent_encode_apdu(uint8_t *apdu,
return apdu_len;
}
/* decode the service request only */
/** Decode the service request only
*
* @param apdu APDU buffer to encode to.
* @param apdu_len Valid bytes in the buffer.
* @param lastReceivedObjectIdentifier Object identifier
*
* @return Bytes encoded.
*/
int getevent_decode_service_request(uint8_t *apdu,
unsigned apdu_len,
BACNET_OBJECT_ID *lastReceivedObjectIdentifier)
@@ -78,8 +92,10 @@ int getevent_decode_service_request(uint8_t *apdu,
if (!decode_is_context_tag(&apdu[len++], 0)) {
return -1;
}
len += decode_object_id(&apdu[len], &lastReceivedObjectIdentifier->type,
&lastReceivedObjectIdentifier->instance);
if (len < apdu_len) {
len += decode_object_id(&apdu[len], &lastReceivedObjectIdentifier->type,
&lastReceivedObjectIdentifier->instance);
}
}
return (int)len;
+34 -3
View File
@@ -39,6 +39,14 @@
/** @file ihave.c Encode/Decode I-Have service */
/**
* Encode the I Have request
*
* @param apdu Pointer to the APDU buffer
* @param data Pointer to the I Have data structure.
*
* @return Bytes encoded.
*/
int ihave_encode_apdu(uint8_t *apdu, BACNET_I_HAVE_DATA *data)
{
int len = 0; /* length of each encoding */
@@ -67,7 +75,15 @@ int ihave_encode_apdu(uint8_t *apdu, BACNET_I_HAVE_DATA *data)
#if BACNET_SVC_I_HAVE_A
/* decode the service request only */
/**
* Decode the I Have request only
*
* @param apdu Pointer to the APDU buffer
* @param apdu_len Valid bytes in the buffer
* @param data Pointer to the I Have data structure.
*
* @return Bytes decoded.
*/
int ihave_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_I_HAVE_DATA *data)
{
@@ -76,7 +92,7 @@ int ihave_decode_service_request(
uint32_t len_value = 0;
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE; /* for decoding */
if (apdu_len && data) {
if ((apdu_len >= 2) && data) {
/* deviceIdentifier */
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) {
@@ -87,6 +103,9 @@ int ihave_decode_service_request(
return -1;
}
/* objectIdentifier */
if ((unsigned)len >= apdu_len) {
return -1;
}
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) {
len += decode_object_id(
@@ -96,6 +115,9 @@ int ihave_decode_service_request(
return -1;
}
/* objectName */
if ((unsigned)len >= apdu_len) {
return -1;
}
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
len += decode_character_string(
@@ -110,12 +132,21 @@ int ihave_decode_service_request(
return len;
}
/**
* Decode the I Have
*
* @param apdu Pointer to the APDU buffer
* @param apdu_len Valid bytes in the buffer
* @param data Pointer to the I Have data structure.
*
* @return Bytes decoded.
*/
int ihave_decode_apdu(
uint8_t *apdu, unsigned apdu_len, BACNET_I_HAVE_DATA *data)
{
int len = 0;
if (!apdu) {
if ((!apdu) || (apdu_len < 2)) {
return -1;
}
/* optional checking - most likely was already done prior to this call */
+33 -8
View File
@@ -32,6 +32,7 @@
-------------------------------------------
####COPYRIGHTEND####*/
#include <stdint.h>
#include "bacnet/bacenum.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacdef.h"
@@ -39,7 +40,16 @@
/** @file rd.c Encode/Decode Reinitialize Device APDUs */
#if BACNET_SVC_RD_A
/* encode service */
/** Encode Reinitialize Device service
*
* @param apdu Pointer to the APDU buffer.
* @param invoke_id Invoke-Id
* @param state Reinitialization state
* @param password Pointer to the pass phrase.
*
* @return Bytes encoded.
*/
int rd_encode_apdu(uint8_t *apdu,
uint8_t invoke_id,
BACNET_REINITIALIZED_STATE state,
@@ -58,9 +68,11 @@ int rd_encode_apdu(uint8_t *apdu,
apdu_len += len;
/* optional password */
if (password) {
/* FIXME: must be at least 1 character, limited to 20 characters */
len = encode_context_character_string(&apdu[apdu_len], 1, password);
apdu_len += len;
/* Must be at least 1 character, limited to 20 characters */
if ((password->length >= 1) && (password->length <= 20)) {
len = encode_context_character_string(&apdu[apdu_len], 1, password);
apdu_len += len;
}
}
}
@@ -68,7 +80,15 @@ int rd_encode_apdu(uint8_t *apdu,
}
#endif
/* decode the service request only */
/** Decode Reinitialize Device service
*
* @param apdu Pointer to the APDU buffer.
* @param apdu_len Valid bytes in the buffer
* @param state Pointer to the Reinitialization state
* @param password Pointer to the pass phrase.
*
* @return Bytes encoded.
*/
int rd_decode_service_request(uint8_t *apdu,
unsigned apdu_len,
BACNET_REINITIALIZED_STATE *state,
@@ -80,7 +100,7 @@ int rd_decode_service_request(uint8_t *apdu,
uint32_t value = 0;
/* check for value pointers */
if (apdu_len) {
if ((apdu) && (apdu_len >= 2)) {
/* Tag 0: reinitializedStateOfDevice */
if (!decode_is_context_tag(&apdu[len], 0)) {
return -1;
@@ -98,8 +118,13 @@ int rd_decode_service_request(uint8_t *apdu,
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
len +=
decode_character_string(&apdu[len], len_value_type, password);
if (len < apdu_len) {
if (password) {
len += decode_character_string(&apdu[len],
len_value_type,
password);
}
}
}
}
+204 -100
View File
@@ -61,10 +61,15 @@
* }
*/
/*****************************************************************************
* Build a ReadRange request packet. *
*****************************************************************************/
/**
* Build a ReadRange request packet.
*
* @param apdu Pointer to the APDU buffer.
* @param invoke_id Invoke ID
* @param rrdata Pointer to the data used for encoding.
*
* @return Bytes encoded.
*/
int rr_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_READ_RANGE_DATA *rrdata)
{
@@ -134,10 +139,15 @@ int rr_encode_apdu(
return apdu_len;
}
/*****************************************************************************
* Decode the received ReadRange request *
*****************************************************************************/
/**
* Decode the received ReadRange request
*
* @param apdu Pointer to the APDU buffer.
* @param apdu_len Bytes valid in the APDU buffer.
* @param rrdata Pointer to the data used for encoding.
*
* @return Bytes encoded.
*/
int rr_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_READ_RANGE_DATA *rrdata)
{
@@ -150,7 +160,7 @@ int rr_decode_service_request(
BACNET_UNSIGNED_INTEGER unsigned_value;
/* check for value pointers */
if (apdu_len && rrdata) {
if ((apdu_len >= 5) && apdu && rrdata) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0)) {
return -1;
@@ -158,6 +168,9 @@ int rr_decode_service_request(
len += decode_object_id(&apdu[len], &type, &rrdata->object_instance);
rrdata->object_type = type;
/* Tag 1: Property ID */
if (len >= apdu_len) {
return(-1);
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
@@ -199,30 +212,60 @@ int rr_decode_service_request(
switch (tag_number) {
case 3: /* ReadRange by position */
rrdata->RequestType = RR_BY_POSITION;
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_unsigned(
&apdu[len], len_value_type, &unsigned_value);
rrdata->Range.RefIndex = (uint32_t)unsigned_value;
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_signed(
&apdu[len], len_value_type, &rrdata->Count);
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
break;
case 6: /* ReadRange by sequence number */
rrdata->RequestType = RR_BY_SEQUENCE;
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_unsigned(
&apdu[len], len_value_type, &unsigned_value);
rrdata->Range.RefSeqNum = (uint32_t)unsigned_value;
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_signed(
&apdu[len], len_value_type, &rrdata->Count);
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
/* Allow for this in the response */
@@ -231,17 +274,38 @@ int rr_decode_service_request(
case 7: /* ReadRange by time stamp */
rrdata->RequestType = RR_BY_TIME;
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_date(&apdu[len], &rrdata->Range.RefTime.date);
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_bacnet_time(
&apdu[len], &rrdata->Range.RefTime.time);
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (len >= apdu_len) {
break;
}
len += decode_signed(
&apdu[len], len_value_type, &rrdata->Count);
if (len >= apdu_len) {
break;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
/* Allow for this in the response */
@@ -253,6 +317,8 @@ int rr_decode_service_request(
break;
}
}
} else {
return(-1);
}
return (int)len;
@@ -272,13 +338,19 @@ int rr_decode_service_request(
* }
*/
/*****************************************************************************
* Build a ReadRange response packet *
*****************************************************************************/
/**
* Build a ReadRange response packet
*
* @param apdu Pointer to the buffer.
* @param invoke_id ID invoked.
* @param rrdata Pointer to the read range data structure used for encoding.
*
* @return The count of encoded bytes.
*/
int rr_ack_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_READ_RANGE_DATA *rrdata)
{
int imax = 0;
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
@@ -309,7 +381,11 @@ int rr_ack_encode_apdu(
*/
apdu_len += encode_opening_tag(&apdu[apdu_len], 5);
if (rrdata->ItemCount != 0) {
for (len = 0; len < rrdata->application_data_len; len++) {
imax = rrdata->application_data_len;
if (imax > (MAX_APDU - apdu_len - 2 /*closing*/)) {
imax = (MAX_APDU - apdu_len - 2);
}
for (len = 0; len < imax; len++) {
apdu[apdu_len++] = rrdata->application_data[len];
}
}
@@ -319,18 +395,25 @@ int rr_ack_encode_apdu(
(rrdata->RequestType != RR_BY_POSITION) &&
(rrdata->RequestType != RR_READ_ALL)) {
/* Context 6 Sequence number of first item */
apdu_len += encode_context_unsigned(
&apdu[apdu_len], 6, rrdata->FirstSequence);
if (apdu_len < (MAX_APDU - 4)) {
apdu_len += encode_context_unsigned(
&apdu[apdu_len], 6, rrdata->FirstSequence);
}
}
}
return apdu_len;
}
/*****************************************************************************
* Decode the received ReadRange response *
*****************************************************************************/
/**
* Decode the received ReadRange response
*
* @param apdu Pointer to the APDU buffer.
* @param apdu_len Bytes valid in the APDU buffer.
* @param rrdata Pointer to the data filled while decoding.
*
* @return Bytes decoded.
*/
int rr_ack_decode_service_request(uint8_t *apdu,
int apdu_len, /* total length of the apdu */
BACNET_READ_RANGE_DATA *rrdata)
@@ -344,90 +427,111 @@ int rr_ack_decode_service_request(uint8_t *apdu,
uint32_t property = 0; /* for decoding */
BACNET_UNSIGNED_INTEGER unsigned_value;
/* FIXME: check apdu_len against the len during decode */
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0)) {
return -1;
}
len = 1;
len += decode_object_id(&apdu[len], &object_type, &rrdata->object_instance);
rrdata->object_type = object_type;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
return -1;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rrdata->object_property = (BACNET_PROPERTY_ID)property;
/* Tag 2: Optional Array Index */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rrdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
} else {
rrdata->array_index = BACNET_ARRAY_ALL;
}
/* Tag 3: Result Flags */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 3) {
return -1;
}
len += decode_bitstring(&apdu[len], len_value_type, &rrdata->ResultFlags);
/* Tag 4: Item count */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 4) {
return -1;
}
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rrdata->ItemCount = (uint32_t)unsigned_value;
if (decode_is_opening_tag_number(&apdu[len], 5)) {
len++; /* a tag number of 5 is not extended so only one octet */
/* Setup the start position and length of the data returned from the
* request don't decode the application tag number or its data here */
rrdata->application_data = &apdu[len];
start_len = len;
while (len < apdu_len) {
if (IS_CONTEXT_SPECIFIC(apdu[len]) &&
(decode_is_closing_tag_number(&apdu[len], 5))) {
rrdata->application_data_len = len - start_len;
len++; /* Step over single byte closing tag */
break;
} else {
/* Don't care about tag number, just skipping over anyway */
len += decode_tag_number_and_value(
&apdu[len], NULL, &len_value_type);
len += len_value_type; /* Skip over data value as well */
if (len >= apdu_len) { /* APDU is exhausted so we have failed to
find closing tag */
return (-1);
}
}
}
} else {
return -1;
}
if (len < apdu_len) { /* Still something left to look at? */
/* Tag 6: Item count */
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (tag_number != 6) {
/* Check apdu_len against the len during decode. */
if (apdu && (apdu_len >= 5 /* minimum */)) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0)) {
return -1;
}
len = 1;
len += decode_object_id(&apdu[len], &object_type, &rrdata->object_instance);
rrdata->object_type = object_type;
/* Tag 1: Property ID */
if (len >= apdu_len) {
return -1;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
return -1;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rrdata->object_property = (BACNET_PROPERTY_ID)property;
/* Tag 2: Optional Array Index */
if (len >= apdu_len) {
return -1;
}
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rrdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
} else {
rrdata->array_index = BACNET_ARRAY_ALL;
}
/* Tag 3: Result Flags */
if (len >= apdu_len) {
return -1;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 3) {
return -1;
}
if (len >= apdu_len) {
return -1;
}
len += decode_bitstring(&apdu[len], len_value_type, &rrdata->ResultFlags);
/* Tag 4: Item count */
if (len >= apdu_len) {
return -1;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 4) {
return -1;
}
if (len >= apdu_len) {
return -1;
}
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rrdata->FirstSequence = (uint32_t)unsigned_value;
rrdata->ItemCount = (uint32_t)unsigned_value;
if (len >= apdu_len) {
return -1;
}
if (decode_is_opening_tag_number(&apdu[len], 5)) {
len++; /* A tag number of 5 is not extended so only one octet
* Setup the start position and length of the data returned from the
* request don't decode the application tag number or its data here. */
rrdata->application_data = &apdu[len];
start_len = len;
while (len < apdu_len) {
if (IS_CONTEXT_SPECIFIC(apdu[len]) &&
(decode_is_closing_tag_number(&apdu[len], 5))) {
rrdata->application_data_len = len - start_len;
len++; /* Step over single byte closing tag */
break;
} else {
/* Don't care about tag number, just skipping over anyway */
len += decode_tag_number_and_value(
&apdu[len], NULL, &len_value_type);
len += len_value_type; /* Skip over data value as well */
if (len >= apdu_len) { /* APDU is exhausted so we have failed to
* find closing tag */
return (-1);
}
}
}
} else {
return -1;
}
if (len < apdu_len) { /* Still something left to look at? */
/* Tag 6: Item count */
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (tag_number != 6) {
return -1;
}
if (len < apdu_len) {
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rrdata->FirstSequence = (uint32_t)unsigned_value;
}
}
}
return len;
+24 -3
View File
@@ -141,7 +141,14 @@ BACNET_ERROR_CODE reject_convert_to_error_code(BACNET_REJECT_REASON reject_code)
return (error_code);
}
/* encode service */
/** Encode service
*
* @param apdu Pointer to the APDU buffer.
* @param invoke_id Invoke-Id.
* @param reject_reason Reason for having rejected.
*
* @return Bytes encoded, typically 3.
*/
int reject_encode_apdu(uint8_t *apdu, uint8_t invoke_id, uint8_t reject_reason)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -157,7 +164,17 @@ int reject_encode_apdu(uint8_t *apdu, uint8_t invoke_id, uint8_t reject_reason)
}
#if !BACNET_SVC_SERVER
/* decode the service request only */
/** Decode the service request only.
*
* @param apdu Pointer to the APDU buffer.
* @param apdu_len Bytes valid in the buffer
* @param invoke_id Invoke-Id.
* @param reject_reason Pointer to the variable taking
* the reason for having rejected.
*
* @return Bytes encoded, typically 3.
*/
int reject_decode_service_request(uint8_t *apdu,
unsigned apdu_len,
uint8_t *invoke_id,
@@ -170,7 +187,11 @@ int reject_decode_service_request(uint8_t *apdu,
*invoke_id = apdu[0];
}
if (reject_reason) {
*reject_reason = apdu[1];
if (apdu_len > 1) {
*reject_reason = apdu[1];
} else {
*reject_reason = 0;
}
}
}
+107 -41
View File
@@ -40,7 +40,15 @@
/** @file rp.c Encode/Decode Read Property and RP ACKs */
#if BACNET_SVC_RP_A
/* encode service */
/** Encode the service
*
* @param apdu Pointer to the buffer for encoding.
* @param invoke_id Invoke ID
* @param rpdata Pointer to the property data to be encoded.
*
* @return Bytes encoded or zero on error.
*/
int rp_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_READ_PROPERTY_DATA *rpdata)
{
@@ -60,6 +68,7 @@ int rp_encode_apdu(
rpdata->object_type, rpdata->object_instance);
apdu_len += len;
}
/* The value should be in the range of 0 to 4194303. */
if (rpdata->object_property <= MAX_BACNET_PROPERTY_ID) {
/* check bounds so that we could create malformed
messages for testing */
@@ -79,7 +88,14 @@ int rp_encode_apdu(
}
#endif
/* decode the service request only */
/** Decode the service request only
*
* @param apdu Pointer to the buffer for encoding.
* @param apdu_len Count of valid bytes inthe buffer.
* @param rpdata Pointer to the property data to be encoded.
*
* @return Bytes decoded or zero on error.
*/
int rp_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_READ_PROPERTY_DATA *rpdata)
{
@@ -143,7 +159,14 @@ int rp_decode_service_request(
return (int)len;
}
/* alternate method to encode the ack without extra buffer */
/** Alternate method to encode the ack without extra buffer.
*
* @param apdu Pointer to the buffer for encoding.
* @param invoke_id Invoke Id
* @param rpdata Pointer to the property data to be encoded.
*
* @return Bytes decoded or zero on error.
*/
int rp_ack_encode_apdu_init(
uint8_t *apdu, uint8_t invoke_id, BACNET_READ_PROPERTY_DATA *rpdata)
{
@@ -176,7 +199,15 @@ int rp_ack_encode_apdu_init(
return apdu_len;
}
/* note: encode the application tagged data yourself */
/** Encode the closing tag for the object property.
* Note: Encode the application tagged data yourself.
*
* @param apdu Pointer to the buffer for encoding.
* @param invoke_id Invoke Id
* @param rpdata Pointer to the property data to be encoded.
*
* @return Bytes encoded or zero on error.
*/
int rp_ack_encode_apdu_object_property_end(uint8_t *apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -188,17 +219,31 @@ int rp_ack_encode_apdu_object_property_end(uint8_t *apdu)
return apdu_len;
}
/** Encode the acknowledge.
*
* @param apdu Pointer to the buffer for encoding.
* @param invoke_id Invoke Id
* @param rpdata Pointer to the property data to be encoded.
*
* @return Bytes encoded or zero on error.
*/
int rp_ack_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_READ_PROPERTY_DATA *rpdata)
{
int imax = 0;
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu) {
/* Do the initial encoding */
apdu_len = rp_ack_encode_apdu_init(apdu, invoke_id, rpdata);
/* propertyValue */
for (len = 0; len < rpdata->application_data_len; len++) {
/* propertyValue
* double check maximum possible */
imax = rpdata->application_data_len;
if (imax > (MAX_APDU - apdu_len - 2)) {
imax = (MAX_APDU - apdu_len - 2);
}
for (len = 0; len < imax; len++) {
apdu[apdu_len++] = rpdata->application_data[len];
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 3);
@@ -231,41 +276,62 @@ int rp_ack_decode_service_request(uint8_t *apdu,
uint32_t property = 0; /* for decoding */
BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding */
/* FIXME: check apdu_len against the len during decode */
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0)) {
return -1;
}
len = 1;
len += decode_object_id(&apdu[len], &object_type, &rpdata->object_instance);
rpdata->object_type = object_type;
/* Tag 1: Property ID */
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
return -1;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpdata->object_property = (BACNET_PROPERTY_ID)property;
/* Tag 2: Optional Array Index */
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rpdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
} else {
rpdata->array_index = BACNET_ARRAY_ALL;
}
/* Tag 3: opening context tag */
if (decode_is_opening_tag_number(&apdu[len], 3)) {
/* a tag number of 3 is not extended so only one octet */
len++;
/* don't decode the application tag number or its data here */
rpdata->application_data = &apdu[len];
rpdata->application_data_len = apdu_len - len - 1 /*closing tag */;
/* len includes the data and the closing tag */
len = apdu_len;
/* Check basics. */
if (apdu && (apdu_len >= 8 /*minimum*/)) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0)) {
return -1;
}
len = 1;
len += decode_object_id(&apdu[len], &object_type, &rpdata->object_instance);
rpdata->object_type = object_type;
/* Tag 1: Property ID */
if (len >= apdu_len) {
return -1;
}
len +=
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
return -1;
}
if (len >= apdu_len) {
return -1;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpdata->object_property = (BACNET_PROPERTY_ID)property;
/* Tag 2: Optional Array Index */
if (len >= apdu_len) {
return -1;
}
tag_len =
decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rpdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
} else {
rpdata->array_index = BACNET_ARRAY_ALL;
}
/* Tag 3: opening context tag */
if (len >= apdu_len) {
return -1;
}
if (decode_is_opening_tag_number(&apdu[len], 3)) {
/* a tag number of 3 is not extended so only one octet */
len++;
/* don't decode the application tag number or its data here */
rpdata->application_data = &apdu[len];
/* Just to ensure we do not create a wrapped over value here. */
if (len < apdu_len) {
rpdata->application_data_len = apdu_len - len - 1 /*closing tag */;
} else {
rpdata->application_data_len = 0;
}
/* len includes the data and the closing tag */
len = apdu_len;
} else {
return -1;
}
} else {
return -1;
}
+114 -14
View File
@@ -43,7 +43,14 @@
/** @file rpm.c Encode/Decode Read Property Multiple and RPM ACKs */
#if BACNET_SVC_RPM_A
/* encode the initial portion of the service */
/** Encode the initial portion of the service
*
* @param apdu Pointer to the buffer for encoding.
* @param invoke_id Invoke ID
*
* @return Bytes encoded (usually 4) or zero on error.
*/
int rpm_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -59,6 +66,15 @@ int rpm_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
return apdu_len;
}
/** Encode the beginning, including
* Object-id and Read-Access of the service.
*
* @param apdu Pointer to the buffer for encoding.
* @param object_type Object type to encode
* @param object_instance Object instance to encode
*
* @return Bytes encoded or zero on error.
*/
int rpm_encode_apdu_object_begin(
uint8_t *apdu, BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
{
@@ -74,6 +90,14 @@ int rpm_encode_apdu_object_begin(
return apdu_len;
}
/** Encode the object properties of the service.
*
* @param apdu Pointer to the buffer for encoding.
* @param object_property Object property.
* @param array_index Optional array index.
*
* @return Bytes encoded or zero on error.
*/
int rpm_encode_apdu_object_property(
uint8_t *apdu, BACNET_PROPERTY_ID object_property, BACNET_ARRAY_INDEX array_index)
{
@@ -91,6 +115,12 @@ int rpm_encode_apdu_object_property(
return apdu_len;
}
/** Encode the end (closing tag) of the service
*
* @param apdu Pointer to the buffer for encoding.
*
* @return Bytes encoded or zero on error.
*/
int rpm_encode_apdu_object_end(uint8_t *apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -130,6 +160,8 @@ int rpm_encode_apdu(uint8_t *apdu,
apdu_len += len;
rpm_object = read_access_data;
while (rpm_object) {
/* The encode function will return a length not more than 12. So the temp buffer
* being 16 bytes is fine enought. */
len = encode_context_object_id(&apdu_temp[0], 0,
rpm_object->object_type, rpm_object->object_instance);
len = (int)memcopy(&apdu[0], &apdu_temp[0], (size_t)apdu_len,
@@ -148,7 +180,9 @@ int rpm_encode_apdu(uint8_t *apdu,
apdu_len += len;
rpm_property = rpm_object->listOfProperties;
while (rpm_property) {
/* stuff as many properties into it as APDU length will allow */
/* The encode function will return a length not more than 12. So the temp buffer
* being 16 bytes is fine enought.
* Stuff as many properties into it as APDU length will allow. */
len = encode_context_enumerated(
&apdu_temp[0], 0, rpm_property->propertyIdentifier);
len = (int)memcopy(&apdu[0], &apdu_temp[0], (size_t)apdu_len,
@@ -169,6 +203,10 @@ int rpm_encode_apdu(uint8_t *apdu,
apdu_len += len;
}
rpm_property = rpm_property->next;
/* Full? */
if ((unsigned)apdu_len >= max_apdu) {
return 0;
}
}
len = encode_closing_tag(&apdu_temp[0], 1);
len = (int)memcopy(&apdu[0], &apdu_temp[0], (size_t)apdu_len,
@@ -185,8 +223,14 @@ int rpm_encode_apdu(uint8_t *apdu,
#endif
/* decode the object portion of the service request only. Bails out if
* tags are wrong or missing/incomplete
/** Decode the object portion of the service request only. Bails out if
* tags are wrong or missing/incomplete.
*
* @param apdu [in] Buffer of bytes received.
* @param apdu_len [in] Count of valid bytes in the buffer.
* @param rpmdata [in] The data structure to be filled.
*
* @return Length of decoded bytes, or 0 on failure.
*/
int rpm_decode_object_id(
uint8_t *apdu, unsigned apdu_len, BACNET_RPM_DATA *rpmdata)
@@ -218,6 +262,13 @@ int rpm_decode_object_id(
return len;
}
/** Decode the end portion of the service request only.
*
* @param apdu [in] Buffer of bytes received.
* @param apdu_len [in] Count of valid bytes in the buffer.
*
* @return Length of decoded bytes (usually 1), or 0 on failure.
*/
int rpm_decode_object_end(uint8_t *apdu, unsigned apdu_len)
{
int len = 0; /* total length of the apdu, return value */
@@ -231,14 +282,20 @@ int rpm_decode_object_end(uint8_t *apdu, unsigned apdu_len)
return len;
}
/* decode the object property portion of the service request only */
/* BACnetPropertyReference ::= SEQUENCE {
propertyIdentifier [0] BACnetPropertyIdentifier,
propertyArrayIndex [1] Unsigned OPTIONAL
--used only with array datatype
-- if omitted with an array the entire array is referenced
}
*/
/** Decode the object property portion of the service request only
* BACnetPropertyReference ::= SEQUENCE {
* propertyIdentifier [0] BACnetPropertyIdentifier,
* propertyArrayIndex [1] Unsigned OPTIONAL
* --used only with array datatype
* -- if omitted with an array the entire array is referenced
* }
*
* @param apdu Pointer to received bytes.
* @param apdu_len Count of received bytes.
* @param rpmdata Pointer to the data structure to be filled.
*
* @return Bytes decoded or zero on failure.
*/
int rpm_decode_object_property(
uint8_t *apdu, unsigned apdu_len, BACNET_RPM_DATA *rpmdata)
{
@@ -294,6 +351,13 @@ int rpm_decode_object_property(
return len;
}
/** Encode the acknowledge for a RPM.
*
* @param apdu [in] Buffer of bytes to transmit.
* @param invoke_id [in] Invoke Id.
*
* @return Length of encoded bytes (usually 3) or 0 on failure.
*/
int rpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -308,6 +372,13 @@ int rpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
return apdu_len;
}
/** Encode the object type for an acknowledge of a RPM.
*
* @param apdu [in] Buffer of bytes to transmit.
* @param rpmdata [in] Pointer to the data used to fill in the APDU.
*
* @return Length of encoded bytes or 0 on failure.
*/
int rpm_ack_encode_apdu_object_begin(uint8_t *apdu, BACNET_RPM_DATA *rpmdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -323,6 +394,14 @@ int rpm_ack_encode_apdu_object_begin(uint8_t *apdu, BACNET_RPM_DATA *rpmdata)
return apdu_len;
}
/** Encode the object property for an acknowledge of a RPM.
*
* @param apdu [in] Buffer of bytes to transmit.
* @param object_property [in] Object property ID.
* @param array_index Optional array index
*
* @return Length of encoded bytes or 0 on failure.
*/
int rpm_ack_encode_apdu_object_property(
uint8_t *apdu, BACNET_PROPERTY_ID object_property, BACNET_ARRAY_INDEX array_index)
{
@@ -333,14 +412,21 @@ int rpm_ack_encode_apdu_object_property(
apdu_len = encode_context_enumerated(&apdu[0], 2, object_property);
/* Tag 3: optional propertyArrayIndex */
if (array_index != BACNET_ARRAY_ALL) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 3, array_index);
apdu_len += encode_context_unsigned(&apdu[apdu_len], 3, array_index);
}
}
return apdu_len;
}
/** Encode the object property value for an acknowledge of a RPM.
*
* @param apdu [in] Buffer of bytes to transmit.
* @param application_data [in] Pointer to the application data used to fill in the APDU.
* @param application_data_len [in] Length of the application data.
*
* @return Length of encoded bytes or 0 on failure.
*/
int rpm_ack_encode_apdu_object_property_value(
uint8_t *apdu, uint8_t *application_data, unsigned application_data_len)
{
@@ -364,6 +450,14 @@ int rpm_ack_encode_apdu_object_property_value(
return apdu_len;
}
/** Encode the object property error for an acknowledge of a RPM.
*
* @param apdu [in] Buffer of bytes to transmit.
* @param error_class [in] Error Class
* @param error_code [in] Error Code
*
* @return Length of encoded bytes or 0 on failure.
*/
int rpm_ack_encode_apdu_object_property_error(
uint8_t *apdu, BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code)
{
@@ -380,6 +474,12 @@ int rpm_ack_encode_apdu_object_property_error(
return apdu_len;
}
/** Encode the end tag for an acknowledge of a RPM.
*
* @param apdu [in] Buffer of bytes to transmit.
*
* @return Length of encoded bytes or 0 on failure.
*/
int rpm_ack_encode_apdu_object_end(uint8_t *apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
+39 -10
View File
@@ -38,13 +38,22 @@
#include "bacnet/wp.h"
/** @file wp.c Encode/Decode BACnet Write Property APDUs */
#if BACNET_SVC_WP_A
/* encode service */
/** Initialize the APDU for encode service.
*
* @param apdu Pointer to the buffer.
* @param invoke_id ID of service invoked.
* @param wpdata Pointer to the write property data.
*
* @return Bytes encoded
*/
int wp_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_WRITE_PROPERTY_DATA *wpdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0; /* total length of the apdu, return value */
int imax = 0; /* maximum application data length */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
@@ -67,10 +76,14 @@ int wp_encode_apdu(
/* propertyValue */
len = encode_opening_tag(&apdu[apdu_len], 3);
apdu_len += len;
for (len = 0; len < wpdata->application_data_len; len++) {
imax = wpdata->application_data_len;
if (imax > (MAX_APDU - 2 /*closing*/ - apdu_len)) {
imax = MAX_APDU - 2 - apdu_len;
}
for (len = 0; len < imax; len++) {
apdu[apdu_len + len] = wpdata->application_data[len];
}
apdu_len += wpdata->application_data_len;
apdu_len += imax;
len = encode_closing_tag(&apdu[apdu_len], 3);
apdu_len += len;
/* optional priority - 0 if not set, 1..16 if set */
@@ -84,9 +97,17 @@ int wp_encode_apdu(
}
#endif
/* decode the service request only */
/* FIXME: there could be various error messages returned
using unique values less than zero */
/** Decode the service request only
*
* FIXME: there could be various error messages returned
* using unique values less than zero.
*
* @param apdu Pointer to the buffer.
* @param apdu_len Valid bytes in the buffer
* @param wpdata Pointer to the write property data.
*
* @return Bytes encoded or a negative value as error.
*/
int wp_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_WRITE_PROPERTY_DATA *wpdata)
{
@@ -98,6 +119,7 @@ int wp_decode_service_request(
uint32_t property = 0; /* for decoding */
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
int i = 0; /* loop counter */
int imax = 0; /* max application data length */
/* check for value pointers */
if (apdu_len && wpdata) {
@@ -132,16 +154,23 @@ int wp_decode_service_request(
return -1;
}
/* determine the length of the data blob */
wpdata->application_data_len = bacapp_data_len(
&apdu[len], apdu_len - len, (BACNET_PROPERTY_ID)property);
imax = bacapp_data_len( \
&apdu[len], apdu_len - len, (BACNET_PROPERTY_ID)property);
if (imax == BACNET_STATUS_ERROR) {
return -2;
}
/* a tag number of 3 is not extended so only one octet */
len++;
/* copy the data from the APDU */
for (i = 0; i < wpdata->application_data_len; i++) {
if (imax > (MAX_APDU - len - 1 /*closing*/)) {
imax = (MAX_APDU - len - 1);
}
for (i = 0; i < imax; i++) {
wpdata->application_data[i] = apdu[len + i];
}
wpdata->application_data_len = imax;
/* add on the data length */
len += wpdata->application_data_len;
len += imax;
if (!decode_is_closing_tag_number(&apdu[len], 3)) {
return -2;
}
+113 -24
View File
@@ -46,8 +46,10 @@
*
* @param apdu [in] The contents of the APDU buffer.
* @param apdu_len [in] The length of the APDU buffer.
* @param data [out] The BACNET_WRITE_PROPERTY_DATA structure
* @param wp_data [out] The BACNET_WRITE_PROPERTY_DATA structure
* which will contain the reponse values or error.
*
* @return Count of decoded bytes.
*/
int wpm_decode_object_id(
uint8_t *apdu, uint16_t apdu_len, BACNET_WRITE_PROPERTY_DATA *wp_data)
@@ -102,6 +104,14 @@ int wpm_decode_object_id(
return (int)len;
}
/** Decoding for an object property.
*
* @param apdu [in] The contents of the APDU buffer.
* @param apdu_len [in] The length of the APDU buffer.
* @param wp_data [out] The BACNET_WRITE_PROPERTY_DATA structure.
*
* @return Bytes decoded
*/
int wpm_decode_object_property(
uint8_t *apdu, uint16_t apdu_len, BACNET_WRITE_PROPERTY_DATA *wp_data)
{
@@ -109,7 +119,7 @@ int wpm_decode_object_property(
uint32_t len_value = 0;
uint32_t enum_value = 0;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
int len = 0, i = 0;
int len = 0, i = 0, imax = 0;
if ((apdu) && (apdu_len) && (wp_data)) {
wp_data->array_index = BACNET_ARRAY_ALL;
@@ -137,19 +147,28 @@ int wpm_decode_object_property(
/* tag 2 - Property Value */
if ((tag_number == 2) && (decode_is_opening_tag(&apdu[len - 1]))) {
len--;
wp_data->application_data_len = bacapp_data_len(&apdu[len],
imax = bacapp_data_len(&apdu[len],
(unsigned)(apdu_len - len), wp_data->object_property);
len++;
/* copy application data */
for (i = 0; i < wp_data->application_data_len; i++) {
/* copy application data, check max lengh */
if (imax > (apdu_len - len)) {
imax = (apdu_len - len);
}
for (i = 0; i < imax; i++) {
wp_data->application_data[i] = apdu[len + i];
}
len += wp_data->application_data_len;
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
/* closing tag 2 */
if ((tag_number != 2) && (decode_is_closing_tag(&apdu[len - 1]))) {
wp_data->application_data_len = imax;
len += imax;
if (len < apdu_len) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
/* closing tag 2 */
if ((tag_number != 2) && (decode_is_closing_tag(&apdu[len - 1]))) {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
} else {
wp_data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
@@ -159,12 +178,14 @@ int wpm_decode_object_property(
}
/* tag 3 - Priority - optional */
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == 3) {
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
wp_data->priority = (uint8_t)unsigned_value;
} else {
len--;
if (len < apdu_len) {
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
if (tag_number == 3) {
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
wp_data->priority = (uint8_t)unsigned_value;
} else {
len--;
}
}
} else {
if (wp_data) {
@@ -176,7 +197,13 @@ int wpm_decode_object_property(
return len;
}
/* encode functions */
/** Init the APDU for encoding.
*
* @param apdu [in] The APDU buffer.
* @param invoke_id [in] The ID of the saervice invoked.
*
* @return Bytes encoded, usually 4 on success.
*/
int wpm_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -192,6 +219,14 @@ int wpm_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
return apdu_len;
}
/** Decode the very begin of an object in the APDU.
*
* @param apdu [in] The APDU buffer.
* @param object_type [in] The object type to decode.
* @param object_instance [in] The object instance.
*
* @return Bytes encoded.
*/
int wpm_encode_apdu_object_begin(
uint8_t *apdu, BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
{
@@ -207,6 +242,12 @@ int wpm_encode_apdu_object_begin(
return apdu_len;
}
/** Decode the very end of an object in the APDU.
*
* @param apdu [in] The APDU buffer.
*
* @return Bytes encoded.
*/
int wpm_encode_apdu_object_end(uint8_t *apdu)
{
int apdu_len = 0; /* total length of the apdu, return value */
@@ -218,11 +259,19 @@ int wpm_encode_apdu_object_end(uint8_t *apdu)
return apdu_len;
}
/** Encode the object property into the APDU.
*
* @param apdu [in] The APDU buffer.
* @param wpdata [in] Pointer to the property data.
*
* @return Bytes encoded.
*/
int wpm_encode_apdu_object_property(
uint8_t *apdu, BACNET_WRITE_PROPERTY_DATA *wpdata)
{
int apdu_len = 0; /* total length of the apdu, return value */
int len = 0;
int imax;
if (apdu) {
apdu_len =
@@ -233,20 +282,35 @@ int wpm_encode_apdu_object_property(
&apdu[apdu_len], 1, wpdata->array_index);
}
apdu_len += encode_opening_tag(&apdu[apdu_len], 2);
for (len = 0; len < wpdata->application_data_len; len++) {
imax = wpdata->application_data_len;
if (imax > (MAX_APDU - apdu_len - 2)) {
imax = (MAX_APDU - apdu_len - 2);
}
for (len = 0; len < imax; len++) {
apdu[apdu_len] = wpdata->application_data[len];
apdu_len++;
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 2);
if (wpdata->priority != BACNET_NO_PRIORITY) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 3, wpdata->priority);
if (apdu_len < MAX_APDU) {
apdu_len +=
encode_context_unsigned(&apdu[apdu_len], 3, wpdata->priority);
}
}
}
return apdu_len;
}
/** Encode the request into the APDU.
*
* @param apdu [in] The APDU buffer.
* @param max_apdu [in] Maximum space in the buffer.
* @param invoke_id [in] Invoked service ID.
* @param write_access_data [in] Access data.
*
* @return Bytes encoded.
*/
int wpm_encode_apdu(uint8_t *apdu,
size_t max_apdu,
uint8_t invoke_id,
@@ -254,6 +318,7 @@ int wpm_encode_apdu(uint8_t *apdu,
{
int apdu_len = 0;
int len = 0;
size_t usize;
BACNET_WRITE_ACCESS_DATA *wpm_object; /* current object */
uint8_t apdu_temp[MAX_APDU]; /* temp for data before copy */
BACNET_PROPERTY_VALUE *wpm_property; /* current property */
@@ -271,6 +336,9 @@ int wpm_encode_apdu(uint8_t *apdu,
len = wpm_encode_apdu_object_begin(&apdu[apdu_len],
wpm_object->object_type, wpm_object->object_instance);
apdu_len += len;
if (apdu_len >= (int)max_apdu) {
break;
}
wpm_property = wpm_object->listOfProperties;
@@ -278,12 +346,18 @@ int wpm_encode_apdu(uint8_t *apdu,
wpdata.object_property = wpm_property->propertyIdentifier;
wpdata.array_index = wpm_property->propertyArrayIndex;
wpdata.priority = wpm_property->priority;
wpdata.application_data_len =
bacapp_encode_data(&apdu_temp[0], &wpm_property->value);
memcpy(&wpdata.application_data[0], &apdu_temp[0],
(size_t)wpdata.application_data_len);
usize = (size_t)bacapp_encode_data(&apdu_temp[0], &wpm_property->value);
if (usize > sizeof(wpdata.application_data))
{
usize = sizeof(wpdata.application_data);
}
wpdata.application_data_len = (int)usize;
memcpy(&wpdata.application_data[0], &apdu_temp[0], usize);
len = wpm_encode_apdu_object_property(&apdu[apdu_len], &wpdata);
apdu_len += len;
if (apdu_len >= (int)max_apdu) {
break;
}
wpm_property = wpm_property->next;
}
@@ -298,6 +372,13 @@ int wpm_encode_apdu(uint8_t *apdu,
return apdu_len;
}
/** Init the APDU for encoding the confiremd write property multiple service.
*
* @param apdu [in] The APDU buffer.
* @param invoke_id [in] Invoked service ID.
*
* @return Bytes encoded, usually 3.
*/
int wpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
{
int len = 0;
@@ -311,6 +392,14 @@ int wpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id)
return len;
}
/** Encode an Error acknowledge in the APDU.
*
* @param apdu [in] The APDU buffer.
* @param invoke_id [in] Invoked service ID.
* @param wp_data [in] Data of the invoked property.
*
* @return Bytes encoded.
*/
int wpm_error_ack_encode_apdu(
uint8_t *apdu, uint8_t invoke_id, BACNET_WRITE_PROPERTY_DATA *wp_data)
{