diff --git a/man/hostname.xml b/man/hostname.xml
index 746de21cd1..76acb8d7a5 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 70a6d295f6..07cb0bc005 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 548eb47a4c..54978edd43 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 165b1e2e16..7159473b58 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 4449c1eb39..4c5abe760f 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 0368ec891f..ee4b2d6baf 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 9ef8d89560..cd72f54396 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 4895631b67..6ca0dc6fa5 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 bf03f177d3..144e3550a3 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 110f0a7400..fb80e1280a 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 e99a0d6cae..f9a1b2ffaa 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 ef3a456ea7..91f9d0c9cc 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 1904885189..af89f92bdc 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 3244c6c368..bc602acbfc 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 67da2f03f1..da6e379699 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 598ab96d91..adfc6b2d6a 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);