1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "resolv"
18 
19 #include "Dns64Configuration.h"
20 
21 #include <android-base/logging.h>
22 #include <netdb.h>
23 #include <netdutils/BackoffSequence.h>
24 #include <netdutils/DumpWriter.h>
25 #include <netdutils/InternetAddresses.h>
26 #include <netdutils/ThreadUtil.h>
27 #include <thread>
28 #include <utility>
29 
30 #include <arpa/inet.h>
31 
32 #include "DnsResolver.h"
33 #include "getaddrinfo.h"
34 #include "netd_resolv/resolv.h"
35 #include "stats.pb.h"
36 
37 namespace android {
38 
39 using android::base::StringPrintf;
40 using android::net::NetworkDnsEventReported;
41 using netdutils::DumpWriter;
42 using netdutils::IPAddress;
43 using netdutils::IPPrefix;
44 using netdutils::ScopedAddrinfo;
45 using netdutils::setThreadName;
46 
47 namespace net {
48 
49 const char Dns64Configuration::kIPv4OnlyHost[] = "ipv4only.arpa.";
50 const char Dns64Configuration::kIPv4Literal1[] = "192.0.0.170";
51 const char Dns64Configuration::kIPv4Literal2[] = "192.0.0.171";
52 
startPrefixDiscovery(unsigned netId)53 void Dns64Configuration::startPrefixDiscovery(unsigned netId) {
54     std::lock_guard guard(mMutex);
55 
56     // TODO: Keep previous prefix for a while
57     // Currently, we remove current prefix, if any, before starting a prefix discovery.
58     // This causes that Netd and framework temporarily forgets DNS64 prefix even the prefix may be
59     // discovered in a short time.
60     removeDns64Config(netId);
61 
62     Dns64Config cfg(getNextId(), netId);
63     // Emplace a copy of |cfg| in the map.
64     mDns64Configs.emplace(std::make_pair(netId, cfg));
65 
66     // Note that capturing |cfg| in this lambda creates a copy.
67     std::thread discovery_thread([this, cfg, netId] {
68         setThreadName(StringPrintf("Nat64Pfx_%u", netId).c_str());
69 
70         // Make a mutable copy rather than mark the whole lambda mutable.
71         // No particular reason.
72         Dns64Config evalCfg(cfg);
73 
74         auto backoff = netdutils::BackoffSequence<>::Builder()
75                                .withInitialRetransmissionTime(std::chrono::seconds(1))
76                                .withMaximumRetransmissionTime(std::chrono::seconds(3600))
77                                .build();
78 
79         while (true) {
80             if (!this->shouldContinueDiscovery(evalCfg)) break;
81 
82             android_net_context netcontext{};
83             mGetNetworkContextCallback(evalCfg.netId, 0, &netcontext);
84 
85             // Prefix discovery must bypass private DNS because in strict mode
86             // the server generally won't know the NAT64 prefix.
87             netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
88             if (doRfc7050PrefixDiscovery(netcontext, &evalCfg)) {
89                 this->recordDns64Config(evalCfg);
90                 break;
91             }
92 
93             if (!this->shouldContinueDiscovery(evalCfg)) break;
94 
95             if (!backoff.hasNextTimeout()) break;
96             {
97                 std::unique_lock<std::mutex> cvGuard(mMutex);
98                 // TODO: Consider some chrono math, combined with wait_until()
99                 // perhaps, to prevent early re-resolves from the removal of
100                 // other netids with IPv6-only nameservers.
101                 mCv.wait_for(cvGuard, backoff.getNextTimeout());
102             }
103         }
104     });
105     discovery_thread.detach();
106 }
107 
stopPrefixDiscovery(unsigned netId)108 void Dns64Configuration::stopPrefixDiscovery(unsigned netId) {
109     std::lock_guard guard(mMutex);
110     removeDns64Config(netId);
111     mCv.notify_all();
112 }
113 
getPrefix64Locked(unsigned netId) const114 IPPrefix Dns64Configuration::getPrefix64Locked(unsigned netId) const REQUIRES(mMutex) {
115     const auto& iter = mDns64Configs.find(netId);
116     if (iter != mDns64Configs.end()) return iter->second.prefix64;
117 
118     return IPPrefix{};
119 }
120 
getPrefix64(unsigned netId) const121 IPPrefix Dns64Configuration::getPrefix64(unsigned netId) const {
122     std::lock_guard guard(mMutex);
123     return getPrefix64Locked(netId);
124 }
125 
dump(DumpWriter & dw,unsigned netId)126 void Dns64Configuration::dump(DumpWriter& dw, unsigned netId) {
127     static const char kLabel[] = "DNS64 config";
128 
129     std::lock_guard guard(mMutex);
130 
131     const auto& iter = mDns64Configs.find(netId);
132     if (iter == mDns64Configs.end()) {
133         dw.println("%s: none", kLabel);
134         return;
135     }
136 
137     const Dns64Config& cfg = iter->second;
138     if (cfg.prefix64.length() == 0) {
139         dw.println("%s: no prefix yet discovered", kLabel);
140     } else {
141         dw.println("%s: %s prefix %s", kLabel, cfg.isFromPrefixDiscovery() ? "discovered" : "set",
142                    cfg.prefix64.toString().c_str());
143     }
144 }
145 
146 // NOTE: The full RFC 7050 DNS64 discovery process is not implemented here.
147 // Instead, and more simplistic version of the same thing is done, and it
148 // currently assumes the DNS64 prefix is a /96.
doRfc7050PrefixDiscovery(const android_net_context & netcontext,Dns64Config * cfg)149 bool Dns64Configuration::doRfc7050PrefixDiscovery(const android_net_context& netcontext,
150                                                   Dns64Config* cfg) {
151     LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId
152                  << ") Detecting NAT64 prefix from DNS...";
153 
154     const struct addrinfo hints = {
155             .ai_family = AF_INET6,
156     };
157 
158     // TODO: Refactor so that netd can get all the regular getaddrinfo handling
159     // that regular apps get. We bypass the UNIX socket connection back to
160     // ourselves, which means we also bypass all the special netcontext flag
161     // handling and the resolver event logging.
162     struct addrinfo* res = nullptr;
163     NetworkDnsEventReported event;
164     const int status =
165             resolv_getaddrinfo(kIPv4OnlyHost, nullptr, &hints, &netcontext, &res, &event);
166     ScopedAddrinfo result(res);
167     if (status != 0) {
168         LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId << ") plat_prefix/dns("
169                      << kIPv4OnlyHost << ") status = " << status << "/" << gai_strerror(status);
170         return false;
171     }
172 
173     // Use only the first result.  If other records are present, possibly
174     // with differing DNS64 prefixes they are ignored. Note that this is a
175     // violation of https://tools.ietf.org/html/rfc7050#section-3
176     //
177     //     "A node MUST look through all of the received AAAA resource records
178     //      to collect one or more Pref64::/n."
179     //
180     // TODO: Consider remedying this.
181     if (result->ai_family != AF_INET6) {
182         LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId
183                      << ") plat_prefix/unexpected address family: " << result->ai_family;
184         return false;
185     }
186     const IPAddress ipv6(reinterpret_cast<sockaddr_in6*>(result->ai_addr)->sin6_addr);
187     // Only /96 DNS64 prefixes are supported at this time.
188     cfg->prefix64 = IPPrefix(ipv6, 96);
189     LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId << ") Detected NAT64 prefix "
190                  << cfg->prefix64.toString();
191     return true;
192 }
193 
isDiscoveryInProgress(const Dns64Config & cfg) const194 bool Dns64Configuration::isDiscoveryInProgress(const Dns64Config& cfg) const REQUIRES(mMutex) {
195     const auto& iter = mDns64Configs.find(cfg.netId);
196     if (iter == mDns64Configs.end()) return false;
197 
198     const Dns64Config& currentCfg = iter->second;
199     return (currentCfg.discoveryId == cfg.discoveryId);
200 }
201 
reportNat64PrefixStatus(unsigned netId,bool added,const IPPrefix & pfx)202 bool Dns64Configuration::reportNat64PrefixStatus(unsigned netId, bool added, const IPPrefix& pfx) {
203     if (pfx.ip().family() != AF_INET6 || pfx.ip().scope_id() != 0) {
204         LOG(WARNING) << "Abort to send NAT64 prefix notification. Unexpected NAT64 prefix ("
205                      << netId << ", " << added << ", " << pfx.toString() << ").";
206         return false;
207     }
208     Nat64PrefixInfo args = {netId, added, pfx.ip().toString(), (uint8_t)pfx.length()};
209     mPrefixCallback(args);
210     return true;
211 }
212 
shouldContinueDiscovery(const Dns64Config & cfg)213 bool Dns64Configuration::shouldContinueDiscovery(const Dns64Config& cfg) {
214     std::lock_guard guard(mMutex);
215     return isDiscoveryInProgress(cfg);
216 }
217 
removeDns64Config(unsigned netId)218 void Dns64Configuration::removeDns64Config(unsigned netId) REQUIRES(mMutex) {
219     const auto& iter = mDns64Configs.find(netId);
220     if (iter == mDns64Configs.end()) return;
221 
222     Dns64Config cfg = iter->second;
223     mDns64Configs.erase(iter);
224 
225     // Only report a prefix removed event if the prefix was discovered, not if it was set.
226     if (cfg.isFromPrefixDiscovery() && !cfg.prefix64.isUninitialized()) {
227         reportNat64PrefixStatus(netId, PREFIX_REMOVED, cfg.prefix64);
228     }
229 }
230 
recordDns64Config(const Dns64Config & cfg)231 void Dns64Configuration::recordDns64Config(const Dns64Config& cfg) {
232     std::lock_guard guard(mMutex);
233     if (!isDiscoveryInProgress(cfg)) return;
234 
235     removeDns64Config(cfg.netId);
236     mDns64Configs.emplace(std::make_pair(cfg.netId, cfg));
237 
238     reportNat64PrefixStatus(cfg.netId, PREFIX_ADDED, cfg.prefix64);
239 }
240 
setPrefix64(unsigned netId,const IPPrefix & pfx)241 int Dns64Configuration::setPrefix64(unsigned netId, const IPPrefix& pfx) {
242     if (pfx.isUninitialized() || pfx.family() != AF_INET6 || pfx.length() != 96) {
243         return -EINVAL;
244     }
245 
246     std::lock_guard guard(mMutex);
247 
248     // This method may only be called if prefix discovery has been stopped or was never started.
249     auto iter = mDns64Configs.find(netId);
250     if (iter != mDns64Configs.end()) {
251         if (iter->second.isFromPrefixDiscovery()) {
252             return -EEXIST;
253         } else {
254             mDns64Configs.erase(iter);
255         }
256     }
257 
258     Dns64Config cfg(kNoDiscoveryId, netId);
259     cfg.prefix64 = pfx;
260     mDns64Configs.emplace(std::make_pair(netId, cfg));
261 
262     return 0;
263 }
264 
clearPrefix64(unsigned netId)265 int Dns64Configuration::clearPrefix64(unsigned netId) {
266     std::lock_guard guard(mMutex);
267 
268     const auto& iter = mDns64Configs.find(netId);
269     if (iter == mDns64Configs.end() || iter->second.isFromPrefixDiscovery()) {
270         return -ENOENT;
271     }
272 
273     mDns64Configs.erase(iter);
274 
275     return 0;
276 }
277 
278 }  // namespace net
279 }  // namespace android
280