glib-aux: add nm_hostname_is_valid() helper from systemd

This commit is contained in:
Thomas Haller
2022-04-12 17:16:11 +02:00
parent 202d9c36c3
commit 9ff1f66680
3 changed files with 130 additions and 0 deletions

View File

@@ -7175,3 +7175,79 @@ nm_path_simplify(char *path)
*f = '\0';
return path;
}
/*****************************************************************************/
static gboolean
valid_ldh_char(char c)
{
/* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-';
}
/**
* nm_hostname_is_valid:
* @s: the hostname to check.
* @trailing_dot: Accept trailing dot on multi-label names.
*
* Return: %TRUE if valid.
*/
gboolean
nm_hostname_is_valid(const char *s, gboolean trailing_dot)
{
unsigned n_dots = 0;
const char *p;
gboolean dot;
gboolean hyphen;
/* Copied from systemd's hostname_is_valid()
* https://github.com/systemd/systemd/blob/bc85f8b51d962597360e982811e674c126850f56/src/basic/hostname-util.c#L85 */
/* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only
* checks if the name is composed of allowed characters and the length is not above the maximum
* allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if
* VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note
* that due to the restricted charset and length this call is substantially more conservative than
* dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames
* with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */
if (nm_str_is_empty(s))
return FALSE;
for (p = s, dot = hyphen = TRUE; *p; p++)
if (*p == '.') {
if (dot || hyphen)
return FALSE;
dot = TRUE;
hyphen = FALSE;
n_dots++;
} else if (*p == '-') {
if (dot)
return FALSE;
dot = FALSE;
hyphen = TRUE;
} else {
if (!valid_ldh_char(*p))
return FALSE;
dot = FALSE;
hyphen = FALSE;
}
if (dot && (n_dots < 2 || !trailing_dot))
return FALSE;
if (hyphen)
return FALSE;
/* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to
* 255 characters */
if (p - s > HOST_NAME_MAX)
return FALSE;
return TRUE;
}

View File

@@ -3346,4 +3346,8 @@ nm_path_startswith(const char *path, const char *prefix)
return nm_path_startswith_full(path, prefix, TRUE);
}
/*****************************************************************************/
gboolean nm_hostname_is_valid(const char *s, gboolean trailing_dot);
#endif /* __NM_SHARED_UTILS_H__ */

View File

@@ -2101,6 +2101,55 @@ test_path_simplify(void)
/*****************************************************************************/
static void
test_hostname_is_valid(void)
{
g_assert(nm_hostname_is_valid("foobar", FALSE));
g_assert(nm_hostname_is_valid("foobar.com", FALSE));
g_assert(!nm_hostname_is_valid("foobar.com.", FALSE));
g_assert(nm_hostname_is_valid("fooBAR", FALSE));
g_assert(nm_hostname_is_valid("fooBAR.com", FALSE));
g_assert(!nm_hostname_is_valid("fooBAR.", FALSE));
g_assert(!nm_hostname_is_valid("fooBAR.com.", FALSE));
g_assert(!nm_hostname_is_valid("fööbar", FALSE));
g_assert(!nm_hostname_is_valid("", FALSE));
g_assert(!nm_hostname_is_valid(".", FALSE));
g_assert(!nm_hostname_is_valid("..", FALSE));
g_assert(!nm_hostname_is_valid("foobar.", FALSE));
g_assert(!nm_hostname_is_valid(".foobar", FALSE));
g_assert(!nm_hostname_is_valid("foo..bar", FALSE));
g_assert(!nm_hostname_is_valid("foo.bar..", FALSE));
g_assert(
!nm_hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
FALSE));
g_assert(!nm_hostname_is_valid(
"au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local",
FALSE));
g_assert(nm_hostname_is_valid("foobar", TRUE));
g_assert(nm_hostname_is_valid("foobar.com", TRUE));
g_assert(nm_hostname_is_valid("foobar.com.", TRUE));
g_assert(nm_hostname_is_valid("fooBAR", TRUE));
g_assert(nm_hostname_is_valid("fooBAR.com", TRUE));
g_assert(!nm_hostname_is_valid("fooBAR.", TRUE));
g_assert(nm_hostname_is_valid("fooBAR.com.", TRUE));
g_assert(!nm_hostname_is_valid("fööbar", TRUE));
g_assert(!nm_hostname_is_valid("", TRUE));
g_assert(!nm_hostname_is_valid(".", TRUE));
g_assert(!nm_hostname_is_valid("..", TRUE));
g_assert(!nm_hostname_is_valid("foobar.", TRUE));
g_assert(!nm_hostname_is_valid(".foobar", TRUE));
g_assert(!nm_hostname_is_valid("foo..bar", TRUE));
g_assert(!nm_hostname_is_valid("foo.bar..", TRUE));
g_assert(
!nm_hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
TRUE));
}
/*****************************************************************************/
NMTST_DEFINE();
int
@@ -2144,6 +2193,7 @@ main(int argc, char **argv)
g_test_add_func("/general/test_path_find_first_component", test_path_find_first_component);
g_test_add_func("/general/test_path_startswith", test_path_startswith);
g_test_add_func("/general/test_path_simplify", test_path_simplify);
g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid);
return g_test_run();
}