Warning: DNS encryption in Little Snitch 6.1 may occasionally fail
(obdev.at)529 points by HelenePhisher 3 days ago
529 points by HelenePhisher 3 days ago
>so presumably there's some other low-level non-blocking call
Correct, CFNetwork is open source so you can check implementation but last I remember it used some variant like `getaddrinfo_async`. But Apple really doesn't want you (the end-user) to use getaddrinfo (or the async variant CF exposes) to resolve an IP and then directly connect() via that ip, everything is geared towards connect-by-hostname since then Apple's can internally handle the implementation of happy-eyeballs.
Edit: You can read https://www.ietf.org/proceedings/72/slides/plenaryw-6.pdf for their thoughts on why they don't like the getaddrinfo() model [there are speaker notes at the bottom of each slide]
If you do need the lower-level control, Apple does still recommend `getaddrinfo`. It handles NAT64 translation for IPv6-only carrier networks:
https://developer.apple.com/library/archive/documentation/Ne...
That’s not the current documentation, as evidenced by the “archive” in the URL.
If you want to stay at a lower level the recommendation these days is to use Network.framework. If you want something higher level then use CFNetwork (probably through the classes exported by Foundation like NSURLSession).
I actually found it linked from here, which seems current: https://developer.apple.com/support/ipv6/
It is not best practice to use `getaddrinfo` for DNS resolution, for sure. But it is best practice to use it before connecting to an IP address directly because that address may need to be translated.
TL;DR:
Applications should not use getaddrinfo(). Because for the connect by name, the OS or app SDK can parallelize the entire multi-step lookup and connection process, not just step by step:
“Now, I’m not saying that all implementations of these APIs [Java, Apple Foundation, etc., doing connect by name] necessarily do the right thing today, but if applications are using these APIs, then the implementations can be improved over time.”
“The difference with getaddrinfo() and similar APIs is that they fundamentally can’t be improved over time. The API definition is that they return you a full list of addresses, so they have to wait until they have that full list to give you. There’s no way getaddrinfo can return you a partial list and then later give you some more.”
The deck's position on implementation of happy-eyeballs (which could sound dismissive here but is treated as "you had one job" important by the deck), is finding a way to avoid waiting 5 seconds for either side of IPv4 vs. IPv5 stack to timeout before finishing connection setup and serving the user a web page.
Thanks for that link, it's a very convincing presentation that very clearly explains the shortcomings of getaddrinfo.
Yeah, I went in thinking that it was going to be some case of Apple wanting to bend the Unix philosophy to their will for their own desires and steer implementations in their direction, but no - they are simply pointing out a clear flaw in the design of the function in question for a usecase that does not apply only to Apple. Basically all OS vendors need to be doing something like this usecase to support IPv6 adoption.
I often find this is the case with Apple on a technical level.
For instance, their recent Spatial (stereographic) Video features uses a format that has basically zero current support outside of Apple—which is in fact just standard MV-HEVC [0] (with some extra optional metadata [1]), which is just the H.265 evolution of the standard H.264 MVC that 3D Blu-rays have used for a long time. (AFAIK no 4K 3D Blu-rays have been released, presumably due to space constraints, explaining the lack of usage of MV-HEVC outside Apple).
In piracy world, most re-encoded 3D movies just use objectively inferior composited 2D formats like half-side-by-side or over/under. And without diving in you’d just assume Apple was using some bespoke format to be evil, when in fact they are popularizing what should be the canonical, standardized format for 3D video.
[0] http://hevc.info/mvhevc [1] https://developer.apple.com/av-foundation/HEVC-Stereo-Video-...
> getaddrinfo() is the way to resolve names on Linux
Not at all. That's just a glibc function, it's got nothing to do with Linux. People just assume that glibc is how things are done in Linux user space but it doesn't have to be that way. For example, systemd came up with its own resolved mechanism which turned out to be much better than the glibc stuff. I will probably end up inventing my own at some point as well since I'm working on freestanding software targeting Linux.
getaddrinfo is defined by POSIX and UNIX. Where the implementation is doesn’t matter. It’s portable, which is why it’s used. The slide deck referenced above talks about better implementations for various platforms, but they are all platform specific.
So OP might not be completely accurate, but getaddrinfo is _the_ way to resolve names if you are writing portable POSIX and/or UNIX code.
Linux and the popular Linux distributions are not POSIX compliant to begin with. Only GNU tries to be, and even GNU adds on a ludicrous amount of extensions because the truth is POSIX isn't good enough.
You're probably aware of c-ares, if not then check it out unless you really want to write your own.
(As an administrator I'm getting a bit tired of working around the differing bugs and behaviour of different resolver implementations).
glibc also has an async getaddrinfo_a function for asynchronous name resolution, with completion notification.
I will look into it even if I end up writing my own. Reading source code is always helpful. Thank you.
> getaddrinfo() is the way to resolve names on Linux and I suspect the *BSDs.
At least on OpenBSD, all classical/standard DNS functions (getaddrinfo/gethostbyname/...) are wrappers around OpenBSD's libc asr implementation, written by Eric Faurot.
> Maybe things are drastically different on macOS, but getaddrinfo() is the way to resolve names on Linux and I suspect the *BSDs.
I'm not sure if this is the case in this case, but it might be worth noting that some system functions with the same name have drastically different internal/implementation differences between Linux/*BSD/MacOS. With there being differences between the *BSDs too.
So on some systems one function call is "the way", because its been maintained over the years, but then on another it might actually be old and not useful.
Everything in the UNIX compatibility layer is low-level in macOS. Not necessarily "legacy" though.
But this is no different than saying that, for example, calling out platform-specific native OS APIs from Java is "low-level." Which it is, from the perspective of compile-once, run-anywhere Java applets. macOS is a NeXT-compatible non-UNIX API, and you are supposed to use the macOS frameworks for everything. Calling down to BSD or even mach is definitely not what Apple wants you to do.
> macOS is a ... non-UNIX
Seems to be badly phrased and meant something else, since macOS is certified to be UNIX - https://www.opengroup.org/openbrand/register/ - contrary to Linux which is not UNIX-certified.
HN posted about this at least once - https://news.ycombinator.com/item?id=29984016
Curious about this
Isn't the Mach kernel based on BSD?
How much of getaddrinfo is in the kernel, how much of it is pure "libc"?
No, mach is a microkernel, like L5. It was developed for the purpose of replacing the BSD kernel, by having a small amount of functionality in the kernel itself, and the rest of the BSD-compatibility layer implemented in user space. macOS' frameworks are then a layer on top of that.
For quite some versions that modern networking APIs on macOS using Objective-C frameworks, starting in 2018.
See WWDC 2018's "Introducing Network.framework, A modern alternative to sockets".
NeXTSTEP might have been a UNIX, and macOS derives from it, but the whole UNIX story has always been to bring UNIX software into the platform, not to make it easier to move elsewhere.
macOS is still certified POSIX UNIX
EDIT: maybe not anymore, Sequoia isn't listed yet, https://www.opengroup.org/openbrand/register/xy.htm
Regardless, even if they renew the certification, they aren't obliged to expose in the classical POSIX APIs more than what the certification requires in features, or happens to be optional, implementation defined.
As anyone that has painfully tried to write POSIX portable code across big iron UNIX is aware of.
Of course, Apple does not want their app to call `getaddrinfo()` directly, because it would interfere with their internal XDR/NDS/IPS mechanism.
I can’t blame them but I personally would still have my apps use them, even knowingly that it would be made off-limit to iOS/iPadOS apps … soon.
I'll pile on, as someone who has never developed for Apple systems: What APIs are supposed to be used for DNS resolution?
* Host file
* Configured DNS server
* App-specific DNS server if it exists
What "API" is there? Why doesn't an app doing system-wide DNS modifictions just modify the settings for default resolver?The Apple deck linked elsewhere in this thread suggests the developer's goal generally isn't "DNS resolution", the dev's goal is usually establishing a connection to a host/server/endpoint to start doing something.
So, usually devs should use the Java or Apple or whatever higher level OS API gets you connected the fastest, and that API is free to implement the connection however most quickly gets to the point of able to return data to the user (app or end user).
The API that returns a list of addresses is stuck doing that, instead of being able to parallize the entire "get connected" data flow.
> This library wraps around the dnssd framework and the c-ares C library with Swift-friendly APIs and data structures.
It feels like Embrace, Extend, Extinguish to claim that a portable API is "legacy" and that its replacement is Apple-only.
Yes, this! I even wonder how else you would do this. By the way I worked with many IoT devices that do not use your dhcp dns but just hardcode quad 8 or similar
We recently had a developer join our team and he got stuck setting up his dev environment.
We use a .dev domain as a localhost alias, and turns out his ISP’s DNS wouldn’t resolve 127.0.0.1 (or whatever it is) for the .dev domain. Changing his resolver at the network level to 1.1.1.1 fixed it.
I imagine there are lots of difficult support tickets for app devs, and at a certain point they just hardcode the DNS to remove one variable from the equation when debugging bug reports.
Yeah, this report seems a little spun. The essence is basically that the encrypted DNS needs to go through the proxy, and there's resolver code elsewhere in the OS that doesn't use the proxy. It's a bug, sure. It could plausibly have interesting exploits, though none are shown. But it's not a very interesting bug.
Wait until you try to get the mac address on an iphone.
There isn't.
> @dang is a no-op. The only way to get reliable message delivery is to email hn@ycombinator.com
What?
You can do a fresh install of an older macOS version whenever you like (you need to enable that option in the rescue system tho).
You can also run older macOS in a VM (the hypervisor framework keeps getting new features that make guest macOS more fully supported).
Name an OS (ok maybe NixOS) that allows you to do clean downgrades out of the box. Also wonder what's gonna happen to your data in e.g. Postgres if you blindly downgrade.
yeah it feels like they decided bank accounts flush with cash were a better investment than legacy system support
Sequoia also breaks an application's ability to use DNS (or presumably anything UDP-based) if the macOS firewall is enabled, and an app is listed as "Block incoming connections". https://waclaw.blog/macos-firewall-blocking-web-browsing-aft...
I can't reproduce this. Some people say it has to do with ESET: https://www.reddit.com/r/MacOS/comments/1fievr5/updating_mad...
Confirmed, it is from an old ESET network filter: https://support.eset.com/en/alert8723-network-connection-los...
It's easily reproducible with a fresh macOS install. Yes, ESET has its own issue. But this is a problem in and of itself. https://imgur.com/a/Nr7Gk6c
Before Sequoia when using OpenDNS for VPN, could be on VPN and iMessage and other apps still work, but since Sequoia, when on VPN iMessage (text messages) etc no longer work. Once I disconnect to VPN all goes through. Is this related at all? Do have macOS firewall enabled. But not block all incoming connections.
Were the autofilled DNS servers in RFC1918 private space (10.0.0.0/8, 192.168.0.0/16, etc.)? I had issues after the upgrade with Google Chrome being unable to access hosts in these ranges, and fixed it by going to System Settings -> Privacy & Security -> Local Network and toggling Google Chrome off and on again.
> could not browse with Safari or Mozilla
FYI, it looks like Firefox fixed this.
> not macOS error?
It worked before I upgraded to Sequoia. But I don't know enough to point fingers. Just mentioning that turning off the firewall long enough for Firefox to update fixes the problem.
Honestly, I'm fine with that. Applications themselves should not be resolving DNS outside of what I set in settings.
The reasons applications do this is to prevent users from blocking telemetry etc. It's my computer, I should have final say on what goes out.
All major browsers now implement the ability to use a browser-defined resolver.
Seeing this getting downvoted is fucking wild.
I remember 20+ years ago when one of the most commonly seen attacks was malware configuring a proxy server in Internet Explorer which by design overrode the operating system's configuration.
What a lot of software does today by ignoring the operating system in lieu of their own shit is just like the above. If your program doesn't (or can't) respect the operating system, your shit is malware and you should reconsider who you write code for.
> Seeing this getting downvoted is fucking wild.
If you consider the source of income of what's most likely a considerable portion of the HN community, I think this makes more sense. Apple is one of the only companies interested in preventing tracking, and it hurts, in the billions sort of way [1][2].
[1] https://www.forbes.com/sites/kateoflahertyuk/2022/10/08/appl...
[2] https://www.forbes.com/sites/timbajarin/2022/07/26/apples-do...
I had a great Windows firewall like this about 20 years ago. It would pop up a dialog for every network request from an app. You could block or allow based on port or destination, or "block all". It was amazing, because as you say, it made it very obvious when an app was trying shady shit.
I would love to have that back, but I was never able to find a firewall so hostile to the user experience of the general population.
Shady shit? Not every network request is a call to an HTTP REST API.
Blocking socket APIs would break every app that supports other protocols. Goodbye file transfer apps, VPN apps, file sync apps, database tools, SSH clients, remote desktop clients, audio and video conferencing apps, etc.
I maybe imagining but I feel like deja vu that there will be a problem with DNS that would affect Little snitch., Mullvad and others with new releases of iOS and Mac. If true I would really question what apple is doing during their months long developer and beta testing.
I was confused at the Little Snitch mention, and then reading further it just seems like a LS bug, that it only works in certain cases.
Well, seems this is the LS blog, so only confusion is why this is portrayed as a macOS bug? I'm not saying it's wrong, it's their domain not mine after all, it just doesn't seem to be justified in TFA?
The /etc/resolv.conf system is woefully inadequate. It doesn't have a concept of per-interface customization so you can't customize according to the currently active network interface. It doesn't distinguish between DNS configuration delivered by the network administrator (which can and should be changed remotely) versus set by the computer administrator. It doesn't work very well with VPNs where a specific DNS server is used for resolving addresses on that VPN.
If I recall, Apple deprecated use of certain network apis for third party developers. But Apple’s own apps (App Store) do not have these same restrictions. Thus, when trying to filter network traffic via app firewall via new APIs. It would fail since App Store uses legacy APIs.
Maybe part of this old bug (that I thought was fixed)
getaddrinfo() is not a legacy API, it's a standard cross platform API for doing DNS lookups.
Specifically defined by POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/functions/g...
Funny how that goes: macOS is POSIX certified but no other desktop BSD or Linux is.
There have been POSIX-certified Linux variants. But the open source projects you use don't bother (for obvious reasons) and commercial derivatives like Android and ChromeOS don't need it. Similarly Window NT was POSIX-certified way back in the day yet its descendants aren't, even though they implement the same API set (via very different technology).
According to https://www.opengroup.org/openbrand/register/ macOS 15 is not certified.
it is apparently on Mac and arguably with good reason. See this comment https://news.ycombinator.com/item?id=41572770
I use routedns [0] as my local stub resolver so that I can pick and choose which requests go to where and also what transport they use. It can also blocklist, re-write, cache, load balance, and/or handle fall back requests; so it give you lots of control.
I use a stub listener on localhost:53 for local requests and then forward them via UDP QUIC (TLS 0-RTT) requests to Cloudflare (1.1.1.1) with caching for most requests. Fast and reasonably secure.
> After further investigation, we found that this bug has already existed at least since macOS 14.5 Sonoma
Isn't this an inherent risk when attempting to do network stuff in userspace? You're at a very high level so hoping that lower level things comply seems risky if DNS encryption is critical to your use case.
Apple removed support for kernel extensions, and instead added a bunch of APIs that allow to do network filtering etc in user space. Unfortunately, some of their networking code just bypasses those network filter extensions (probably because of bugs) -- this is not the first time the developers of Little Snitch publicized a bug like this.
The battle of DNS resolving ownership rages on: who has the rights to set the DNS nameservers/resolver.
As a long-time DNS security researcher, the ultimate and final end means would be to mirror the root servers, but I assert, for now, popping in your own `resolv.conf` should suffice, … again, for now.
I wonder how little snitch sets the dns encryption up. In macOS, you need to setup encrypted dns via a profile System (Settings => General => VPN, DNS & Device Management) and then in the browser. However, I think terminal and appstore still use whatever server is obtained via DHCP and is not encrypted.
> "To protect (DNS lookups) from prying eyes, Little Snitch 6 offers a new feature: DNS encryption."
Browsers such as Firefox have offered this directly for a while. Of course, that only covers DNS lookups made from the web browser, but it doesn't rely on OS-level hooks that (at least in Apple's case) can break.
They're using Little Snitch as an OS-level DNS proxy, which should intercept all DNS requests from any app and encrypt them. But, depending on what API the app uses for its DNS lookups, some DNS requests do not go via the proxy. Presumably Firefox, in its default configuration with DNS encryption set to OFF ("Use your default DNS resolver"), uses one the affected APIs.
Plugging https://www.supernetworks.org/ -- when on wifi/vpn all DNS will go up over DNS over HTTPS as plaintext DNS is DNAT'd to CoreDNS which is by default configured to use DoH.
Deploying DNS encryption on macOS is in general really tedious. Applying it as a system or user profile has different results. Switching between providers or temporarily disabling DNS encryption is painful.
I also still haven't figured out how to get SSID-based switching to work, does it even?
This is why I firewall egress port 53 at the router level.
I’ve had issues using the Resolv library in Ruby when I’m connecting to the internet via a tethered iPhone. Never ran into that until Sequoia. I wonder if that’s related?
TBH I’m too lazy to dig in and find out. Has anybody else run into this issue?
See: https://waclaw.blog/macos-firewall-blocking-web-browsing-aft...
If disabling the firewall (for testing) solves this problem, this is likely your issue.
Can some ELI5 why you'd use a proxy rather than reset the server name?
> UPDATE: Spoke too soon… The problem discussed here turned out to be specific to Little Snitch 6.1 and not a general issue in macOS.
Not really.
I would believe so. I have a custom DNS profile setup that redirects a few domains to a server I run. The server has custom SSL certs issued by a private CA. I the certificate installed on iOS as a trusted root certificate.
Everytime I'm connected to my home WiFi I would randomly get `peer closed connection in SSL handshake (104: Connection reset by peer)`. I have absolutely no clue why it does this and this issue goes away when I'm connected on mobile data.
Now I'm guessing that it is bypassing the DNS profile and resolving it using my ISPs DNS or some other way.
> I would believe so.
It won't, it was specifically a bug in Little Snitch (which doesn't currently run on iOS, I believe.)
"The problem discussed here turned out to be specific to Little Snitch 6.1 and not a general issue in macOS. It has already been fixed in Little Snitch 6.1.1."
macos may bypass LITTLE SNITCH'S encrypted dns proxy, more like it.
Hmm. I use NextDNS for this feature. I think. May have to do some testing to see whether or not it is operational at all.
It's a little weird to me that getaddrinfo() is considered a "low-level legacy API". Maybe things are drastically different on macOS, but getaddrinfo() is the way to resolve names on Linux and I suspect the *BSDs.
Sure, I expect most macOS apps will use something in Foundation or some other NetworkKit-type framework to do DNS queries, but it's odd to me that the code there wouldn't then call down to getaddrinfo() or the like to do the dirty work. I guess GAI is blocking, so presumably there's some other low-level non-blocking call?