From nobody Mon Jun 29 15:52:06 2026 X-Original-To: dev-commits-src-main@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 4gprPg087Sz6jNl5 for ; Mon, 29 Jun 2026 15:52:07 +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 4gprPf4tcKz3L1g for ; Mon, 29 Jun 2026 15:52:06 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782748326; 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=q6NGaTcyztLSR7ScVG1wO7Dp7mxZlz5Gz2OSn8c+JKI=; b=fl1VkO/0FG/7MxzlRW+CXn+3xAhEpltE2roLxTH/TBB1flBUK8Lt2Wqn6nPLALq7QCJIRc NNCEcWhIWtI0D6J+ve3NHftTUnSwc6EBJP+3yt5N1dJL4Zxvk9ZoaqAp+Cbq4Bm0q1Djiu qFTW5MdeZ3mfdYFFZpLHBA4umz6vA0lO8COaBoFUTh5vHpZpWeiQIyZ8RSiU9LYEpDNHDC h5qEBtbpgnR86YCQFUqH8wJ1Vf+TJ9MPVpmcDT833hw5K7V30bW2V2rUrvOkBRNiMxIjS/ lKaWr50CgDgpGTVWnKG3vnBX2ouTMqeJyXeWHo1iCsDCrOPBoBelUJgJL+lDhw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782748326; a=rsa-sha256; cv=none; b=Qvgo9m0v6B9Trcpc0lENKRpqCOJuMVpmsWwJCOGF89nP2BxUjcqWNXD9G2warsckIYMrHd zm9CmJDpwkx8z3jQEE0qqB7ivQRXI2iZ/rWVJBRX5+YEhdUQbnqZPFUJdVPZSBCdBOEo0P xhFC0eSTMUa5xxV6GWIIkLCnHyIiRIT/vV6VsujPYUKzhK2wJs0TvVpyO3zsR4a6ZcgOSj rUqdjJTKv4boRPdAvbXJrW8MzChqJK8r4WmNHPJbwFembJmibfoQW5+C4fMRR4lwPujjO1 URQn8exRINECuo6OnLMB6xtgCeH4ygRY5XwMOTI50OZDeuMagXG1C93IFSv7sA== 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=1782748326; 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=q6NGaTcyztLSR7ScVG1wO7Dp7mxZlz5Gz2OSn8c+JKI=; b=cAJUqBAsW5neTuwU+qS7DbI97CIDgRxUIf4sXmgaj9pxepGJMH1bBXCO/qlCnsVwcTR5G0 GH2+xE6uIZUOa4IT3sG9Ur8mu5IDDZTT8VEBzwIMlRUBaQyjBWue3T8OYp3yBRNwQtqi8J 9pPS1O7HYExUhhruWEQEJr+Jm3eShzuDse9uI2lTDhfb5PdZpQG/Cqx4Q/+lzxLS+2HZ9X YlZpX40goYFZx4tBS0Cp5nwqqDez2L3EkwyuqVAeyjImixJdIRD95/9Zr+BttiA7+MPuGN fhOxiGpxgg/C6kw9liZNfAxLl8FpT+3EcFl0/yHIvHvuRvo4IDV7B7iLYDrkbg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gprPf3bgVz1467 for ; Mon, 29 Jun 2026 15:52:06 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3b6a3 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 29 Jun 2026 15:52:06 +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: 40e10af6c6dc - main - libc/resolv: Refactor the option parser List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@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: 40e10af6c6dc46d9c0835db3dc95368ce7d69b55 Auto-Submitted: auto-generated Date: Mon, 29 Jun 2026 15:52:06 +0000 Message-Id: <6a4294a6.3b6a3.7017daff@gitrepo.freebsd.org> The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=40e10af6c6dc46d9c0835db3dc95368ce7d69b55 commit 40e10af6c6dc46d9c0835db3dc95368ce7d69b55 Author: Dag-Erling Smørgrav AuthorDate: 2026-06-25 11:33:47 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-06-29 13:49:18 +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. 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 value, we would previously happily set a numerical option to a negative value and treat non-numerical arguments as 0, while a numerical argument that happened to exceed the maximum for the option would result in the option being set to its default value. Now, any failure to parse the argument, including overflow, results in the option being set to its default value. MFC after: 1 week --- lib/libc/resolv/res_init.c | 165 ++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 77 deletions(-) diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index e9e982fe27bb..20f6824857b5 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 @@ -80,6 +81,7 @@ #include #include +#include #include #include #include @@ -103,9 +105,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. @@ -520,129 +524,136 @@ __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; + if (res_match(cp, sp, "ndots") && sp < ep) { + num = strtol(sp + 1, &numend, 10); + if (numend == ep && num <= RES_MAXNDOTS) + statp->ndots = num; else statp->ndots = RES_MAXNDOTS; #ifdef DEBUG 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 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; else statp->retrans = RES_MAXRETRANS; #ifdef DEBUG 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 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; else statp->retry = RES_MAXRETRY; #ifdef DEBUG 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 && + ext != NULL) { + ext->reload_period = (u_short)num; } - } #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)) { + strncpy(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)) { + strncpy(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; } }