]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
OPTIM: pattern: try literal IPv6 parsing before DNS resolution in pat_parse_ip
authorAndre Brait <andrebrait@gmail.com>
Wed, 22 Jul 2026 20:53:17 +0000 (22:53 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 3 Aug 2026 08:10:31 +0000 (10:10 +0200)
pat_parse_ip() calls str2net() with DNS resolution enabled before trying
str62net(), so every IPv6 entry of a pattern file pays a blocking DNS
resolution attempt (which necessarily fails) before being parsed as an
IPv6 literal. On a configuration loading a 20k-entry IPv6 acl file on a
host with a resolv.conf search domain, this added two DNS round trips
per entry and made "haproxy -c" take 14 seconds instead of about 1.

Try both literal parsers first, then fall back to the resolving call of
str2net(). An IPv6 literal can never be a valid host name (colons are
not permitted in DNS labels) and host names keep resolving exactly as
before, so no working configuration changes behaviour.

Diagnosed on pfSense, where large pfBlockerNG IP block lists are loaded
through "acl ... -f" files:
https://github.com/pfsense/FreeBSD-ports/pull/1450

This patch was authored with Claude Fable 5 and reviewed by GPT-5.6 Sol.

src/pattern.c

index 17322e3a3ce24e2f0e7db1eb68000d510b36082c..d62718210123b36a4e26fdfbeca5f0c322d227a8 100644 (file)
@@ -408,8 +408,14 @@ int pat_parse_dotted_ver(const char *text, struct pattern *pattern, int mflags,
  */
 int pat_parse_ip(const char *text, struct pattern *pattern, int mflags, char **err)
 {
-       if (str2net(text, !(mflags & PAT_MF_NO_DNS) && (global.mode & MODE_STARTING),
-                   &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
+       /* Try literal parsing of both address families first, and only then
+        * fall back to a DNS resolution attempt for the IPv4 case. Resolving
+        * before trying the IPv6 literal parsing would cost a useless blocking
+        * DNS round trip for every IPv6 entry of a pattern file, since an IPv6
+        * literal can never be a valid host name (colons are not permitted in
+        * DNS labels).
+        */
+       if (str2net(text, 0, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
                pattern->type = SMP_T_IPV4;
                return 1;
        }
@@ -417,6 +423,11 @@ int pat_parse_ip(const char *text, struct pattern *pattern, int mflags, char **e
                pattern->type = SMP_T_IPV6;
                return 1;
        }
+       else if (!(mflags & PAT_MF_NO_DNS) && (global.mode & MODE_STARTING) &&
+                str2net(text, 1, &pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
+               pattern->type = SMP_T_IPV4;
+               return 1;
+       }
        else {
                memprintf(err, "'%s' is not a valid IPv4 or IPv6 address", text);
                return 0;