dns_config_service_linux.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/dns/dns_config_service_linux.h"
  5. #include <netdb.h>
  6. #include <netinet/in.h>
  7. #include <resolv.h>
  8. #include <sys/socket.h>
  9. #include <sys/types.h>
  10. #include <map>
  11. #include <memory>
  12. #include <string>
  13. #include <type_traits>
  14. #include <utility>
  15. #include <vector>
  16. #include "base/bind.h"
  17. #include "base/callback.h"
  18. #include "base/check.h"
  19. #include "base/containers/contains.h"
  20. #include "base/files/file_path.h"
  21. #include "base/files/file_path_watcher.h"
  22. #include "base/location.h"
  23. #include "base/logging.h"
  24. #include "base/memory/raw_ptr.h"
  25. #include "base/metrics/histogram_functions.h"
  26. #include "base/metrics/histogram_macros.h"
  27. #include "base/sequence_checker.h"
  28. #include "base/threading/scoped_blocking_call.h"
  29. #include "base/time/time.h"
  30. #include "net/base/ip_endpoint.h"
  31. #include "net/dns/dns_config.h"
  32. #include "net/dns/nsswitch_reader.h"
  33. #include "net/dns/public/resolv_reader.h"
  34. #include "net/dns/serial_worker.h"
  35. #include "third_party/abseil-cpp/absl/types/optional.h"
  36. namespace net {
  37. namespace internal {
  38. namespace {
  39. const base::FilePath::CharType kFilePathHosts[] =
  40. FILE_PATH_LITERAL("/etc/hosts");
  41. #ifndef _PATH_RESCONF // Normally defined in <resolv.h>
  42. #define _PATH_RESCONF FILE_PATH_LITERAL("/etc/resolv.conf")
  43. #endif
  44. constexpr base::FilePath::CharType kFilePathResolv[] = _PATH_RESCONF;
  45. #ifndef _PATH_NSSWITCH_CONF // Normally defined in <netdb.h>
  46. #define _PATH_NSSWITCH_CONF FILE_PATH_LITERAL("/etc/nsswitch.conf")
  47. #endif
  48. constexpr base::FilePath::CharType kFilePathNsswitch[] = _PATH_NSSWITCH_CONF;
  49. absl::optional<DnsConfig> ConvertResStateToDnsConfig(
  50. const struct __res_state& res) {
  51. absl::optional<std::vector<net::IPEndPoint>> nameservers =
  52. GetNameservers(res);
  53. DnsConfig dns_config;
  54. dns_config.unhandled_options = false;
  55. if (!nameservers.has_value())
  56. return absl::nullopt;
  57. // Expected to be validated by GetNameservers()
  58. DCHECK(res.options & RES_INIT);
  59. dns_config.nameservers = std::move(nameservers.value());
  60. dns_config.search.clear();
  61. for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
  62. dns_config.search.emplace_back(res.dnsrch[i]);
  63. }
  64. dns_config.ndots = res.ndots;
  65. dns_config.fallback_period = base::Seconds(res.retrans);
  66. dns_config.attempts = res.retry;
  67. #if defined(RES_ROTATE)
  68. dns_config.rotate = res.options & RES_ROTATE;
  69. #endif
  70. #if !defined(RES_USE_DNSSEC)
  71. // Some versions of libresolv don't have support for the DO bit. In this
  72. // case, we proceed without it.
  73. static const int RES_USE_DNSSEC = 0;
  74. #endif
  75. // The current implementation assumes these options are set. They normally
  76. // cannot be overwritten by /etc/resolv.conf
  77. const unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
  78. if ((res.options & kRequiredOptions) != kRequiredOptions) {
  79. dns_config.unhandled_options = true;
  80. return dns_config;
  81. }
  82. const unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
  83. if (res.options & kUnhandledOptions) {
  84. dns_config.unhandled_options = true;
  85. return dns_config;
  86. }
  87. if (dns_config.nameservers.empty())
  88. return absl::nullopt;
  89. // If any name server is 0.0.0.0, assume the configuration is invalid.
  90. for (const IPEndPoint& nameserver : dns_config.nameservers) {
  91. if (nameserver.address().IsZero())
  92. return absl::nullopt;
  93. }
  94. return dns_config;
  95. }
  96. // Helper to add the effective result of `action` to `in_out_parsed_behavior`.
  97. // Returns false if `action` results in inconsistent behavior (setting an action
  98. // for a status that already has a different action).
  99. bool SetActionBehavior(const NsswitchReader::ServiceAction& action,
  100. std::map<NsswitchReader::Status, NsswitchReader::Action>&
  101. in_out_parsed_behavior) {
  102. if (action.negated) {
  103. for (NsswitchReader::Status status :
  104. {NsswitchReader::Status::kSuccess, NsswitchReader::Status::kNotFound,
  105. NsswitchReader::Status::kUnavailable,
  106. NsswitchReader::Status::kTryAgain}) {
  107. if (status != action.status) {
  108. NsswitchReader::ServiceAction effective_action = {
  109. /*negated=*/false, status, action.action};
  110. if (!SetActionBehavior(effective_action, in_out_parsed_behavior))
  111. return false;
  112. }
  113. }
  114. } else {
  115. if (in_out_parsed_behavior.count(action.status) >= 1 &&
  116. in_out_parsed_behavior[action.status] != action.action) {
  117. return false;
  118. }
  119. in_out_parsed_behavior[action.status] = action.action;
  120. }
  121. return true;
  122. }
  123. // Helper to determine if `actions` match `expected_actions`, meaning `actions`
  124. // contains no unknown statuses or actions and for every expectation set in
  125. // `expected_actions`, the expected action matches the effective result from
  126. // `actions`.
  127. bool AreActionsCompatible(
  128. const std::vector<NsswitchReader::ServiceAction>& actions,
  129. const std::map<NsswitchReader::Status, NsswitchReader::Action>
  130. expected_actions) {
  131. std::map<NsswitchReader::Status, NsswitchReader::Action> parsed_behavior;
  132. for (const NsswitchReader::ServiceAction& action : actions) {
  133. if (action.status == NsswitchReader::Status::kUnknown ||
  134. action.action == NsswitchReader::Action::kUnknown) {
  135. return false;
  136. }
  137. if (!SetActionBehavior(action, parsed_behavior))
  138. return false;
  139. }
  140. // Default behavior if not configured.
  141. if (parsed_behavior.count(NsswitchReader::Status::kSuccess) == 0)
  142. parsed_behavior[NsswitchReader::Status::kSuccess] =
  143. NsswitchReader::Action::kReturn;
  144. if (parsed_behavior.count(NsswitchReader::Status::kNotFound) == 0)
  145. parsed_behavior[NsswitchReader::Status::kNotFound] =
  146. NsswitchReader::Action::kContinue;
  147. if (parsed_behavior.count(NsswitchReader::Status::kUnavailable) == 0)
  148. parsed_behavior[NsswitchReader::Status::kUnavailable] =
  149. NsswitchReader::Action::kContinue;
  150. if (parsed_behavior.count(NsswitchReader::Status::kTryAgain) == 0)
  151. parsed_behavior[NsswitchReader::Status::kTryAgain] =
  152. NsswitchReader::Action::kContinue;
  153. for (const std::pair<const NsswitchReader::Status, NsswitchReader::Action>&
  154. expected : expected_actions) {
  155. if (parsed_behavior[expected.first] != expected.second)
  156. return false;
  157. }
  158. return true;
  159. }
  160. // These values are emitted in metrics. Entries should not be renumbered and
  161. // numeric values should never be reused. (See NsswitchIncompatibleReason in
  162. // tools/metrics/histograms/enums.xml.)
  163. enum class IncompatibleNsswitchReason {
  164. kFilesMissing = 0,
  165. kMultipleFiles = 1,
  166. kBadFilesActions = 2,
  167. kDnsMissing = 3,
  168. kBadDnsActions = 4,
  169. kBadMdnsMinimalActions = 5,
  170. kBadOtherServiceActions = 6,
  171. kUnknownService = 7,
  172. kIncompatibleService = 8,
  173. kMaxValue = kIncompatibleService
  174. };
  175. void RecordIncompatibleNsswitchReason(
  176. IncompatibleNsswitchReason reason,
  177. absl::optional<NsswitchReader::Service> service_token) {
  178. base::UmaHistogramEnumeration("Net.DNS.DnsConfig.Nsswitch.IncompatibleReason",
  179. reason);
  180. if (service_token) {
  181. base::UmaHistogramEnumeration(
  182. "Net.DNS.DnsConfig.Nsswitch.IncompatibleService",
  183. service_token.value());
  184. }
  185. }
  186. bool IsNsswitchConfigCompatible(
  187. const std::vector<NsswitchReader::ServiceSpecification>& nsswitch_hosts) {
  188. bool files_found = false;
  189. for (const NsswitchReader::ServiceSpecification& specification :
  190. nsswitch_hosts) {
  191. switch (specification.service) {
  192. case NsswitchReader::Service::kUnknown:
  193. RecordIncompatibleNsswitchReason(
  194. IncompatibleNsswitchReason::kUnknownService, specification.service);
  195. return false;
  196. case NsswitchReader::Service::kFiles:
  197. if (files_found) {
  198. RecordIncompatibleNsswitchReason(
  199. IncompatibleNsswitchReason::kMultipleFiles,
  200. specification.service);
  201. return false;
  202. }
  203. files_found = true;
  204. // Chrome will use the result on HOSTS hit and otherwise continue to
  205. // DNS. `kFiles` entries must match that behavior to be compatible.
  206. if (!AreActionsCompatible(specification.actions,
  207. {{NsswitchReader::Status::kSuccess,
  208. NsswitchReader::Action::kReturn},
  209. {NsswitchReader::Status::kNotFound,
  210. NsswitchReader::Action::kContinue},
  211. {NsswitchReader::Status::kUnavailable,
  212. NsswitchReader::Action::kContinue},
  213. {NsswitchReader::Status::kTryAgain,
  214. NsswitchReader::Action::kContinue}})) {
  215. RecordIncompatibleNsswitchReason(
  216. IncompatibleNsswitchReason::kBadFilesActions,
  217. specification.service);
  218. return false;
  219. }
  220. break;
  221. case NsswitchReader::Service::kDns:
  222. if (!files_found) {
  223. RecordIncompatibleNsswitchReason(
  224. IncompatibleNsswitchReason::kFilesMissing,
  225. /*service_token=*/absl::nullopt);
  226. return false;
  227. }
  228. // Chrome will always stop if DNS finds a result or will otherwise
  229. // fallback to the system resolver (and get whatever behavior is
  230. // configured in nsswitch.conf), so the only compatibility requirement
  231. // is that `kDns` entries are configured to return on success.
  232. if (!AreActionsCompatible(specification.actions,
  233. {{NsswitchReader::Status::kSuccess,
  234. NsswitchReader::Action::kReturn}})) {
  235. RecordIncompatibleNsswitchReason(
  236. IncompatibleNsswitchReason::kBadDnsActions,
  237. specification.service);
  238. return false;
  239. }
  240. // Ignore any entries after `kDns` because Chrome will fallback to the
  241. // system resolver if a result was not found in DNS.
  242. return true;
  243. case NsswitchReader::Service::kMdns:
  244. case NsswitchReader::Service::kMdns4:
  245. case NsswitchReader::Service::kMdns6:
  246. case NsswitchReader::Service::kResolve:
  247. RecordIncompatibleNsswitchReason(
  248. IncompatibleNsswitchReason::kIncompatibleService,
  249. specification.service);
  250. return false;
  251. case NsswitchReader::Service::kMdnsMinimal:
  252. case NsswitchReader::Service::kMdns4Minimal:
  253. case NsswitchReader::Service::kMdns6Minimal:
  254. // Always compatible as long as `kUnavailable` is `kContinue` because
  255. // the service is expected to always result in `kUnavailable` for any
  256. // names Chrome would attempt to resolve (non-*.local names because
  257. // Chrome always delegates *.local names to the system resolver).
  258. if (!AreActionsCompatible(specification.actions,
  259. {{NsswitchReader::Status::kUnavailable,
  260. NsswitchReader::Action::kContinue}})) {
  261. RecordIncompatibleNsswitchReason(
  262. IncompatibleNsswitchReason::kBadMdnsMinimalActions,
  263. specification.service);
  264. return false;
  265. }
  266. break;
  267. case NsswitchReader::Service::kMyHostname:
  268. case NsswitchReader::Service::kNis:
  269. // Similar enough to Chrome behavior (or unlikely to matter for Chrome
  270. // resolutions) to be considered compatible unless the actions do
  271. // something very weird to skip remaining services without a result.
  272. if (!AreActionsCompatible(specification.actions,
  273. {{NsswitchReader::Status::kNotFound,
  274. NsswitchReader::Action::kContinue},
  275. {NsswitchReader::Status::kUnavailable,
  276. NsswitchReader::Action::kContinue},
  277. {NsswitchReader::Status::kTryAgain,
  278. NsswitchReader::Action::kContinue}})) {
  279. RecordIncompatibleNsswitchReason(
  280. IncompatibleNsswitchReason::kBadOtherServiceActions,
  281. specification.service);
  282. return false;
  283. }
  284. break;
  285. }
  286. }
  287. RecordIncompatibleNsswitchReason(IncompatibleNsswitchReason::kDnsMissing,
  288. /*service_token=*/absl::nullopt);
  289. return false;
  290. }
  291. } // namespace
  292. class DnsConfigServiceLinux::Watcher : public DnsConfigService::Watcher {
  293. public:
  294. explicit Watcher(DnsConfigServiceLinux& service)
  295. : DnsConfigService::Watcher(service) {}
  296. ~Watcher() override = default;
  297. Watcher(const Watcher&) = delete;
  298. Watcher& operator=(const Watcher&) = delete;
  299. bool Watch() override {
  300. CheckOnCorrectSequence();
  301. bool success = true;
  302. if (!resolv_watcher_.Watch(
  303. base::FilePath(kFilePathResolv),
  304. base::FilePathWatcher::Type::kNonRecursive,
  305. base::BindRepeating(&Watcher::OnResolvFilePathWatcherChange,
  306. base::Unretained(this)))) {
  307. LOG(ERROR) << "DNS config (resolv.conf) watch failed to start.";
  308. success = false;
  309. }
  310. if (!nsswitch_watcher_.Watch(
  311. base::FilePath(kFilePathNsswitch),
  312. base::FilePathWatcher::Type::kNonRecursive,
  313. base::BindRepeating(&Watcher::OnNsswitchFilePathWatcherChange,
  314. base::Unretained(this)))) {
  315. LOG(ERROR) << "DNS nsswitch.conf watch failed to start.";
  316. success = false;
  317. }
  318. if (!hosts_watcher_.Watch(
  319. base::FilePath(kFilePathHosts),
  320. base::FilePathWatcher::Type::kNonRecursive,
  321. base::BindRepeating(&Watcher::OnHostsFilePathWatcherChange,
  322. base::Unretained(this)))) {
  323. LOG(ERROR) << "DNS hosts watch failed to start.";
  324. success = false;
  325. }
  326. return success;
  327. }
  328. private:
  329. void OnResolvFilePathWatcherChange(const base::FilePath& path, bool error) {
  330. base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.FileChange", true);
  331. OnConfigChanged(!error);
  332. }
  333. void OnNsswitchFilePathWatcherChange(const base::FilePath& path, bool error) {
  334. base::UmaHistogramBoolean("Net.DNS.DnsConfig.Nsswitch.FileChange", true);
  335. OnConfigChanged(!error);
  336. }
  337. void OnHostsFilePathWatcherChange(const base::FilePath& path, bool error) {
  338. OnHostsChanged(!error);
  339. }
  340. base::FilePathWatcher resolv_watcher_;
  341. base::FilePathWatcher nsswitch_watcher_;
  342. base::FilePathWatcher hosts_watcher_;
  343. };
  344. // A SerialWorker that uses libresolv to initialize res_state and converts
  345. // it to DnsConfig.
  346. class DnsConfigServiceLinux::ConfigReader : public SerialWorker {
  347. public:
  348. explicit ConfigReader(DnsConfigServiceLinux& service,
  349. std::unique_ptr<ResolvReader> resolv_reader,
  350. std::unique_ptr<NsswitchReader> nsswitch_reader)
  351. : service_(&service),
  352. work_item_(std::make_unique<WorkItem>(std::move(resolv_reader),
  353. std::move(nsswitch_reader))) {
  354. // Allow execution on another thread; nothing thread-specific about
  355. // constructor.
  356. DETACH_FROM_SEQUENCE(sequence_checker_);
  357. }
  358. ~ConfigReader() override = default;
  359. ConfigReader(const ConfigReader&) = delete;
  360. ConfigReader& operator=(const ConfigReader&) = delete;
  361. std::unique_ptr<SerialWorker::WorkItem> CreateWorkItem() override {
  362. // Reuse same `WorkItem` to allow reuse of contained reader objects.
  363. DCHECK(work_item_);
  364. return std::move(work_item_);
  365. }
  366. bool OnWorkFinished(std::unique_ptr<SerialWorker::WorkItem>
  367. serial_worker_work_item) override {
  368. DCHECK(serial_worker_work_item);
  369. DCHECK(!work_item_);
  370. DCHECK(!IsCancelled());
  371. work_item_.reset(static_cast<WorkItem*>(serial_worker_work_item.release()));
  372. if (work_item_->dns_config_.has_value()) {
  373. service_->OnConfigRead(std::move(work_item_->dns_config_).value());
  374. return true;
  375. } else {
  376. LOG(WARNING) << "Failed to read DnsConfig.";
  377. return false;
  378. }
  379. }
  380. private:
  381. class WorkItem : public SerialWorker::WorkItem {
  382. public:
  383. WorkItem(std::unique_ptr<ResolvReader> resolv_reader,
  384. std::unique_ptr<NsswitchReader> nsswitch_reader)
  385. : resolv_reader_(std::move(resolv_reader)),
  386. nsswitch_reader_(std::move(nsswitch_reader)) {
  387. DCHECK(resolv_reader_);
  388. DCHECK(nsswitch_reader_);
  389. }
  390. void DoWork() override {
  391. base::ScopedBlockingCall scoped_blocking_call(
  392. FROM_HERE, base::BlockingType::MAY_BLOCK);
  393. {
  394. std::unique_ptr<ScopedResState> res = resolv_reader_->GetResState();
  395. if (res) {
  396. dns_config_ = ConvertResStateToDnsConfig(res->state());
  397. }
  398. }
  399. base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Read",
  400. dns_config_.has_value());
  401. if (!dns_config_.has_value())
  402. return;
  403. base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Valid",
  404. dns_config_->IsValid());
  405. base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Compatible",
  406. !dns_config_->unhandled_options);
  407. // Override `fallback_period` value to match default setting on
  408. // Windows.
  409. dns_config_->fallback_period = kDnsDefaultFallbackPeriod;
  410. if (dns_config_ && !dns_config_->unhandled_options) {
  411. std::vector<NsswitchReader::ServiceSpecification> nsswitch_hosts =
  412. nsswitch_reader_->ReadAndParseHosts();
  413. base::UmaHistogramCounts100("Net.DNS.DnsConfig.Nsswitch.NumServices",
  414. nsswitch_hosts.size());
  415. dns_config_->unhandled_options =
  416. !IsNsswitchConfigCompatible(nsswitch_hosts);
  417. base::UmaHistogramBoolean("Net.DNS.DnsConfig.Nsswitch.Compatible",
  418. !dns_config_->unhandled_options);
  419. base::UmaHistogramBoolean(
  420. "Net.DNS.DnsConfig.Nsswitch.NisServiceInHosts",
  421. base::Contains(nsswitch_hosts, NsswitchReader::Service::kNis,
  422. &NsswitchReader::ServiceSpecification::service));
  423. }
  424. }
  425. private:
  426. friend class ConfigReader;
  427. absl::optional<DnsConfig> dns_config_;
  428. std::unique_ptr<ResolvReader> resolv_reader_;
  429. std::unique_ptr<NsswitchReader> nsswitch_reader_;
  430. };
  431. // Raw pointer to owning DnsConfigService.
  432. const raw_ptr<DnsConfigServiceLinux> service_;
  433. // Null while the `WorkItem` is running on the `ThreadPool`.
  434. std::unique_ptr<WorkItem> work_item_;
  435. };
  436. DnsConfigServiceLinux::DnsConfigServiceLinux()
  437. : DnsConfigService(kFilePathHosts) {
  438. // Allow constructing on one thread and living on another.
  439. DETACH_FROM_SEQUENCE(sequence_checker_);
  440. }
  441. DnsConfigServiceLinux::~DnsConfigServiceLinux() {
  442. if (config_reader_)
  443. config_reader_->Cancel();
  444. }
  445. void DnsConfigServiceLinux::ReadConfigNow() {
  446. if (!config_reader_)
  447. CreateReader();
  448. config_reader_->WorkNow();
  449. }
  450. bool DnsConfigServiceLinux::StartWatching() {
  451. CreateReader();
  452. watcher_ = std::make_unique<Watcher>(*this);
  453. return watcher_->Watch();
  454. }
  455. void DnsConfigServiceLinux::CreateReader() {
  456. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  457. DCHECK(!config_reader_);
  458. DCHECK(resolv_reader_);
  459. DCHECK(nsswitch_reader_);
  460. config_reader_ = std::make_unique<ConfigReader>(
  461. *this, std::move(resolv_reader_), std::move(nsswitch_reader_));
  462. }
  463. } // namespace internal
  464. // static
  465. std::unique_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
  466. return std::make_unique<internal::DnsConfigServiceLinux>();
  467. }
  468. } // namespace net