On Thu, Oct 29, 2026 at 11:58:14PM -0400, Laine Stump wrote:
On 10/26/26 8:18 AM, Daniel P. Berrangé via Devel wrote:
From: Daniel P. Berrangé <berrange@redhat.com>
Hostnames must comply with certain rules about maximum length and permitted characters. Since these need to be validated in several places, introduce a helper to centralize the rules and provide testing.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virutil.c | 50 ++++++++++++++++++++++++++++ src/util/virutil.h | 2 ++ tests/utiltest.c | 72 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 798d2d28c8..66520a3e35 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3791,6 +3791,7 @@ virGetUserRuntimeDirectory; virGetUserShell; virHostGetDRMRenderNode; virHostHasIOMMU; +virHostnameValidate; virIndexToDiskName; virKernelCmdlineMatchParam; virKernelCmdlineNextParam; diff --git a/src/util/virutil.c b/src/util/virutil.c index 2be9a460ac..75ecb6da5a 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -1979,3 +1979,53 @@ virPipeNonBlock(int fds[2]) { return virPipeImpl(fds, true, true); } + +#define VIR_HOSTNAME_VALID_CHARS \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789-" + +int virHostnameValidate(const char *hostname, bool qualified) +{ + g_auto(GStrv) labels = g_strsplit(hostname, ".", -1); + size_t i; + + if (!qualified && g_strv_length(labels) > 1) { + virReportError(VIR_ERR_INVALID_ARG, + _("Hostname '%1$s' must not be qualified"), + hostname); + return -1; + } + if (strlen(hostname) > 253) { + virReportError(VIR_ERR_INVALID_ARG, + _("Hostname '%1$s' must be 253 characters or less"), + hostname); + return -1; + } + + for (i = 0; labels[i] != NULL; i++) { + size_t labellen = strlen(labels[i]); + if (labellen == 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Hostname '%1$s' components must be non-empty"), + hostname); + return -1; + } + + if (labellen > 63) { + virReportError(VIR_ERR_INVALID_ARG, + _("Hostname '%1$s' components must be 63 characters or less"), + hostname); + return -1; + } + + if (strspn(labels[i], + VIR_HOSTNAME_VALID_CHARS) != labellen) { + virReportError(VIR_ERR_INVALID_ARG, + _("Hostname '%1$s' components must only contain a-z, A-Z, 0-9 and -"), + hostname); + return -1; + }
1) Additionally, although a hostname is now allowed to start with a number (originally it wasn't), it cannot start or end with a "-" (more specifically, each individual label cannot start or end with a "-".
Ok, we need the '-' validation. Also for TXT/SRV records I need to allow a leading '_'
2) In general, a domainname's octets can't be interpreted or limited by a DNS server; that is declared to be the responsibility of the application that is actually using the information. (e.g. a DNS A or AAAA record key can completely legitimately contain a "_", or any other character and the DNS server is supposed to accept that and forward it on or whatever). It's only when an application decides to use a domainname.
And actually I've been reading RFC2181 for the first time and section 11 says "any binary string whatever can be used as the label of any resource record" (after first listing the length limitations you've checked for above). It also says "DNS server must not refuse to serve a zone because it contains labels that might not be acceptable to some DNS client programs" (oddly the "must" isn't capitalized, which has always had a special meaning in RFCs (at least to me). (https://www.rfc-editor.org/info/rfc2181/#section-11). So technically we *could* allow anything in a hostname and rely on the receiving client to validate. Not that I'm suggesting that, I just found it interesting that was the case :-)
3) I was curious about what happens with hostnames/domain names containing international characters. I've known for several years that it's now supported, but never looked at the details before. This is my chance!
So, on the wire, only the characters you've specified are supported (in a hostname), and any character beyond 0x80 will cause the name to be translated into "Punycode"[*], which I won't try to describe fully, but it ends up with a string that starts with "xn--" and then does a special encoding of the source string that I'll let you look up for yourself.
We *could* just say that libvirt only allows using the Punycode-encoded version of international hostnames in its config files (and thus your validation would remain the same, but with addition of checking leading and trailing "-"), but dnsmasq actually has support for international characters in hostnames and domainnames in its config files, and will automatically translate any hostname/domainname with non-ascii characters to Punycode before putting it on the wire. And certainly it would be much easier to recognize:
mötleycrüe.com
in the config file rather than
xn-mtleycre-n4a5pc.com
:-P. (and possibly there are even already people doing this in their libvirt config files, so adding the validation as you've written it could break someone's setup.)
Yes, you're right, we must not restrict what is already permitted by dnsmasq. What matters is that we don't break the config file format ultimately. I guess I need to throw most of this away and just allow anything that is not whitespace, or control characters. With regards, Daniel -- |: https://berrange.com ~~ https://hachyderm.io/@berrange :| |: https://libvirt.org ~~ https://entangle-photo.org :| |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|