Secured I-Am request encoding and decoding, and updated the example apps and handlers to use secure version of I-Am decoder. (#1080)
This commit is contained in:
+6
-4
@@ -136,7 +136,7 @@ static void mstp_monitor_i_am(uint8_t mac, const uint8_t *pdu, uint16_t pdu_len)
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
int apdu_offset = 0;
|
||||
uint16_t apdu_len = 0;
|
||||
uint16_t apdu_len = 0, service_len = 0;
|
||||
const uint8_t *apdu = NULL;
|
||||
uint8_t pdu_type = 0;
|
||||
uint8_t service_choice = 0;
|
||||
@@ -156,10 +156,12 @@ static void mstp_monitor_i_am(uint8_t mac, const uint8_t *pdu, uint16_t pdu_len)
|
||||
(apdu_len >= 2)) {
|
||||
service_choice = apdu[1];
|
||||
service_request = &apdu[2];
|
||||
service_len = apdu_len - 2;
|
||||
if (service_choice == SERVICE_UNCONFIRMED_I_AM) {
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, NULL, NULL, NULL);
|
||||
if (len != -1) {
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, NULL, NULL,
|
||||
NULL);
|
||||
if (len > 0) {
|
||||
MSTP_Statistics[mac].device_id = device_id;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -109,9 +109,9 @@ static void my_i_am_handler(
|
||||
uint16_t vendor_id = 0;
|
||||
unsigned i = 0;
|
||||
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, &max_apdu, &segmentation,
|
||||
&vendor_id);
|
||||
if (BACnet_Debug_Enabled) {
|
||||
fprintf(stderr, "Received I-Am Request");
|
||||
}
|
||||
|
||||
@@ -95,9 +95,9 @@ static void LocalIAmHandler(
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void)src;
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, &max_apdu, &segmentation,
|
||||
&vendor_id);
|
||||
if (len != -1) {
|
||||
address_add(device_id, max_apdu, src);
|
||||
} else {
|
||||
|
||||
+3
-3
@@ -131,9 +131,9 @@ static void LocalIAmHandler(
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void)src;
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, &max_apdu, &segmentation,
|
||||
&vendor_id);
|
||||
fprintf(stderr, "Received I-Am Request");
|
||||
if (len != -1) {
|
||||
fprintf(stderr, " from %u!\n", device_id);
|
||||
|
||||
@@ -156,9 +156,9 @@ static void My_I_Am_Bind(
|
||||
bool found = false;
|
||||
bool bind = false;
|
||||
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, &max_apdu, &segmentation,
|
||||
&vendor_id);
|
||||
if (len > 0) {
|
||||
found = address_bind_request(device_id, NULL, NULL);
|
||||
if (!found) {
|
||||
|
||||
@@ -33,9 +33,9 @@ void handler_i_am_add(
|
||||
int segmentation = 0;
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, &max_apdu, &segmentation,
|
||||
&vendor_id);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received I-Am Request");
|
||||
#endif
|
||||
@@ -73,9 +73,9 @@ void handler_i_am_bind(
|
||||
int segmentation = 0;
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
len = bacnet_iam_request_decode(
|
||||
service_request, service_len, &device_id, &max_apdu, &segmentation,
|
||||
&vendor_id);
|
||||
if (len > 0) {
|
||||
/* only add address if requested to bind */
|
||||
address_add_binding(device_id, max_apdu, src);
|
||||
|
||||
+159
-76
@@ -14,6 +14,75 @@
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/iam.h"
|
||||
|
||||
/**
|
||||
* @brief Encode the I-Am Request.
|
||||
* @param apdu Transmit buffer
|
||||
* @param device_id Device Id
|
||||
* @param max_apdu Transmit buffer size.
|
||||
* @param segmentation True, if segmentation shall be featured.
|
||||
* @param vendor_id Vendor Id
|
||||
* @return Total length of the apdu, zero otherwise.
|
||||
*/
|
||||
int bacnet_iam_request_encode(
|
||||
uint8_t *apdu,
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
int segmentation,
|
||||
uint16_t vendor_id)
|
||||
{
|
||||
int len; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
len = encode_application_object_id(apdu, OBJECT_DEVICE, device_id);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_application_unsigned(apdu, max_apdu);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_application_enumerated(apdu, (uint32_t)segmentation);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_application_unsigned(apdu, vendor_id);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode the WriteGroup service request
|
||||
* @param apdu Pointer to the buffer for encoding into
|
||||
* @param apdu_size number of bytes available in the buffer
|
||||
* @param data Pointer to the service data used for encoding values
|
||||
* @return number of bytes encoded, or zero if unable to encode or too large
|
||||
*/
|
||||
size_t bacnet_iam_service_request_encode(
|
||||
uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
int segmentation,
|
||||
uint16_t vendor_id)
|
||||
{
|
||||
size_t apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
apdu_len = bacnet_iam_request_encode(
|
||||
NULL, device_id, max_apdu, segmentation, vendor_id);
|
||||
if (apdu_len > apdu_size) {
|
||||
apdu_len = 0;
|
||||
} else {
|
||||
apdu_len = bacnet_iam_request_encode(
|
||||
apdu, device_id, max_apdu, segmentation, vendor_id);
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode the I-Am service.
|
||||
*
|
||||
@@ -38,17 +107,94 @@ int iam_encode_apdu(
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
|
||||
apdu[1] = SERVICE_UNCONFIRMED_I_AM; /* service choice */
|
||||
apdu_len = 2;
|
||||
len = encode_application_object_id(
|
||||
&apdu[apdu_len], OBJECT_DEVICE, device_id);
|
||||
apdu_len += len;
|
||||
len = encode_application_unsigned(&apdu[apdu_len], max_apdu);
|
||||
apdu_len += len;
|
||||
len = encode_application_enumerated(
|
||||
&apdu[apdu_len], (uint32_t)segmentation);
|
||||
apdu_len += len;
|
||||
len = encode_application_unsigned(&apdu[apdu_len], vendor_id);
|
||||
apdu_len += len;
|
||||
}
|
||||
len = 2;
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = bacnet_iam_request_encode(
|
||||
apdu, device_id, max_apdu, segmentation, vendor_id);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode the I-Am-Request.
|
||||
* @ingroup BIBB-DM-DOB
|
||||
* @param apdu [in] Buffer containing the APDU
|
||||
* @param apdu_size [in] The length of the APDU
|
||||
* @param pDevice_id Pointer to the variable that shall take the device Id.
|
||||
* @param pMax_apdu Pointer to a variable that shall take the decoded length.
|
||||
* @param pSegmentation Pointer to a variable taking if segmentation is used.
|
||||
* @param pVendor_id Pointer to a variable taking the vendor id.
|
||||
* @return The number of bytes decoded , or #BACNET_STATUS_ERROR on error
|
||||
*/
|
||||
int bacnet_iam_request_decode(
|
||||
const uint8_t *apdu,
|
||||
unsigned apdu_size,
|
||||
uint32_t *pDevice_id,
|
||||
unsigned *pMax_apdu,
|
||||
int *pSegmentation,
|
||||
uint16_t *pVendor_id)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
/* should be a Device Object */
|
||||
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
|
||||
uint32_t object_instance = 0;
|
||||
uint32_t enum_value = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
|
||||
|
||||
/* OBJECT ID - object id */
|
||||
len = bacnet_object_id_application_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, &object_type, &object_instance);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (object_type != OBJECT_DEVICE) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (pDevice_id) {
|
||||
*pDevice_id = object_instance;
|
||||
}
|
||||
/* MAX APDU - unsigned */
|
||||
len = bacnet_unsigned_application_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, &unsigned_value);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (pMax_apdu) {
|
||||
*pMax_apdu = (unsigned)unsigned_value;
|
||||
}
|
||||
/* Segmentation - enumerated */
|
||||
len = bacnet_enumerated_application_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, &enum_value);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (enum_value >= MAX_BACNET_SEGMENTATION) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (pSegmentation) {
|
||||
*pSegmentation = (int)enum_value;
|
||||
}
|
||||
/* Vendor ID - unsigned16 */
|
||||
len = bacnet_unsigned_application_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, &unsigned_value);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (unsigned_value > 0xFFFF) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (pVendor_id) {
|
||||
*pVendor_id = (uint16_t)unsigned_value;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
@@ -72,69 +218,6 @@ int iam_decode_service_request(
|
||||
int *pSegmentation,
|
||||
uint16_t *pVendor_id)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
/* should be a Device Object */
|
||||
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
|
||||
uint32_t object_instance = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint32_t enum_value = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
|
||||
|
||||
/* OBJECT ID - object id */
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
|
||||
apdu_len += len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) {
|
||||
return -1;
|
||||
}
|
||||
len = decode_object_id(&apdu[apdu_len], &object_type, &object_instance);
|
||||
apdu_len += len;
|
||||
if (object_type != OBJECT_DEVICE) {
|
||||
return -1;
|
||||
}
|
||||
if (pDevice_id) {
|
||||
*pDevice_id = object_instance;
|
||||
}
|
||||
/* MAX APDU - unsigned */
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
|
||||
apdu_len += len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len = decode_unsigned(&apdu[apdu_len], len_value, &unsigned_value);
|
||||
apdu_len += len;
|
||||
if (pMax_apdu) {
|
||||
*pMax_apdu = (unsigned)unsigned_value;
|
||||
}
|
||||
/* Segmentation - enumerated */
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
|
||||
apdu_len += len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
return -1;
|
||||
}
|
||||
len = decode_enumerated(&apdu[apdu_len], len_value, &enum_value);
|
||||
apdu_len += len;
|
||||
if (enum_value >= MAX_BACNET_SEGMENTATION) {
|
||||
return -1;
|
||||
}
|
||||
if (pSegmentation) {
|
||||
*pSegmentation = (int)enum_value;
|
||||
}
|
||||
/* Vendor ID - unsigned16 */
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len], &tag_number, &len_value);
|
||||
apdu_len += len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len = decode_unsigned(&apdu[apdu_len], len_value, &unsigned_value);
|
||||
apdu_len += len;
|
||||
if (unsigned_value > 0xFFFF) {
|
||||
return -1;
|
||||
}
|
||||
if (pVendor_id) {
|
||||
*pVendor_id = (uint16_t)unsigned_value;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
return bacnet_iam_request_decode(
|
||||
apdu, MAX_APDU, pDevice_id, pMax_apdu, pSegmentation, pVendor_id);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,32 @@ int iam_decode_service_request(
|
||||
int *pSegmentation,
|
||||
uint16_t *pVendor_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_iam_request_decode(
|
||||
const uint8_t *apdu,
|
||||
unsigned apdu_size,
|
||||
uint32_t *pDevice_id,
|
||||
unsigned *pMax_apdu,
|
||||
int *pSegmentation,
|
||||
uint16_t *pVendor_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_iam_request_encode(
|
||||
uint8_t *apdu,
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
int segmentation,
|
||||
uint16_t vendor_id);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bacnet_iam_service_request_encode(
|
||||
uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
int segmentation,
|
||||
uint16_t vendor_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -26,7 +26,7 @@ static int iam_decode_apdu(
|
||||
int *pSegmentation,
|
||||
uint16_t *pVendor_id)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
int apdu_len;
|
||||
|
||||
/* valid data? */
|
||||
if (!apdu) {
|
||||
@@ -39,7 +39,8 @@ static int iam_decode_apdu(
|
||||
if (apdu[1] != SERVICE_UNCONFIRMED_I_AM) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len = iam_decode_service_request(
|
||||
apdu_len = 2;
|
||||
apdu_len += iam_decode_service_request(
|
||||
&apdu[2], pDevice_id, pMax_apdu, pSegmentation, pVendor_id);
|
||||
|
||||
return apdu_len;
|
||||
@@ -52,7 +53,7 @@ static void testIAm(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int test_len = 0, null_len = 0, apdu_len;
|
||||
uint32_t device_id = 42;
|
||||
unsigned max_apdu = 480;
|
||||
int segmentation = SEGMENTATION_NONE;
|
||||
@@ -62,19 +63,86 @@ static void testIAm(void)
|
||||
int test_segmentation = 0;
|
||||
uint16_t test_vendor_id = 0;
|
||||
|
||||
len =
|
||||
null_len =
|
||||
iam_encode_apdu(NULL, device_id, max_apdu, segmentation, vendor_id);
|
||||
apdu_len =
|
||||
iam_encode_apdu(&apdu[0], device_id, max_apdu, segmentation, vendor_id);
|
||||
zassert_not_equal(len, 0, NULL);
|
||||
zassert_not_equal(apdu_len, 0, NULL);
|
||||
zassert_equal(apdu_len, null_len, NULL);
|
||||
|
||||
len = iam_decode_apdu(
|
||||
test_len = iam_decode_apdu(
|
||||
&apdu[0], &test_device_id, &test_max_apdu, &test_segmentation,
|
||||
&test_vendor_id);
|
||||
|
||||
zassert_not_equal(len, -1, NULL);
|
||||
zassert_equal(
|
||||
test_len, apdu_len, "test_len=%d apdu_len=%d", test_len, apdu_len);
|
||||
zassert_equal(test_device_id, device_id, NULL);
|
||||
zassert_equal(test_vendor_id, vendor_id, NULL);
|
||||
zassert_equal(test_max_apdu, max_apdu, NULL);
|
||||
zassert_equal(test_segmentation, segmentation, NULL);
|
||||
|
||||
null_len = bacnet_iam_request_encode(
|
||||
NULL, device_id, max_apdu, segmentation, vendor_id);
|
||||
apdu_len = bacnet_iam_request_encode(
|
||||
&apdu[0], device_id, max_apdu, segmentation, vendor_id);
|
||||
zassert_not_equal(apdu_len, 0, NULL);
|
||||
zassert_equal(apdu_len, null_len, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], sizeof(apdu), &test_device_id, &test_max_apdu,
|
||||
&test_segmentation, &test_vendor_id);
|
||||
zassert_equal(test_len, apdu_len, NULL);
|
||||
zassert_equal(test_device_id, device_id, NULL);
|
||||
zassert_equal(test_vendor_id, vendor_id, NULL);
|
||||
zassert_equal(test_max_apdu, max_apdu, NULL);
|
||||
zassert_equal(test_segmentation, segmentation, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
NULL, apdu_len, &test_device_id, &test_max_apdu, &test_segmentation,
|
||||
&test_vendor_id);
|
||||
zassert_equal(test_len, BACNET_STATUS_ERROR, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], apdu_len, NULL, &test_max_apdu, &test_segmentation,
|
||||
&test_vendor_id);
|
||||
zassert_equal(test_len, apdu_len, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], apdu_len, &test_device_id, NULL, &test_segmentation,
|
||||
&test_vendor_id);
|
||||
zassert_equal(test_len, apdu_len, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], apdu_len, &test_device_id, &test_max_apdu, NULL,
|
||||
&test_vendor_id);
|
||||
zassert_equal(test_len, apdu_len, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], apdu_len, &test_device_id, &test_max_apdu, &test_segmentation,
|
||||
NULL);
|
||||
zassert_equal(test_len, apdu_len, NULL);
|
||||
while (apdu_len) {
|
||||
apdu_len--;
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], apdu_len, &test_device_id, &test_max_apdu,
|
||||
&test_segmentation, &test_vendor_id);
|
||||
zassert_equal(
|
||||
test_len, BACNET_STATUS_ERROR, "apdu_len=%d test_len=%d", apdu_len,
|
||||
test_len);
|
||||
}
|
||||
apdu_len = bacnet_iam_service_request_encode(
|
||||
apdu, sizeof(apdu), device_id, max_apdu, segmentation, vendor_id);
|
||||
zassert_not_equal(apdu_len, 0, NULL);
|
||||
zassert_equal(apdu_len, null_len, NULL);
|
||||
while (apdu_len) {
|
||||
apdu_len--;
|
||||
test_len = bacnet_iam_service_request_encode(
|
||||
apdu, apdu_len, device_id, max_apdu, segmentation, vendor_id);
|
||||
zassert_equal(test_len, 0, NULL);
|
||||
}
|
||||
/* internal bounds checking - segmentation enumeration */
|
||||
apdu_len = bacnet_iam_request_encode(
|
||||
&apdu[0], device_id, max_apdu, MAX_BACNET_SEGMENTATION, vendor_id);
|
||||
zassert_not_equal(apdu_len, 0, NULL);
|
||||
test_len = bacnet_iam_request_decode(
|
||||
&apdu[0], sizeof(apdu), &test_device_id, &test_max_apdu,
|
||||
&test_segmentation, &test_vendor_id);
|
||||
zassert_equal(
|
||||
test_len, BACNET_STATUS_ERROR, "apdu_len=%d test_len=%d", apdu_len,
|
||||
test_len);
|
||||
}
|
||||
/**
|
||||
* @}
|
||||
|
||||
Reference in New Issue
Block a user