From nobody Tue Jul 21 08:56:52 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 4h4B8P55Rcz6lcKb for ; Tue, 21 Jul 2026 08:56:53 +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 4h4B8P0xFmz3dG0 for ; Tue, 21 Jul 2026 08:56:53 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784624213; 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=lqtpl6kA4tOEyjAOvTI53kwYt5ZkUcr3rikx50B/NNg=; b=k3NUea3gVUsUFWp1nl8Uys9uPKvZfoa9z5ZLyI8Ef46GbP1tH5K1MsNebf9rJrfT0h3tcS zsldLOov2Vgmj6WwR8iynCi8IFo7m04vGL9ID5xsgMVqvXlqFodImDkML8Ig9ufisuCVlz M3KgTvlzjQyNdLS+G4wrNVkCQDJEANGnuPqnX/ScGCSMQsoXhg89l4qTgDRqshF+sjp+wj 0VG8jtTi7dLXzxXiiQCQWZsJuITf1t40ocLtq8uLEpZylz44juxPPyYjnPc5etD4YA6Xv7 Fzk9+5K2Y2VJ9MDFlfivshe006SBMc+3qrj548PVc7A4klgKLc5GFHc10aeMPg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1784624213; a=rsa-sha256; cv=none; b=j7COIt31KhH3w/crm800Fs3PtCjmQ2wDVOptodqpUaAXAnsjmXCLVxEi5Q3m0V3s2iGx/R tw5aAhoUQeHBYL/TL3+CH4gtnhHaZK3WzfmWTvqob0hL3CgrQwU0sjbVqyCKBgHLxBmBn0 f+wxbkCuaeW3pZq40kEHbs7xDq5Xf+x4TS6mwhGH9YE+QNiPeZJQxQGoiC/XI6BHOkSVvN gks8ezWJ7ocIjkpNc0f3eOCwTEeBmXihPEEkp0jDXnDdqMEkFl6LqR2CpubkHrCeZdtSLm dqTXQoIw48H9tib3Uni/+NjeDJS/kDyOf7t1nC42H927/AVmc/okCtJlx3F2Tw== 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=1784624213; 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=lqtpl6kA4tOEyjAOvTI53kwYt5ZkUcr3rikx50B/NNg=; b=Uij2Cr1xW1N2F1vdHcy+0b/wS6dePktWP2JllHN8d8pmgvuVmOfD3mWRWej2vakowHCEYR NZi/jUJv7tLFB9WldOuyQnKMPvu2Ip49Rqdcl3Ojm2Tkge53DWpKUMnwczaF+9yUqqyXZs buDZhS1KGCYJXABJSdMf439NgRJi0tv+VRW4ugZXnWScJmnPwdZXZTagfwvCGZkg8l3o7I Z4rdL4rcHKl26TVjZNlzulsEPc5CLMjtRe0jM5EzeduwJoa8tbfcdGDA5x8wLiS5/S3aji nDIfnfLxKzwFByXDd0E5gmF2scskgyQkEBnl4l5leoaO6GE2p88inb57nXTDFg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4h4B8N6cSjzjBf for ; Tue, 21 Jul 2026 08:56:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 2162e by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 21 Jul 2026 08:56:52 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: b52d95bf1494 - stable/14 - libc/resolv: Refactor the option 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/stable/14 X-Git-Reftype: branch X-Git-Commit: b52d95bf14945108be077fd621c194d24c1f3118 Auto-Submitted: auto-generated Date: Tue, 21 Jul 2026 08:56:52 +0000 Message-Id: <6a5f3454.2162e.70eaa5ba@gitrepo.freebsd.org> The branch stable/14 has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=b52d95bf14945108be077fd621c194d24c1f3118 commit b52d95bf14945108be077fd621c194d24c1f3118 Author: Dag-Erling Smørgrav AuthorDate: 2026-07-06 12:23:29 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-07-21 08:54:59 +0000 libc/resolv: Refactor the option parser Start the loop by finding the end of the option name, the name-value separator (if any), and the end of the option. Use those pointers to simplify matching the option name and parsing the option value, and validate option names and values more strictly. This means that: * We no longer accept trailing garbage in an option name or value. For instance, we would previously interpret “edns0123” as “edns0” and “timeout:3xyz” as “timeout:3”. This was actually quite lucky because we also failed to recognize the newline at the end of the option line as a whitespace character. * For options that take a numerical argument, we would previously accept negative values and treat non-numerical arguments as 0, while large numerical arguments would be capped to the option's maximum permitted value. Now, any failure to parse the argument, including overflow, results in the option being left unchanged. MFC after: 1 week Relnotes: yes Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D57923 (cherry picked from commit 9bfdfecd27359130aa4ef63fc0aa32f98f9e7b50) --- lib/libc/resolv/res_init.c | 194 ++++++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 89 deletions(-) diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index a769d7b19395..69333730c694 100644 --- a/lib/libc/resolv/res_init.c +++ b/lib/libc/resolv/res_init.c @@ -3,6 +3,7 @@ * * Copyright (c) 1985, 1989, 1993 * The Regents of the University of California. All rights reserved. + * Copyright (c) 2026 Dag-Erling Smørgrav * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -84,6 +85,7 @@ static const char rcsid[] = "$Id: res_init.c,v 1.26 2008/12/11 09:59:00 marka Ex #include #include +#include #include #include #include @@ -120,9 +122,11 @@ static const char sort_mask[] = "/&"; static u_int32_t net_mask(struct in_addr); #endif -#if !defined(isascii) /*%< XXX - could be a function */ -# define isascii(c) (!(c & 0200)) -#endif +#define res_space(ch) \ + isspace((unsigned char)(ch)) +#define res_match(str, end, word) \ + (strncmp((str), (word), (end) - (str)) == 0 && \ + (word)[(end) - (str)] == '\0') /* * Resolver state default settings. @@ -539,129 +543,141 @@ __res_vinit(res_state statp, int preinit) { static void res_setoptions(res_state statp, const char *options, const char *source) { - const char *cp = options; - int i; + const char *cp = options, *sp, *ep; + char *numend; + long num; struct __res_state_ext *ext = statp->_u._ext.ext; #ifdef DEBUG - if (statp->options & RES_DEBUG) + if (statp->options & RES_DEBUG) { printf(";; res_setoptions(\"%s\", \"%s\")...\n", options, source); + } #endif - while (*cp) { + for (;;) { /* skip leading and inner runs of spaces */ - while (*cp == ' ' || *cp == '\t') + while (res_space(*cp)) cp++; + /* find the separator if there is one */ + sp = cp; + while (*sp != '\0' && !res_space(*sp) && *sp != ':') + sp++; + /* find the end of the option */ + ep = sp; + while (*ep != '\0' && !res_space(*ep)) + ep++; + /* end of option line */ + if (ep == cp) + break; /* search for and process individual options */ - if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) { - i = atoi(cp + sizeof("ndots:") - 1); - if (i <= RES_MAXNDOTS) - statp->ndots = i; - else - statp->ndots = RES_MAXNDOTS; + /* note: sp < ep if implies *sp == ':' */ + if (res_match(cp, sp, "ndots") && sp < ep) { + num = strtol(sp + 1, &numend, 10); + if (numend == ep && num >= 0 && num <= RES_MAXNDOTS) { + statp->ndots = num; #ifdef DEBUG - if (statp->options & RES_DEBUG) - printf(";;\tndots=%d\n", statp->ndots); + if (statp->options & RES_DEBUG) + printf(";;\tndots=%d\n", statp->ndots); #endif - } else if (!strncmp(cp, "timeout:", sizeof("timeout:") - 1)) { - i = atoi(cp + sizeof("timeout:") - 1); - if (i <= RES_MAXRETRANS) - statp->retrans = i; - else - statp->retrans = RES_MAXRETRANS; + } + } else if (res_match(cp, sp, "timeout") && sp < ep) { + num = strtol(sp + 1, &numend, 10); + if (numend == ep && num > 0 && num <= RES_MAXRETRANS) { + statp->retrans = num; #ifdef DEBUG - if (statp->options & RES_DEBUG) - printf(";;\ttimeout=%d\n", statp->retrans); + if (statp->options & RES_DEBUG) + printf(";;\ttimeout=%d\n", + statp->retrans); #endif - } else if (!strncmp(cp, "attempts:", sizeof("attempts:") - 1)){ - i = atoi(cp + sizeof("attempts:") - 1); - if (i <= RES_MAXRETRY) - statp->retry = i; - else - statp->retry = RES_MAXRETRY; + } + } else if (res_match(cp, sp, "attempts") && sp < ep) { + num = strtol(sp + 1, &numend, 10); + if (numend == ep && num > 0 && num <= RES_MAXRETRY) { + statp->retry = num; #ifdef DEBUG - if (statp->options & RES_DEBUG) - printf(";;\tattempts=%d\n", statp->retry); + if (statp->options & RES_DEBUG) + printf(";;\tattempts=%d\n", + statp->retry); #endif - } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) { + } + } else if (res_match(cp, ep, "debug")) { #ifdef DEBUG if (!(statp->options & RES_DEBUG)) { printf(";; res_setoptions(\"%s\", \"%s\")..\n", options, source); - statp->options |= RES_DEBUG; } +#endif + statp->options |= RES_DEBUG; +#ifdef DEBUG printf(";;\tdebug\n"); #endif - } else if (!strncmp(cp, "no_tld_query", - sizeof("no_tld_query") - 1) || - !strncmp(cp, "no-tld-query", - sizeof("no-tld-query") - 1)) { + } else if (res_match(cp, ep, "no-tld-query") || + res_match(cp, ep, "no_tld_query")) { statp->options |= RES_NOTLDQUERY; - } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) { + } else if (res_match(cp, ep, "inet6")) { statp->options |= RES_USE_INET6; - } else if (!strncmp(cp, "insecure1", sizeof("insecure1") - 1)) { - statp->options |= RES_INSECURE1; - } else if (!strncmp(cp, "insecure2", sizeof("insecure2") - 1)) { - statp->options |= RES_INSECURE2; - } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) { + } else if (res_match(cp, ep, "insecure1")) { + statp->options |= RES_INSECURE1; + } else if (res_match(cp, ep, "insecure2")) { + statp->options |= RES_INSECURE2; + } else if (res_match(cp, ep, "blast")) { + statp->options |= RES_BLAST; + } else if (res_match(cp, ep, "rotate")) { statp->options |= RES_ROTATE; - } else if (!strncmp(cp, "usevc", sizeof("usevc") - 1)) { + } else if (res_match(cp, ep, "usevc")) { statp->options |= RES_USEVC; - } else if (!strncmp(cp, "no-check-names", - sizeof("no-check-names") - 1)) { + } else if (res_match(cp, ep, "no-check-names")) { statp->options |= RES_NOCHECKNAME; - } else if (!strncmp(cp, "reload-period:", - sizeof("reload-period:") - 1)) { - if (ext != NULL) { - ext->reload_period = (u_short) - atoi(cp + sizeof("reload-period:") - 1); + } else if (res_match(cp, sp, "reload-period") && sp < ep) { + num = strtol(sp + 1, &numend, 10); + if (numend == ep && num > 0 && num <= USHRT_MAX) { + if (ext != NULL) + ext->reload_period = (u_short)num; +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";;\treload-period %hu\n", + (u_short)num); +#endif } - } #ifdef RES_USE_EDNS0 - else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) { + } else if (res_match(cp, ep, "edns0")) { statp->options |= RES_USE_EDNS0; - } #endif #ifndef _LIBC - else if (!strncmp(cp, "dname", sizeof("dname") - 1)) { + } else if (res_match(cp, ep, "dname")) { statp->options |= RES_USE_DNAME; - } - else if (!strncmp(cp, "nibble:", sizeof("nibble:") - 1)) { - if (ext == NULL) - goto skip; - cp += sizeof("nibble:") - 1; - i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix) - 1); - strncpy(ext->nsuffix, cp, i); - ext->nsuffix[i] = '\0'; - } - else if (!strncmp(cp, "nibble2:", sizeof("nibble2:") - 1)) { - if (ext == NULL) - goto skip; - cp += sizeof("nibble2:") - 1; - i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix2) - 1); - strncpy(ext->nsuffix2, cp, i); - ext->nsuffix2[i] = '\0'; - } - else if (!strncmp(cp, "v6revmode:", sizeof("v6revmode:") - 1)) { - cp += sizeof("v6revmode:") - 1; + } else if (res_match(cp, sp, "nibble") && sp < ep) { + sp++; + if (ext != NULL && ep - sp < sizeof(ext->nsuffix)) { + memcpy(ext->nsuffix, sp, ep - sp); + ext->nsuffix[ep - sp] = '\0'; + } + } else if (res_match(cp, sp, "nibble2") && sp < ep) { + sp++; + if (ext != NULL && ep - sp < sizeof(ext->nsuffix2)) { + memcpy(ext->nsuffix2, sp, ep - sp); + ext->nsuffix2[ep - sp] = '\0'; + } + } else if (res_match(cp, sp, "v6revmode") && sp < ep) { /* "nibble" and "bitstring" used to be valid */ - if (!strncmp(cp, "single", sizeof("single") - 1)) { + if (res_match(sp + 1, ep, "single")) statp->options |= RES_NO_NIBBLE2; - } else if (!strncmp(cp, "both", sizeof("both") - 1)) { - statp->options &= - ~RES_NO_NIBBLE2; - } - } + else if (res_match(sp + 1, ep, "both")) + statp->options &= ~RES_NO_NIBBLE2; +#ifdef DEBUG + else + printf(";;\t%.*s: unrecognized value: %.*s\n", + (int)(sp - cp), cp, + (int)(ep - sp - 1), sp + 1); #endif - else { - /* XXX - print a warning here? */ - } -#ifndef _LIBC - skip: +#endif /* !_LIBC */ + } else { +#ifdef DEBUG + printf(";;\tunrecognized option: %.*s\n", + (int)(ep - cp), cp); #endif - /* skip to next run of spaces */ - while (*cp && *cp != ' ' && *cp != '\t') - cp++; + } + cp = ep; } }