From 2ccc518849c6cb4dab35fa60f5540a1ce5dae872 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 16 Sep 2022 17:41:52 -0300 Subject: [PATCH] Fix apple dns, part 817 --- llarp/config/config.cpp | 4 +-- llarp/dns/server.cpp | 74 +++++++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 9826e98f6..3cc0c8004 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -828,9 +828,7 @@ namespace llarp conf.defineOption( "dns", "query-bind", -#ifdef __APPLE__ - Default{"127.0.0.1:1253"}, -#elif defined(_WIN32) +#if defined(_WIN32) Default{"0.0.0.0:0"}, #else Hidden, diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index f75b280a7..8a85c6fdc 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -186,43 +186,61 @@ namespace llarp::dns } } - void - ConfigureUpstream(const llarp::DnsConfig& conf) + bool + ConfigureAppleTrampoline(const SockAddr& dns) { + // On Apple, when we turn on exit mode, we tear down and then reestablish the unbound + // resolver: in exit mode, we set use upstream to a localhost trampoline that redirects + // packets through the tunnel. In non-exit mode, we directly use the upstream, so we look + // here for a reconfiguration to use the trampoline port to check which state we're in. + // + // We have to do all this crap because we can't directly connect to upstream from here: + // within the network extension, macOS ignores the tunnel we are managing and so, if we + // didn't do this, all our DNS queries would leak out around the tunnel. Instead we have to + // bounce things through the objective C trampoline code (which is what actually handles the + // upstream querying) so that it can call into Apple's special snowflake API to set up a + // socket that has the magic Apple snowflake sauce added on top so that it actually routes + // through the tunnel instead of around it. + // + // But the trampoline *always* tries to send the packet through the tunnel, and that will + // only work in exit mode. + // + // All of this macos behaviour is all carefully and explicitly documented by Apple with + // plenty of examples and other exposition, of course, just like all of their wonderful new + // APIs to reinvent standard unix interfaces with half-baked replacements. + if constexpr (platform::is_apple) { - // On Apple, when we turn on exit mode, we can't directly connect to upstream from here - // because, from within the network extension, macOS ignores setting the tunnel as the - // default route and would leak all DNS; instead we have to bounce things through the - // objective C trampoline code (which is what actually handles the upstream querying) so - // that it can call into Apple's special snowflake API to set up a socket that has the - // magic Apple snowflake sauce added on top so that it actually routes through the tunnel - // instead of around it. - // - // This behaviour is all carefully and explicitly documented by Apple with plenty of - // examples and other exposition, of course, just like all of their wonderful new APIs to - // reinvent standard unix interfaces. - - // Not at all clear why this is needed but without it we get "send failed: Can't - // assign requested address" when unbound tries to connect to the localhost address - // using a source address of 0.0.0.0. Yay apple. - SetOpt("outgoing-interface:", "127.0.0.1"); - - // The trampoline expects just a single source port (and sends everything back to it) - SetOpt("outgoing-range:", "1"); - SetOpt("outgoing-port-avoid:", "0-65535"); - SetOpt("outgoing-port-permit:", "{}", apple::dns_trampoline_source_port); - - AddUpstreamResolver(SockAddr{127, 0, 0, 1, {apple::dns_trampoline_port}}); - - return; + if (dns.hostString() == "127.0.0.1" and dns.getPort() == apple::dns_trampoline_port) + { + // macOS is stupid: the default (0.0.0.0) fails with "send failed: Can't assign requested + // address" when unbound tries to connect to the localhost address using a source address + // of 0.0.0.0. Yay apple. + SetOpt("outgoing-interface:", "127.0.0.1"); + + // The trampoline expects just a single source port (and sends everything back to it). + SetOpt("outgoing-range:", "1"); + SetOpt("outgoing-port-avoid:", "0-65535"); + SetOpt("outgoing-port-permit:", "{}", apple::dns_trampoline_source_port); + return true; + } } + return false; + } + + void + ConfigureUpstream(const llarp::DnsConfig& conf) + { + bool is_apple_tramp = false; // set up forward dns for (const auto& dns : conf.m_upstreamDNS) + { AddUpstreamResolver(dns); + is_apple_tramp = is_apple_tramp or ConfigureAppleTrampoline(dns); + } - if (auto maybe_addr = conf.m_QueryBind) + if (auto maybe_addr = conf.m_QueryBind; maybe_addr and not is_apple_tramp) { SockAddr addr{*maybe_addr}; std::string host{addr.hostString()};