Fixed network port object to accept host name option of host-n-port writes. (#997)

This commit is contained in:
Steve Karg
2025-05-20 15:44:47 -05:00
committed by GitHub
parent 524fd162f9
commit b3fd374c84
9 changed files with 1077 additions and 133 deletions
+303
View File
@@ -374,6 +374,210 @@ bool host_n_port_copy(BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT *src)
return status;
}
/**
* @brief Initialize a BACnetHostNPort_Minimal structure for IP address
* @param host - BACnetHostNPort_Minimal structure
* @param port - port number
* @param address - BACnetHostAddress
*/
void host_n_port_minimal_ip_init(
BACNET_HOST_N_PORT_MINIMAL *host,
uint16_t port,
const uint8_t *address,
size_t address_len)
{
unsigned i, imax;
if (host) {
host->tag = BACNET_HOST_ADDRESS_TAG_IP_ADDRESS;
host->host.ip_address.length = address_len;
imax = min(address_len, sizeof(host->host.ip_address.address));
if (address) {
for (i = 0; i < imax; i++) {
host->host.ip_address.address[i] = address[i];
}
} else {
for (i = 0; i < imax; i++) {
host->host.ip_address.address[i] = 0;
}
}
host->port = port;
}
}
/**
* @brief copy the BACnetHostNPort_Minimal complex data
* @param dest - destination structure
* @param src - source structure
* @return true if successfully copied
*/
bool host_n_port_minimal_copy(
BACNET_HOST_N_PORT_MINIMAL *dest, const BACNET_HOST_N_PORT_MINIMAL *src)
{
bool status = false;
int i;
if (dest && src) {
dest->tag = src->tag;
dest->port = src->port;
if (src->tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) {
status = true;
dest->host.ip_address.length = src->host.ip_address.length;
for (i = 0; i < src->host.ip_address.length; i++) {
if (i < sizeof(dest->host.ip_address.address)) {
dest->host.ip_address.address[i] =
src->host.ip_address.address[i];
}
}
} else if (src->tag == BACNET_HOST_ADDRESS_TAG_NAME) {
status = true;
dest->host.name.length = src->host.name.length;
for (i = 0; i < src->host.name.length; i++) {
if (i < sizeof(dest->host.name.fqdn)) {
dest->host.name.fqdn[i] = src->host.name.fqdn[i];
}
}
} else if (src->tag == BACNET_HOST_ADDRESS_TAG_NONE) {
status = true;
}
}
return status;
}
/**
* @brief Copy the BACnetHostNPort complex data from src to dst
* @param dst - destination structure
* @param src - source structure
* @return true if successfully copied
*/
bool host_n_port_from_minimal(
BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT_MINIMAL *src)
{
bool status = false;
if (dst && src) {
switch (src->tag) {
case BACNET_HOST_ADDRESS_TAG_NONE:
dst->host_ip_address = false;
dst->host_name = false;
status = true;
break;
case BACNET_HOST_ADDRESS_TAG_IP_ADDRESS:
dst->host_ip_address = true;
dst->host_name = false;
octetstring_init(
&dst->host.ip_address, src->host.ip_address.address,
src->host.ip_address.length);
status = true;
break;
case BACNET_HOST_ADDRESS_TAG_NAME:
dst->host_ip_address = false;
dst->host_name = true;
characterstring_init(
&dst->host.name, CHARACTER_ANSI_X34, src->host.name.fqdn,
src->host.name.length);
status = true;
break;
default:
return false; /* invalid tag number */
}
if (status) {
dst->port = src->port;
}
}
return status;
}
/**
* @brief Copy the BACnetHostNPort complex data from src to dst
* @param dst - destination structure
* @param src - source structure
* @return true if successfully copied
*/
bool host_n_port_to_minimal(
BACNET_HOST_N_PORT_MINIMAL *dst, const BACNET_HOST_N_PORT *src)
{
bool status = false;
if (dst && src) {
if (src->host_ip_address) {
dst->tag = BACNET_HOST_ADDRESS_TAG_IP_ADDRESS;
dst->host.ip_address.length = octetstring_copy_value(
dst->host.ip_address.address,
sizeof(dst->host.ip_address.address), &src->host.ip_address);
if (dst->host.ip_address.length > 0) {
status = true;
}
} else if (src->host_name) {
dst->tag = BACNET_HOST_ADDRESS_TAG_NAME;
dst->host.name.length = characterstring_copy_value(
dst->host.name.fqdn, sizeof(dst->host.name.fqdn),
&src->host.name);
if (dst->host.name.length > 0) {
status = true;
}
} else {
dst->tag = BACNET_HOST_ADDRESS_TAG_NONE;
status = true;
}
dst->port = src->port;
}
return status;
}
bool host_n_port_minimal_same(
const BACNET_HOST_N_PORT_MINIMAL *dst,
const BACNET_HOST_N_PORT_MINIMAL *src)
{
bool status = false;
int i;
if (dst && src) {
if (dst->tag == src->tag) {
if (dst->tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) {
if (dst->host.ip_address.length ==
src->host.ip_address.length) {
status = true;
for (i = 0; i < dst->host.ip_address.length; i++) {
if (i < sizeof(dst->host.ip_address.address)) {
if (dst->host.ip_address.address[i] !=
src->host.ip_address.address[i]) {
status = false;
break;
}
}
}
}
} else if (dst->tag == BACNET_HOST_ADDRESS_TAG_NAME) {
if (dst->host.name.length == src->host.name.length) {
status = true;
for (i = 0; i < dst->host.name.length; i++) {
if (i < sizeof(dst->host.name.fqdn)) {
if (dst->host.name.fqdn[i] !=
src->host.name.fqdn[i]) {
status = false;
break;
}
}
}
}
} else if (dst->tag == BACNET_HOST_ADDRESS_TAG_NONE) {
status = true;
}
if (status) {
if (dst->port != src->port) {
status = false;
}
}
}
}
return status;
}
/**
* @brief Compare the BACnetHostNPort complex data of src and dst
* @param host1 - host 1 structure
@@ -1159,3 +1363,102 @@ int bacnet_fdt_entry_to_ascii(
return len;
}
/**
* @brief Checks conformance of a hostname with:
* RFC 1123: Requirements for Internet Hosts application and support
* RFC 2181: Clarifications to the DNS specification
*
* Host software MUST handle host names of up to 63 characters and
* SHOULD handle host names of up to 255 characters.
*
* Whenever a user inputs the identity of an Internet host, it SHOULD
* be possible to enter either (1) a host domain name or (2) an IP
* address in dotted-decimal ("#.#.#.#") form. The host SHOULD check
* the string syntactically for a dotted-decimal number before
* looking it up in the Domain Name System.
*
* The DNS itself places only one restriction on the particular labels
* that can be used to identify resource records. That one restriction
* relates to the length of the label and the full name. The length of
* any one label is limited to between 1 and 63 octets. A full domain
* name is limited to 255 octets (including the separators). The zero
* length full name is defined as representing the root of the DNS tree,
* and is typically written and displayed as ".". Those restrictions
* aside, any binary string whatever can be used as the label of any
* resource record. Similarly, any binary string can serve as the value
* of any record that includes a domain name as some or all of its value
* (SOA, NS, MX, PTR, CNAME, and any others that may be added).
*
* @param hostname - hostname as BACNET_CHARACTER_STRING
* @return true if the host name conorms to RFC 1123, false otherwise
*/
bool bacnet_is_valid_hostname(const BACNET_CHARACTER_STRING *const hostname)
{
int len;
const char *val;
int dot_count = 0;
int i = 0;
char c;
char fqdn_copy[255 + 1] = { 0 };
char *label = NULL;
len = characterstring_length(hostname);
/* Check length */
if ((len == 0) || (len > sizeof(fqdn_copy) - 1)) {
/* Invalid length */
return false;
}
/* Check if it looks like an IP address (basic check) */
val = characterstring_value(hostname);
for (i = 0; i < len; i++) {
if (val[i] == '.') {
dot_count++;
}
}
/* Check if it's a numeric pattern (like an incomplete IP) */
if (dot_count > 0 && strspn(val, "0123456789.") == len) {
/* Invalid: looks like an incomplete IP */
return false;
}
/* Check each character */
for (i = 0; i < len; i++) {
c = val[i];
if (!isalnum(c) && c != '-' && c != '.') {
/* Invalid character */
return false;
}
/* Check for starting and ending hyphens */
if (i == 0 && c == '-') {
/* Cannot start with a hyphen */
return false;
}
if (i == len - 1 && c == '-') {
/* Cannot end with a hyphen */
return false;
}
/* Check for consecutive periods or hyphens */
if (i > 0 &&
((val[i] == '-' && val[i - 1] == '-') ||
(val[i] == '.' && val[i - 1] == '.'))) {
/* Invalid consecutive characters */
return false;
}
}
/* Make a copy to manipulate when checking each label */
strncpy(fqdn_copy, val, sizeof(fqdn_copy) - 1);
/* Split FQDN by '.' */
label = strtok(fqdn_copy, ".");
while (label != NULL) {
/* check for each label length not exceeding 63 characters */
if (strlen(label) > 63) {
/* Invalid label found */
return false;
}
/* Move to the next label */
label = strtok(NULL, ".");
}
/* Valid hostname */
return true;
}