DoH proxy: https-dns-proxy new RFC8484-supporting package and Web UI

You're throwing too many issues at once and the discussion is difficult to keep up with. It's Saturday. I don't want to think that hard. :slight_smile:

I'll do some more experiments in the coming days since I still have concerns about the IPv6 limitations. The LuCI issues and the bootstrap issues are less of a concern to me personally, so I'll let others weigh in.

1 Like

I've done a little more digging and I can't reconcile the docs, the interface, and the code in how it wants to treat IPv6 resolvers.

In the docs, it states that it's an either/or proposition between IPv4 and IPv6. However, the docs should reference the force_ipv6_resolvers option instead.
Official Documentation:

Option Type Default Description
force_ipv6 Boolean 0 If set to 1, forces IPv6 DNS resolvers instead of IPv4.

In LuCI, it reads like it should be IPv4+IPv6 (any family) or IPv6-only:

But in the init script, I see the following behaviors:

  • IPv4 Bootstrap DNS IPs are only permitted if force_ipv6 is disabled (0).
  • IPv6 Bootstrap DNS IPs are only permitted if force_ipv6 is enabled (1).
  • If force_ipv6 is disabled, https-dns-proxy is passed the -4 parameter which only allows the proxy to send IPv4 requests upstream (AF_INET versus AF_UNSPEC).

So at the moment, there is no support for a dual-stack / any family / IPv4+IPv6 instance of https-dns-proxy, even though the upstream project supports it.

If I invoke an instance manually like:

/usr/sbin/https-dns-proxy -r https://security.cloudflare-dns.com/dns-query -p 5055 -b 2606:4700:4700::1112,2606:4700:4700::1002,1.1.1.2,1.0.0.2 -u nobody -g nogroup 

without the -4 parameter and with mixed bootstrap IPs, it will appear to work just fine with either address family.

So what is the intended behavior? Since upstream only implements a "forced IPv4" mode or else "any", is this option better worded as "Force IPv4 only" and the logic inverted?

  • Allow either IPv4 or IPv6 bootstrap if "Force IPv4" is disabled
  • If "Force IPv4" is enabled, do not allow IPv6 bootstrap addresses
  • Only pass the -4 parameter if "Force IPv4" is enabled

Thanks for reading this far and providing feedback.

2 Likes

The copy-paste approach to bootstrap_dns in providers.json (using Cloudflare/Google as a generic fallback for dozens of completely independent providers) merely masks this defect during static tests. The moment a user attempts to scale, isolate filtering profiles, or deploy unique native infrastructure, the logic collapse becomes fully visible.

Thanks all, I am currently traveling the next 4 weeks so not much time to react.

I know stangri is also very busy, fortunately this is not an urgent matter but it will be continued

reference? I don't think you/AI tool have a grasp on what the bootstrap servers are. it's the servers used to resolve the DoH domain name into its IP address only (as @dave14305 kindly verified). You're confusing the non-encrypted filtering DNS servers addresses with the bootstrap servers addresses.

I believe there were issues with legacy-only configs when instances were launched with both families bootstraps. Not to say the current implementation is ideal, just pointing out the reason. If you want to suggest a logic/prompt to claude to fix that, please go ahead.

1 Like

Bug 1 - The Practical Issue:
My ISP uses strict MAC and identity binding. With the new DUID implementation in OpenWrt 25.x, I lose internet access completely. Instead of a legacy DUID (which is just a MAC address plus a few identifier bytes), the ISP receives a massive alphanumeric string. It flags this as an anomaly and blocks both my IPv4 and IPv6 configuration leases.

If I manually clear this "garbage" DUID string, the router reverts to sending the older ID version for IPv6, allowing the ISP to see the correct MAC on the IPv6 side. However, IPv4 remains blocked. My ISP enforces a strict 30-minute session timeout before it flushes the IPv4 state, syncs it with the IPv6 ID, and restores dual-stack access.

During this 30-minute window, I am stuck in a textbook "modern" IPv6-only environment. Even if I get lucky and choose a provider in HTTPS DNS Proxy that includes IPv6 bootstraps, it still doesn't work for those 30 minutes (you can figure out the technical reason yourself). To prove that HTTPS DNS Proxy is the culprit, I simply stop and disable the service. Like magic, the internet starts working immediately using either the ISP's native DNS or manually assigned Cloudflare IPv6 addresses in the WAN section. I could spend these 30 minutes actually using the internet, or I can listen to another lecture about how I'm "clueless" and don't understand how it works. Frankly, I don't need to know the inner minutiae; the objective fact is—it breaks.

Bug 2 - The Logical/Privacy Flaw (A Real-Life Analogy):
Imagine I want to secretly save a few Euros for a new router and PC because my wife thinks it's a waste of money, and I'd rather avoid an argument. I decide to hide the cash in a tree hollow. But instead of keeping this transaction strictly private between myself and the tree, I constantly run to my wife's best friend for advice. I nag her about whether the tree hollow is safe, what plastic bag to use, and whether I am doing it right. Then, on my way back, I forget her advice (simulating a connection drop and a new handshake request in HTTPS DNS Proxy), so I start yelling across a noisy, crowded street, asking her all over again where I should hide the money.

