The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=fbe0257b5613f457af42f82efb2e3bc9395d0557

commit fbe0257b5613f457af42f82efb2e3bc9395d0557
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2026-07-06 12:23:37 +0000
Commit:     Dag-Erling Smørgrav <[email protected]>
CommitDate: 2026-07-06 12:23:37 +0000

    libc/resolv: Reimplement the sortlist parser
    
    When we switched from the BIND4 resolver to the BIND9 resolver, the
    sortlist parser was inadvertently disabled due to a missing #define, and
    nobody seemed to notice.  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 manual
    accordingly.  The new parser accepts IPv4 and IPv6 addresses with or
    without a mask or prefix length, just like the old one, except IPv6
    support was a bit wonky in the original code.
    
    Fixes:          5342d17f09a8 ("Update the resolver in libc to BIND9's one.")
    Relnotes:       yes
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D57925
---
 lib/libc/resolv/Makefile.inc |   1 +
 lib/libc/resolv/res_init.c   | 246 ++++++++++++++++++++++++-------------------
 share/man/man5/resolver.5    |  36 ++++---
 3 files changed, 160 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 bd3fd866c189..120b30fc74f5 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 <resolv.h>
 
+#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 */
@@ -631,6 +556,122 @@ res_setnsaddr(res_state statp, int i, const char *str, 
const char *end)
        return (ret);
 }
 
+#ifdef RESOLVSORT
+/*%
+ * Parse a sortlist entry and insert it into the specified slot in the
+ * sortlist.  Assumes no leading or trailing space.  Returns 1 if an entry
+ * was inserted, 0 otherwise.
+ */
+static 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 (strchr(mask, '.') &&
+                   inet_pton(AF_INET, mask, &a) == 1) {
+                       statp->sort_list[i].mask = a.s_addr;
+               } else if (*mask != '\0') {
+                       if ((num = strtol(mask, &numend, 10)) < 0 ||
+                           num > 32 || *numend != '\0')
+                               return (0);
+                       statp->sort_list[i].mask =
+                           num == 0 ? 0 :
+                           htonl(0xffffffffU << (32 - num));
+               } else if (IN_CLASSA(ntohl(a.s_addr))) {
+                       statp->sort_list[i].mask =
+                           htonl(IN_CLASSA_NET);
+               } else if (IN_CLASSB(ntohl(a.s_addr))) {
+                       statp->sort_list[i].mask =
+                           htonl(IN_CLASSB_NET);
+               } else if (IN_CLASSC(ntohl(a.s_addr))) {
+                       statp->sort_list[i].mask =
+                           htonl(IN_CLASSC_NET);
+               } else {
+                       statp->sort_list[i].mask =
+                           htonl(IN_NETMASK_DEFAULT);
+               }
+               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, &statp->sort_list[i].addr,
+                   addr, sizeof(addr));
+               a.s_addr = statp->sort_list[i].mask;
+               inet_ntop(AF_INET, &a, mask, sizeof(mask));
+               printf(";; sortlist %s/%s\n", addr, mask);
+#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.
  */
@@ -775,21 +816,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 c9e7526ab865..749ffba21fbd 100644
--- a/share/man/man5/resolver.5
+++ b/share/man/man5/resolver.5
@@ -100,20 +100,30 @@ 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 mask
+or
+.Ar addr Ns Li / Ns Ar plen
+entries, for instance:
+.Bd -literal
+sortlist 198.51.100.0/24 203.0.113.0/255.255.255.0 2001:db8::/32
+.Ed
+.Pp
+Masks are only supported for IPv4.
+If the mask or 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

Reply via email to