From nobody Mon Jun 29 15:52:08 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gprPj1H9rz6jNfg for ; Mon, 29 Jun 2026 15:52:09 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gprPh6qy1z3KqC for ; Mon, 29 Jun 2026 15:52:08 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782748329; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6aLn2kAKKr2RMIJJgQ+/7UOS3/X0OU8UyYx3Uo2lBBA=; b=kgxvzo5iwmZYTLhaXlu73ucYEb8sIOCUpc/fR0ZUVcNlaPBPAfPZ4rI7jlHCQ5HtyuCUMX j4S43HvXPOFzlTTZADLt1kmchmoFXx+bFrMEPpg5jBd619T3/UofI4d90ZH/xWwMZ5VwDi lcoA4BgX4aJWVwE4a5NIGPHSWR0XPJSVlAIv1d3R/9C5LThyacgkspeW1hJ4y4p0MJDuPx K0e7moT+qYOjeIAPE02wi3bE3X8RCtIZMUcGMwLg2WrN4p7v7pBmFH5c/qN1AzB38acamp U/R7BbdeT7wBQo5IO8RYvSfBFW7uCv9UcFH69qcrM8FFHp33dudueHnL8yJTOw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782748329; a=rsa-sha256; cv=none; b=YvT9GRzTpS8bB+b0flO8RWZ4JamId8DAp4R/GVd9ylCx4xBOcqNxd1jYdMvp2YZon9Ziut 2wzEVNRES5hs25a4M0t5tFx1FdOrQGM7j/E22Epyov+nULfntOINqQn8HmeA3TcBB9snDz Xug1POuKplbQUBBSEBi4rQg0APOKdWHERRsSB7pbZDLqRCs3pI6iG4GynXgwWgD1qJAza4 2ErxQwfChwCdqMrRwm5PxF3kJFPx4hIbaTYHZ3XimAyXLRYJGNttf7AraUDyq/N1NdI/Gk +Y0cOsdl9Dm2B16G1gNgnobdduEtXFX/D20swLS9AtvnSzQ7jiOqsXE/OLGfcA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782748329; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6aLn2kAKKr2RMIJJgQ+/7UOS3/X0OU8UyYx3Uo2lBBA=; b=fz0VkHxNHxVki+xlVCDa1ibwvpC/Ckkrag9qGQI8BljG5w2T/ocUHeD5FXt/MMQs8uBMh2 vExvIsvNwJ+j6otap8RYMt9TqdleSXAMyvpsq5a5lB7Itc+GfllJtwj+0avUhngdnXJe/J G7JMpL7TOe+nbmNdfnNKIqrXFDJtUFMVcglggGtiQDHknuVSIM2aIxDLi/z0MnyxWTA6cq J6lkv1jHL2B8LdPXUN17yL7JtRPj3OhOosIt64mBTr9jI3fFZ5sZoQbmDAhZLIpL+X+/li OyJryh4LuH3jcfUmrtbEHqn501iqDWTQA/jzPJwyXcLlPQM2dIJiY86bB/uLnA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gprPh5LKcz1441 for ; Mon, 29 Jun 2026 15:52:08 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3a8cd by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 29 Jun 2026 15:52:08 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: 507ab02d751b - main - libc/resolv: Reimplement the sortlist parser List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 507ab02d751b4aaa7f35f30882299e9908448e53 Auto-Submitted: auto-generated Date: Mon, 29 Jun 2026 15:52:08 +0000 Message-Id: <6a4294a8.3a8cd.11224f2b@gitrepo.freebsd.org> The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=507ab02d751b4aaa7f35f30882299e9908448e53 commit 507ab02d751b4aaa7f35f30882299e9908448e53 Author: Dag-Erling Smørgrav AuthorDate: 2026-06-27 18:31:36 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-06-29 13:49:18 +0000 libc/resolv: Reimplement the sortlist parser When we switched from the BIND4 resolver to the BIND9 resolver, the sortlist parser was inadvertently disabled, and nobody noticed. The sorting code remained enabled in the resolver, but there was no way to set a sort order. Reimplement the sortlist parser, but correctly, and update the documentation accordingly. The new parser accepts IPv4 and IPv6 addresses with or without a prefix length. Fixes: 5342d17f09a8 ("Update the resolver in libc to BIND9's one.") Relnotes: yes --- lib/libc/resolv/Makefile.inc | 1 + lib/libc/resolv/res_init.c | 232 +++++++++++++++++++++++-------------------- share/man/man5/resolver.5 | 33 +++--- 3 files changed, 143 insertions(+), 123 deletions(-) diff --git a/lib/libc/resolv/Makefile.inc b/lib/libc/resolv/Makefile.inc index be900e942435..ecb0c2462b08 100644 --- a/lib/libc/resolv/Makefile.inc +++ b/lib/libc/resolv/Makefile.inc @@ -7,4 +7,5 @@ SRCS+= herror.c h_errno.c mtctxres.c res_comp.c res_data.c res_debug.c \ SYM_MAPS+= ${LIBC_SRCTOP}/resolv/Symbol.map +CFLAGS+=-I${LIBC_SRCTOP}/net CFLAGS+=-I${SRCTOP}/lib/libmd diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index c61b247b5199..a0cd381d0f0f 100644 --- a/lib/libc/resolv/res_init.c +++ b/lib/libc/resolv/res_init.c @@ -95,6 +95,7 @@ /* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */ #include +#include "res_config.h" #include "res_private.h" static void res_readconf(res_state, const char *, int); @@ -104,10 +105,8 @@ static int res_setnsaddr(res_state, int, const char *, const char *); static void res_setoptions(res_state, const char *, const char *); #ifdef RESOLVSORT -static const char sort_mask[] = "/&"; -#define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL) -static u_int32_t net_mask(struct in_addr); -#endif +static int res_setsortlist(res_state, int, const char *, const char *); +#endif /* RESOLVSORT */ #define res_space(ch) \ isspace((unsigned char)(ch)) @@ -285,8 +284,7 @@ res_readconf(res_state statp, const char *resconf, int maxns) size_t sz = 0; int nserv = 0; #ifdef RESOLVSORT - int n, nsort = 0; - char *net; + int nsort = 0; #endif if ((fp = fopen(resconf, "re")) == NULL) @@ -357,101 +355,28 @@ res_readconf(res_state statp, const char *resconf, int maxns) continue; } #ifdef RESOLVSORT + /* read result sort order */ if (res_match(line, eow, "sortlist")) { - struct in_addr a; - struct in6_addr a6; - int m, i; - u_char *u; - struct __res_state_ext *ext = statp->_u._ext.ext; - - cp = line + sizeof("sortlist") - 1; while (nsort < MAXRESOLVSORT) { - while (*cp == ' ' || *cp == '\t') + cp = eow; + while (cp < eol && res_space(*cp)) cp++; - if (*cp == '\0' || *cp == '\n' || *cp == ';') + if (cp == eol) break; - net = cp; - while (*cp && !ISSORTMASK(*cp) && *cp != ';' && - isascii(*cp) && !isspace((unsigned char)*cp)) - cp++; - n = *cp; - *cp = 0; - if (inet_aton(net, &a)) { - statp->sort_list[nsort].addr = a; - if (ISSORTMASK(n)) { - *cp++ = n; - net = cp; - while (*cp && *cp != ';' && - isascii(*cp) && - !isspace((unsigned char)*cp)) - cp++; - n = *cp; - *cp = 0; - if (inet_aton(net, &a)) { - statp->sort_list[nsort].mask = a.s_addr; - } else { - statp->sort_list[nsort].mask = - net_mask(statp->sort_list[nsort].addr); - } - } else { - statp->sort_list[nsort].mask = - net_mask(statp->sort_list[nsort].addr); - } - ext->sort_list[nsort].af = AF_INET; - ext->sort_list[nsort].addr.ina = - statp->sort_list[nsort].addr; - ext->sort_list[nsort].mask.ina.s_addr = - statp->sort_list[nsort].mask; - nsort++; - } - else if (inet_pton(AF_INET6, net, &a6) == 1) { - - ext->sort_list[nsort].af = AF_INET6; - ext->sort_list[nsort].addr.in6a = a6; - u = (u_char *)&ext->sort_list[nsort].mask.in6a; - *cp++ = n; - net = cp; - while (*cp && *cp != ';' && - isascii(*cp) && !isspace(*cp)) - cp++; - m = n; - n = *cp; - *cp = 0; - switch (m) { - case '/': - m = atoi(net); - break; - case '&': - if (inet_pton(AF_INET6, net, u) == 1) { - m = -1; - break; - } - /*FALLTHROUGH*/ - default: - m = sizeof(struct in6_addr) * CHAR_BIT; - break; - } - if (m >= 0) { - for (i = 0; i < sizeof(struct in6_addr); i++) { - if (m <= 0) { - *u = 0; - } else { - m -= CHAR_BIT; - *u = (u_char)~0; - if (m < 0) - *u <<= -m; - } - u++; - } - } - statp->sort_list[nsort].addr.s_addr = - (u_int32_t)0xffffffff; - statp->sort_list[nsort].mask = - (u_int32_t)0xffffffff; + eow = cp; + while (eow < eol && !res_space(*eow)) + eow++; + if (res_setsortlist(statp, nsort, cp, eow)) nsort++; - } - *cp = n; } +#ifdef DEBUG + while (cp < eol && res_space(*cp)) + cp++; + if (cp < eol) { + printf(";; sortlist overflow: %.*s\n", + (int)(eol - cp), cp); + } +#endif continue; } #endif /* RESOLVSORT */ @@ -651,6 +576,108 @@ res_setnsaddr(res_state statp, int i, const char *str, const char *end) return (ret); } +#ifdef RESOLVSORT +/*% + * Set the sort order. Assumes no leading or trailing space. + */ +int +res_setsortlist(res_state statp, int i, const char *str, const char *end) +{ + struct __res_state_ext *ext = statp->_u._ext.ext; + char addr[48], mask[48], *dst, *numend; + _Static_assert(sizeof(addr) > INET6_ADDRSTRLEN, + "address buffer too small for AF_INET6"); + _Static_assert(sizeof(addr) > INET_ADDRSTRLEN, + "address buffer too small for AF_INET"); + _Static_assert(sizeof(mask) > INET6_ADDRSTRLEN, + "mask buffer too small for AF_INET6"); + _Static_assert(sizeof(mask) > INET_ADDRSTRLEN, + "mask buffer too small for AF_INET"); + struct in_addr a; + struct in6_addr a6; + uint8_t *u; + long num; + + /* copy the address */ + dst = addr; + while (str < end && !res_space(*str) && *str != '/') { + *dst++ = *str++; + if (dst == addr + sizeof(addr)) + return (0); + } + *dst = '\0'; + + /* copy the mask, if there is one */ + dst = mask; + if (str < end) { + if (*str++ != '/') + return (0); + if (end - str >= sizeof(mask)) + return (0); + memcpy(mask, str, end - str); + dst += end - str; + } + *dst = '\0'; + + /* IPv4 case */ + if (inet_pton(AF_INET, addr, &a) == 1) { + statp->sort_list[i].addr = a; + if (mask[0] != '\0') { + if ((num = strtol(mask, &numend, 10)) < 0 || + num > 32 || *numend != '\0') + return (0); + } else { + if (IN_CLASSA(ntohl(a.s_addr))) + num = 8; + else if (IN_CLASSB(ntohl(a.s_addr))) + num = 16; + else + num = 24; + } + statp->sort_list[i].mask = + htonl(0xffffffffU << (32 - num)); + ext->sort_list[i].af = AF_INET; + ext->sort_list[i].addr.ina = + statp->sort_list[i].addr; + ext->sort_list[i].mask.ina.s_addr = + statp->sort_list[i].mask; +#ifdef DEBUG + inet_ntop(AF_INET, &a, addr, sizeof(addr)); + printf(";; sortlist %s/%ld\n", addr, num); +#endif + return (1); + } + + /* IPv6 case */ + if (inet_pton(AF_INET6, addr, &a6) == 1) { + statp->sort_list[i].addr.s_addr = INADDR_NONE; + statp->sort_list[i].mask = INADDR_NONE; + ext->sort_list[i].af = AF_INET6; + ext->sort_list[i].addr.in6a = a6; + if (mask[0]) { + /* prefix length */ + if ((num = strtol(mask, &numend, 10)) < 0 || + num > 128 || *numend != '\0') + return (0); + } else { + num = 64; + } +#ifdef DEBUG + inet_ntop(AF_INET6, &a6, addr, sizeof(addr)); + printf(";; sortlist %s/%ld\n", addr, num); +#endif + a6 = (struct in6_addr)IN6ADDR_ANY_INIT; + for (u = a6.s6_addr8; num > 0; num -= 8) + *u++ = num > 8 ? 0xffU : 0xffU << (8 - num); + ext->sort_list[i].mask.in6a = a6; + return (1); + } + + /* invalid */ + return (0); +} +#endif /* RESOLVSORT */ + /*% * Split the string up into words and interpret them as resolver options. */ @@ -790,21 +817,6 @@ res_setoptions(res_state statp, const char *options, const char *source) } } -#ifdef RESOLVSORT -/* XXX - should really support CIDR which means explicit masks always. */ -static u_int32_t -net_mask(struct in_addr in) /*!< XXX - should really use system's version of this */ -{ - u_int32_t i = ntohl(in.s_addr); - - if (IN_CLASSA(i)) - return (htonl(IN_CLASSA_NET)); - else if (IN_CLASSB(i)) - return (htonl(IN_CLASSB_NET)); - return (htonl(IN_CLASSC_NET)); -} -#endif - void freebsd15_res_rndinit(res_state statp) { diff --git a/share/man/man5/resolver.5 b/share/man/man5/resolver.5 index 8af69fd2dac7..135e56287b5f 100644 --- a/share/man/man5/resolver.5 +++ b/share/man/man5/resolver.5 @@ -100,20 +100,27 @@ for one of the domains. .Pp The search list is currently limited to six domains with a total of 256 characters. -.It Sy sortlist -Sortlist allows addresses returned by the resolver to be sorted. -A sortlist is specified by IP address netmask pairs. -If the netmask is not specified, -it defaults to the historical Class A/B/C netmask of the net; -this usage is deprecated. -The IP address -and network pairs are separated by slashes. -Up to 10 pairs may -be specified. -E.g., +.It Cm sortlist Ar addr1 Ns Oo Li / Ns Ar plen1 Oc Op addr2 Ns Oo Li / Ns Ar plen2 Oc ... +Sort order for query results in the form of a list of +.Ar addr Ns Li / Ns Ar plen +entries, for instance: +.Bd -literal +sortlist 198.51.100.0/24 203.0.113.0/24 2001:db8::/32 +.Ed +.Pp +If the prefix length is omitted, it defaults to historical class-based +addressing for IPv4 and to 64 for IPv6. +.Pp +Query results that match one of the sort list entries are sorted +accordingly and ahead of query results that don't. +If multiple results match a single sort list entry, the order is +unspecified. .Pp -.Dl "sortlist 10.9.1.0/255.255.240.0 10.9.0.0/255.255.0.0" -.It Cm options Ar option1 Op Ar option2 ... +If multiple +.Cm sortlist +lines are encountered, entries are appended to the list in the order +in which they were encountered, up to a maximum of ten. +.It Cm options Options allows certain internal resolver variables to be modified. The following options are supported: .Bl -tag -width indent