It sounds absurd, because it is. In this scenario, everyone gets compromised—my actual chosen DNS provider, Cloudflare, and Quad9—because instead of quietly negotiating with my target provider directly, I am broadcasting my networking intent to third-party bootstrap servers via unencrypted lookups.

Am I understanding the current logic correctly through these real-world examples? If my points sound slightly off due to the AI translation, it's not because I lack understanding of the networking principles—it's simply because English is not my native language, and AI translation tools are inherently imperfect, as every LLM openly states on its homepage.

What happens if you enable Force use of IPv6 DNS resolvers?
That should get you a working IPv6 solution provided there are IPv6 bootstrap servers

Perhaps. But in my view, this should behave exactly like normal, native router networking: if IPv4 is available, it uses IPv4; if IPv6 is available, it uses IPv6; if both are present, it should gracefully use both dynamically (true dual-stack out of the box).

Having to manually "force" specific IP families is not only suspicious, it's downright alarming. It feels like a forced packet override or an aggressive configuration hack. In networking, there is always an unwritten rule: when you have to start "forcing" things to bypass broken backend logic, something eventually breaks so severely that you won't even be able to fix it.

That is exactly as stangri already mentioned in his comment:

I believe there were issues with legacy-only configs when instances were launched with both families bootstraps. Not to say the current implementation is ideal, just pointing out the reason. If you want to suggest a logic/prompt to claude to fix that, please go ahead.

And @dave14305 elaborated on

First step shoul be to add IPv6 bootstrap servers where none are available.

I can make a PR for that.

You guys are the experts, so you definitely know best. However, I’ve noticed that even in profiles where an IPv6 bootstrap address is already provided, the service still fails to function properly. I’m not speaking from theory here—this is based entirely on my practical, real-world testing. I can only report what I actually see happening on my end.

I completely understand that alternatives always exist, but I genuinely want to use this specific plugin rather than switching to something else. I guess that’s just my cross to bear! It is what it is.

Please give me an honest answer if you can: do my investigations actually make sense, or am I just wasting my time here? If it's the latter, then I'm out. I am neither a programmer nor a freelance beta tester.

If, as the maintainers of this plugin, you truly believe everything is working perfectly and there is no room for improvement, I will step away from the discussion and stop submitting bug reports. That way, everyone gets some peace and quiet, and we can all move on.

There is certainly room for improvement in the handling of IPv6. Just not sure what the original issues were that required the use of this -4 flag.

1 Like

fully agree with @dave14305

Maybe using both IPv4 and IPv6 is not always handled well e.g. if there is no IPv6 implemented on the router.
So maybe we should just keep the -4 or -6 flag and not use both but then at least the settings text should be adapted and the manual updated

But stangri is the one who can make the call :slight_smile:

Proof please. You keep beating this dead horse without substantiating your claims, even when @dave14305 actually sniffed on packages and proved this wrong.

This has been brought up before that by default only IPv4 servers are bootstrapped, people are looking into this.

If @egc @dave14305 are in legacy IP-only environments and can test if adding IPv6 bootstraps still affects resolution on 25.12 and/or make PR to include them, I'd be grateful for help.

1 Like

Let’s clear up the misunderstanding: I never claimed user browsing traffic leaks through the bootstrap.

The leak is infrastructure metadata. If a user chooses an alternative provider specifically to avoid Cloudflare, the current setup still sends an unencrypted port 53 query to 1.1.1.1 revealing exactly which DoH domain they are about to use. Broadcasting your infrastructure choice to a third party in cleartext is a metadata disclosure.

And you happen to have a solution for that?

I'm not a programmer, but maybe this will give some ideas?

  1. Stop using Cloudflare as a generic fallback. If someone selects Quad9, the bootstrap should point to Quad9's own unencrypted IPs. This keeps the metadata within the chosen ecosystem.

  2. Allow pinning static IPs for the DoH hostnames directly in the UCI config (like a local hosts file entry). If the daemon already knows the target IP, it can skip the cleartext Port 53 lookup entirely and go straight to the TLS handshake.

Meaning, completely overhaul the provider database to map each one to its exact native IPs. It might also be worth trimming the list down to just the most popular and reliable ones.

And if anyone wants to request a new provider after that, they should be required to provide a clear description and a direct link to the official documentation with the correct setup/IP details.

https://adguard-dns.io/kb/general/dns-providers

https://dns.alidns.com/dns-query

nslookup dns.alidns.com
Addresses:
          2400:3200::1
          2400:3200:baba::1
          223.6.6.6
          223.5.5.5

If someone selects Quad9 in the Luci app, the instance should use Quad9-affiliated bootstrap servers which are coded in the relevant json file.

PS. If you're that unhappy with whatever is coded in the luci app that you have made countless posts about it with assumptions not based in reality, you're welcome to configure your DoH proxy instances with uci config.

PPS. You can also delete the combined json file that the luci app installs and use a handful of json files just for the resolvers you want. It's trivial to change those files to your liking, even for a non-programmer.

1 Like

Fair point, Quad9 was just an example. Take BlahDNS instead—the issue is still there.

The database needs a consistent cleanup so every single profile relies strictly on its own native IPs instead of third-party fallbacks.

Summary

Fair enough. Since manually editing JSON and UCI files defeats the whole purpose of having a LuCI web interface for regular users, I will just handle my own configuration.

Thanks for the feedback, and good luck with the plugin.