From: Lennart Poettering Date: Thu, 6 Mar 2025 17:31:12 +0000 (+0100) Subject: hostname: support that /etc/hostname contains ??? as wildcards to be replaced by... X-Git-Tag: v258-rc1~1111^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af9c45d5b6c1b1962731dffc638a2320e5eef095;p=thirdparty%2Fsystemd.git hostname: support that /etc/hostname contains ??? as wildcards to be replaced by hash value from /etc/machine-id --- diff --git a/man/hostname.xml b/man/hostname.xml index 746de21cd12..76acb8d7a5c 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -43,6 +43,13 @@ attempt to make the name valid, but obviously it is recommended to use a valid name and not rely on this filtering. + If the question mark character ? appears in + the hostname, it is automatically substituted by a hexadecimal character derived from the + machine-id5 when + applied, securely and deterministically by cryptographic hashing. Example: + foobar-????-???? will automatically expand to foobar-92a9-061c or + similar, depending on the local machine ID. + You may use hostnamectl1 to change the value of this file during runtime from the command line. Use diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml index 70a6d295f6b..07cb0bc005a 100644 --- a/man/hostnamectl.xml +++ b/man/hostnamectl.xml @@ -88,6 +88,8 @@ name labels), or a sequence of such labels separated by single dots that forms a valid DNS FQDN. The hostname must be at most 64 characters, which is a Linux limitation (DNS allows longer names). + + diff --git a/man/os-release.xml b/man/os-release.xml index 548eb47a4c0..54978edd43c 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -538,6 +538,8 @@ that forms a valid DNS FQDN. The hostname must be at most 64 characters, which is a Linux limitation (DNS allows longer names). + + See org.freedesktop.hostname15 for a description of how systemd-hostnamed.service8 diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index 165b1e2e16c..7159473b582 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -14,13 +14,16 @@ #include "string-util.h" #include "strv.h" -char* get_default_hostname(void) { +char* get_default_hostname_raw(void) { int r; + /* Returns the default hostname, and leaves any ??? in place. */ + const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME"); if (e) { - if (hostname_is_valid(e, 0)) + if (hostname_is_valid(e, VALID_HOSTNAME_QUESTION_MARK)) return strdup(e); + log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e); } @@ -29,8 +32,9 @@ char* get_default_hostname(void) { if (r < 0) log_debug_errno(r, "Failed to parse os-release, ignoring: %m"); else if (f) { - if (hostname_is_valid(f, 0)) + if (hostname_is_valid(f, VALID_HOSTNAME_QUESTION_MARK)) return TAKE_PTR(f); + log_debug("Invalid hostname in os-release, ignoring: %s", f); } @@ -81,7 +85,7 @@ bool hostname_is_valid(const char *s, ValidHostnameFlags flags) { hyphen = true; } else { - if (!valid_ldh_char(*p)) + if (!valid_ldh_char(*p) && (*p != '?' || !FLAGS_SET(flags, VALID_HOSTNAME_QUESTION_MARK))) return false; dot = false; @@ -123,7 +127,7 @@ char* hostname_cleanup(char *s) { dot = false; hyphen = true; - } else if (valid_ldh_char(*p)) { + } else if (valid_ldh_char(*p) || *p == '?') { *(d++) = *p; dot = false; hyphen = false; diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 4449c1eb397..4c5abe760f0 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -7,13 +7,14 @@ #include "macro.h" #include "strv.h" -char* get_default_hostname(void); +char* get_default_hostname_raw(void); bool valid_ldh_char(char c) _const_; typedef enum ValidHostnameFlags { - VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */ - VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */ + VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */ + VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */ + VALID_HOSTNAME_QUESTION_MARK = 1 << 2, /* Accept "?" as place holder for hashed machine ID value */ } ValidHostnameFlags; bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_; diff --git a/src/core/main.c b/src/core/main.c index 0368ec891fc..ee4b2d6bafb 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -2453,12 +2453,11 @@ static int initialize_runtime( (void) import_credentials(); (void) os_release_status(); - (void) hostname_setup(/* really = */ true); (void) machine_id_setup(/* root = */ NULL, arg_machine_id, (first_boot ? MACHINE_ID_SETUP_FORCE_TRANSIENT : 0) | (arg_machine_id_from_firmware ? MACHINE_ID_SETUP_FORCE_FIRMWARE : 0), /* ret_machine_id = */ NULL); - + (void) hostname_setup(/* really = */ true); (void) loopback_setup(); bump_unix_max_dgram_qlen(); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 9ef8d89560e..cd72f543969 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -1460,7 +1460,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_HOSTNAME: - if (!hostname_is_valid(optarg, VALID_HOSTNAME_TRAILING_DOT)) + if (!hostname_is_valid(optarg, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Host name %s is not valid.", optarg); diff --git a/src/fuzz/fuzz-hostname-setup.c b/src/fuzz/fuzz-hostname-setup.c index 4895631b67b..6ca0dc6fa53 100644 --- a/src/fuzz/fuzz-hostname-setup.c +++ b/src/fuzz/fuzz-hostname-setup.c @@ -14,7 +14,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fuzz_setup_logging(); - (void) read_etc_hostname_stream(f, &ret); + (void) read_etc_hostname_stream(f, /* substitute_wildcards= */ true, &ret); return 0; } diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index bf03f177d37..144e3550a30 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -598,7 +598,7 @@ static int set_hostname(int argc, char **argv, void *userdata) { /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic * hostname. */ - if (implicit && hostname_is_valid(hostname, VALID_HOSTNAME_TRAILING_DOT)) + if (implicit && hostname_is_valid(hostname, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK)) p = ""; /* No pretty hostname (as it is redundant), just a static one */ else p = hostname; /* Use the passed name as pretty hostname */ diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 110f0a7400c..fb80e1280a6 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -52,6 +52,7 @@ typedef enum { /* Read from /etc/hostname */ PROP_STATIC_HOSTNAME, + PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS, /* Read from /etc/machine-info */ PROP_PRETTY_HOSTNAME, @@ -125,11 +126,25 @@ static void context_read_etc_hostname(Context *c) { stat_inode_unmodified(&c->etc_hostname_stat, ¤t_stat)) return; - context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME); + context_reset(c, + (UINT64_C(1) << PROP_STATIC_HOSTNAME) | + (UINT64_C(1) << PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS)); - r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]); - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m"); + r = read_etc_hostname(/* path= */ NULL, /* substitute_wildcards= */ false, &c->data[PROP_STATIC_HOSTNAME]); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m"); + } else { + _cleanup_free_ char *substituted = strdup(c->data[PROP_STATIC_HOSTNAME]); + if (!substituted) + return (void) log_oom(); + + r = hostname_substitute_wildcards(substituted); + if (r < 0) + log_warning_errno(r, "Failed to substitute wildcards in /etc/hostname, ignoring: %m"); + else + c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS] = TAKE_PTR(substituted); + } c->etc_hostname_stat = current_stat; } @@ -678,8 +693,8 @@ static int context_update_kernel_hostname( assert(c); /* /etc/hostname has the highest preference ... */ - if (c->data[PROP_STATIC_HOSTNAME]) { - hn = c->data[PROP_STATIC_HOSTNAME]; + if (c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS]) { + hn = c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS]; hns = HOSTNAME_STATIC; /* ... the transient hostname, (ie: DHCP) comes next ... */ @@ -946,7 +961,7 @@ static int property_get_static_hostname( context_read_etc_hostname(c); - return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]); + return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS]); } static int property_get_default_hostname( @@ -978,7 +993,7 @@ static void context_determine_hostname_source(Context *c) { (void) gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hostname); - if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME])) + if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS])) c->hostname_source = HOSTNAME_STATIC; else { _cleanup_free_ char *fallback = NULL; @@ -1201,6 +1216,31 @@ static int property_get_vsock_cid( return sd_bus_message_append(reply, "u", (uint32_t) local_cid); } +static int validate_and_substitute_hostname(const char *name, char **ret_substituted, sd_bus_error *error) { + int r; + + assert(ret_substituted); + + if (!name) { + *ret_substituted = NULL; + return 0; + } + + _cleanup_free_ char *substituted = strdup(name); + if (!substituted) + return log_oom(); + + r = hostname_substitute_wildcards(substituted); + if (r < 0) + return log_error_errno(r, "Failed to substitute wildcards in hotname: %m"); + + if (!hostname_is_valid(substituted, 0)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); + + *ret_substituted = TAKE_PTR(substituted); + return 1; +} + static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = ASSERT_PTR(userdata); const char *name; @@ -1217,10 +1257,12 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * /* We always go through with the procedure below without comparing to the current hostname, because * we might want to adjust hostname source information even if the actual hostname is unchanged. */ - if (name && !hostname_is_valid(name, 0)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); + _cleanup_free_ char *substituted = NULL; + r = validate_and_substitute_hostname(name, &substituted, error); + if (r < 0) + return r; - context_read_etc_hostname(c); + name = substituted; r = bus_verify_polkit_async_full( m, @@ -1235,6 +1277,8 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + context_read_etc_hostname(c); + r = context_update_kernel_hostname(c, name); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m"); @@ -1249,8 +1293,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = ASSERT_PTR(userdata); const char *name; - int interactive; - int r; + int interactive, r; assert(m); @@ -1265,8 +1308,10 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME])) return sd_bus_reply_method_return(m, NULL); - if (name && !hostname_is_valid(name, 0)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name); + _cleanup_free_ char *substituted = NULL; + r = validate_and_substitute_hostname(name, &substituted, error); + if (r < 0) + return r; r = bus_verify_polkit_async_full( m, @@ -1285,6 +1330,8 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ if (r < 0) return r; + free_and_replace(c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS], substituted); + r = context_write_data_static_hostname(c); if (r < 0) { log_error_errno(r, "Failed to write static hostname: %m"); @@ -1295,7 +1342,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m"); } - r = context_update_kernel_hostname(c, NULL); + r = context_update_kernel_hostname(c, /* transient_hostname= */ NULL); if (r < 0) { log_error_errno(r, "Failed to set hostname: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m"); @@ -1505,20 +1552,14 @@ static int build_describe_response(Context *c, bool privileged, sd_json_variant context_read_os_release(c); context_determine_hostname_source(c); - r = gethostname_strict(&hn); - if (r < 0) { - if (r != -ENXIO) - return log_error_errno(r, "Failed to read local host name: %m"); - - hn = get_default_hostname(); - if (!hn) - return log_oom(); - } - dhn = get_default_hostname(); if (!dhn) return log_oom(); + r = gethostname_strict(&hn); + if (r < 0 && r != -ENXIO) + return log_error_errno(r, "Failed to read local host name: %m"); + if (isempty(c->data[PROP_ICON_NAME])) in = context_fallback_icon_name(c); @@ -1559,8 +1600,8 @@ static int build_describe_response(Context *c, bool privileged, sd_json_variant r = sd_json_buildo( &v, - SD_JSON_BUILD_PAIR_STRING("Hostname", hn), - SD_JSON_BUILD_PAIR_STRING("StaticHostname", c->data[PROP_STATIC_HOSTNAME]), + SD_JSON_BUILD_PAIR_STRING("Hostname", hn ?: dhn), + SD_JSON_BUILD_PAIR_STRING("StaticHostname", c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS]), SD_JSON_BUILD_PAIR_STRING("PrettyHostname", c->data[PROP_PRETTY_HOSTNAME]), SD_JSON_BUILD_PAIR_STRING("DefaultHostname", dhn), SD_JSON_BUILD_PAIR_STRING("HostnameSource", hostname_source_to_string(c->hostname_source)), diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index e99a0d6cae8..f9a1b2ffaa5 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1654,7 +1654,7 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) { if (r < 0 && r != -ENOENT) log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name); else if (r >= 0) { - r = read_etc_hostname(path, &hostname); + r = read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname); if (r < 0) log_debug_errno(r, "Failed to read /etc/hostname of image %s: %m", i->name); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index ef3a456ea7f..91f9d0c9ccf 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -3692,7 +3692,7 @@ int dissected_image_acquire_metadata( switch (k) { case META_HOSTNAME: - r = read_etc_hostname_stream(f, &hostname); + r = read_etc_hostname_stream(f, /* substitute_wildcards= */ false, &hostname); if (r < 0) log_debug_errno(r, "Failed to read /etc/hostname of image: %m"); diff --git a/src/shared/hostname-setup.c b/src/shared/hostname-setup.c index 1904885189a..af89f92bdc4 100644 --- a/src/shared/hostname-setup.c +++ b/src/shared/hostname-setup.c @@ -13,12 +13,14 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "hostname-setup.h" #include "hostname-util.h" #include "initrd-util.h" #include "log.h" #include "macro.h" #include "proc-cmdline.h" +#include "siphash24.h" #include "string-table.h" #include "string-util.h" @@ -95,7 +97,7 @@ static int acquire_hostname_from_credential(char **ret) { return 0; } -int read_etc_hostname_stream(FILE *f, char **ret) { +int read_etc_hostname_stream(FILE *f, bool substitute_wildcards, char **ret) { int r; assert(f); @@ -114,9 +116,19 @@ int read_etc_hostname_stream(FILE *f, char **ret) { if (IN_SET(line[0], '\0', '#')) continue; + if (substitute_wildcards) { + r = hostname_substitute_wildcards(line); + if (r < 0) + return r; + } + hostname_cleanup(line); /* normalize the hostname */ - if (!hostname_is_valid(line, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */ + /* check that the hostname we return is valid */ + if (!hostname_is_valid( + line, + VALID_HOSTNAME_TRAILING_DOT| + (substitute_wildcards ? 0 : VALID_HOSTNAME_QUESTION_MARK))) return -EBADMSG; *ret = TAKE_PTR(line); @@ -124,7 +136,7 @@ int read_etc_hostname_stream(FILE *f, char **ret) { } } -int read_etc_hostname(const char *path, char **ret) { +int read_etc_hostname(const char *path, bool substitute_wildcards, char **ret) { _cleanup_fclose_ FILE *f = NULL; assert(ret); @@ -136,12 +148,14 @@ int read_etc_hostname(const char *path, char **ret) { if (!f) return -errno; - return read_etc_hostname_stream(f, ret); + return read_etc_hostname_stream(f, substitute_wildcards, ret); } void hostname_update_source_hint(const char *hostname, HostnameSource source) { int r; + assert(hostname); + /* Why save the value and not just create a flag file? This way we will * notice if somebody sets the hostname directly (not going through hostnamed). */ @@ -152,7 +166,7 @@ void hostname_update_source_hint(const char *hostname, HostnameSource source) { if (r < 0) log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\", ignoring: %m"); } else - unlink_or_warn("/run/systemd/default-hostname"); + (void) unlink_or_warn("/run/systemd/default-hostname"); } int hostname_setup(bool really) { @@ -174,7 +188,7 @@ int hostname_setup(bool really) { } if (!hn) { - r = read_etc_hostname(NULL, &hn); + r = read_etc_hostname(/* path= */ NULL, /* substitute_wildcards= */ true, &hn); if (r == -ENOENT) enoent = true; else if (r < 0) @@ -238,6 +252,67 @@ static const char* const hostname_source_table[] = { DEFINE_STRING_TABLE_LOOKUP(hostname_source, HostnameSource); +int hostname_substitute_wildcards(char *name) { + static const sd_id128_t key = SD_ID128_MAKE(98,10,ad,df,8d,7d,4f,b5,89,1b,4b,56,ac,c2,26,8f); + sd_id128_t mid = SD_ID128_NULL; + size_t left_bits = 0, counter = 0; + uint64_t h = 0; + int r; + + assert(name); + + /* Replaces every occurrence of '?' in the specified string with a nibble hashed from + * /etc/machine-id. This is supposed to be used on /etc/hostname files that want to automatically + * configure a hostname derived from the machine ID in some form. + * + * Note that this does not directly use the machine ID, because that's not necessarily supposed to be + * public information to be broadcast on the network, while the hostname certainly is. */ + + for (char *n = name; *n; n++) { + if (*n != '?') + continue; + + if (left_bits <= 0) { + if (sd_id128_is_null(mid)) { + r = sd_id128_get_machine(&mid); + if (r < 0) + return r; + } + + struct siphash state; + siphash24_init(&state, key.bytes); + siphash24_compress(&mid, sizeof(mid), &state); + siphash24_compress(&counter, sizeof(counter), &state); /* counter mode */ + h = siphash24_finalize(&state); + left_bits = sizeof(h) * 8; + counter++; + } + + assert(left_bits >= 4); + *n = hexchar(h & 0xf); + h >>= 4; + left_bits -= 4; + } + + return 0; +} + +char* get_default_hostname(void) { + int r; + + _cleanup_free_ char *h = get_default_hostname_raw(); + if (!h) + return NULL; + + r = hostname_substitute_wildcards(h); + if (r < 0) { + log_debug_errno(r, "Failed to substitute wildcards in hostname, falling back to built-in name: %m"); + return strdup(FALLBACK_HOSTNAME); + } + + return TAKE_PTR(h); +} + int gethostname_full(GetHostnameFlags flags, char **ret) { _cleanup_free_ char *buf = NULL, *fallback = NULL; struct utsname u; diff --git a/src/shared/hostname-setup.h b/src/shared/hostname-setup.h index 3244c6c368d..bc602acbfc4 100644 --- a/src/shared/hostname-setup.h +++ b/src/shared/hostname-setup.h @@ -18,12 +18,16 @@ int sethostname_idempotent(const char *s); int shorten_overlong(const char *s, char **ret); -int read_etc_hostname_stream(FILE *f, char **ret); -int read_etc_hostname(const char *path, char **ret); +int read_etc_hostname_stream(FILE *f, bool substitute_wildcards, char **ret); +int read_etc_hostname(const char *path, bool substitue_wildcards, char **ret); void hostname_update_source_hint(const char *hostname, HostnameSource source); int hostname_setup(bool really); +int hostname_substitute_wildcards(char *name); + +char* get_default_hostname(void); + typedef enum GetHostnameFlags { GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */ GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */ diff --git a/src/test/test-hostname-setup.c b/src/test/test-hostname-setup.c index 67da2f03f11..da6e3796995 100644 --- a/src/test/test-hostname-setup.c +++ b/src/test/test-hostname-setup.c @@ -3,9 +3,12 @@ #include #include "alloc-util.h" +#include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "hostname-setup.h" +#include "hostname-util.h" +#include "id128-util.h" #include "string-util.h" #include "tests.h" #include "tmpfile-util.h" @@ -13,48 +16,64 @@ TEST(read_etc_hostname) { _cleanup_(unlink_tempfilep) char path[] = "/tmp/hostname.XXXXXX"; char *hostname; - int fd; + int r; - fd = mkostemp_safe(path); - assert_se(fd > 0); - close(fd); + safe_close(ASSERT_FD(mkostemp_safe(path))); /* simple hostname */ - assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0); - assert_se(read_etc_hostname(path, &hostname) == 0); + ASSERT_OK(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE)); + ASSERT_OK(read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname)); ASSERT_STREQ(hostname, "foo"); hostname = mfree(hostname); /* with comment */ - assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0); - assert_se(read_etc_hostname(path, &hostname) == 0); - assert_se(hostname); + ASSERT_OK(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE)); + ASSERT_OK(read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname)); + ASSERT_NOT_NULL(hostname); ASSERT_STREQ(hostname, "foo"); hostname = mfree(hostname); /* with comment and extra whitespace */ - assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0); - assert_se(read_etc_hostname(path, &hostname) == 0); - assert_se(hostname); + ASSERT_OK(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE)); + ASSERT_OK(read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname)); + ASSERT_NOT_NULL(hostname); ASSERT_STREQ(hostname, "foo"); hostname = mfree(hostname); /* cleans up name */ - assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0); - assert_se(read_etc_hostname(path, &hostname) == 0); - assert_se(hostname); + ASSERT_OK(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE)); + ASSERT_OK(read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname)); + ASSERT_NOT_NULL(hostname); ASSERT_STREQ(hostname, "foobar.com"); hostname = mfree(hostname); + /* with wildcards */ + ASSERT_OK(write_string_file(path, "foo????????x??????????u", WRITE_STRING_FILE_CREATE)); + ASSERT_OK(read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname)); + ASSERT_NOT_NULL(hostname); + ASSERT_STREQ(hostname, "foo????????x??????????u"); + hostname = mfree(hostname); + r = read_etc_hostname(path, /* substitute_wildcards= */ true, &hostname); + if (ERRNO_IS_NEG_MACHINE_ID_UNSET(r)) + log_tests_skipped("skipping wildcard hostname tests, no machine ID defined"); + else { + ASSERT_OK(r); + ASSERT_NOT_NULL(hostname); + ASSERT_NULL(strchr(hostname, '?')); + ASSERT_EQ(fnmatch("foo????????x??????????u", hostname, /* flags= */ 0), 0); + ASSERT_TRUE(hostname_is_valid(hostname, /* flags= */ 0)); + hostname = mfree(hostname); + } + /* no value set */ hostname = (char*) 0x1234; - assert_se(write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE) == 0); - assert_se(read_etc_hostname(path, &hostname) == -ENOENT); - assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */ + ASSERT_OK(write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_TRUNCATE)); + ASSERT_ERROR(read_etc_hostname(path, /* substitute_wildcards= */ false, &hostname), ENOENT); + assert(hostname == (char*) 0x1234); /* does not touch argument on error */ /* nonexisting file */ - assert_se(read_etc_hostname("/non/existing", &hostname) == -ENOENT); - assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */ + ASSERT_ERROR(read_etc_hostname("/non/existing", /* substitute_wildcards= */ false, &hostname), ENOENT); + assert(hostname == (char*) 0x1234); /* does not touch argument on error */ } TEST(hostname_setup) { @@ -71,4 +90,21 @@ TEST(hostname_malloc) { log_info("hostname_short_malloc: \"%s\"", l); } +TEST(default_hostname) { + if (!hostname_is_valid(FALLBACK_HOSTNAME, 0)) { + log_error("Configured fallback hostname \"%s\" is not valid.", FALLBACK_HOSTNAME); + exit(EXIT_FAILURE); + } + + _cleanup_free_ char *n = get_default_hostname(); + ASSERT_NOT_NULL(n); + log_info("get_default_hostname: \"%s\"", n); + ASSERT_TRUE(hostname_is_valid(n, /* flags= */ 0)); + + _cleanup_free_ char *m = get_default_hostname_raw(); + ASSERT_NOT_NULL(m); + log_info("get_default_hostname_raw: \"%s\"", m); + ASSERT_TRUE(hostname_is_valid(m, VALID_HOSTNAME_QUESTION_MARK)); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index 598ab96d918..adfc6b2d6af 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -44,6 +44,9 @@ TEST(hostname_is_valid) { assert_se(!hostname_is_valid("foo..bar", VALID_HOSTNAME_TRAILING_DOT)); assert_se(!hostname_is_valid("foo.bar..", VALID_HOSTNAME_TRAILING_DOT)); assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", VALID_HOSTNAME_TRAILING_DOT)); + + ASSERT_FALSE(hostname_is_valid("foo??bar", 0)); + ASSERT_TRUE(hostname_is_valid("foo??bar", VALID_HOSTNAME_QUESTION_MARK)); } TEST(hostname_cleanup) { @@ -91,16 +94,4 @@ TEST(hostname_cleanup) { ASSERT_STREQ(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } -TEST(default_hostname) { - if (!hostname_is_valid(FALLBACK_HOSTNAME, 0)) { - log_error("Configured fallback hostname \"%s\" is not valid.", FALLBACK_HOSTNAME); - exit(EXIT_FAILURE); - } - - _cleanup_free_ char *n = get_default_hostname(); - assert_se(n); - log_info("get_default_hostname: \"%s\"", n); - assert_se(hostname_is_valid(n, 0)); -} - DEFINE_TEST_MAIN(LOG_DEBUG);