Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1636)

Unified Diff: net/dns/dns_config_service_posix.cc

Issue 7518028: DnsConfigService and a posix implementation (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: delinted Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/dns/dns_config_service_posix.cc
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a637584f770259d0668561ac4e0605c6d1f78511
--- /dev/null
+++ b/net/dns/dns_config_service_posix.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <resolv.h>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/scoped_ptr.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
+#include "net/dns/dns_config_service.h"
+
+namespace net {
+
+namespace {
+
+using base::files::FilePathWatcher;
+
+// fills in |dns_config| from |res|
+void ConfigFromResolver(const struct __res_state& res, DnsConfig* dns_config) {
+ CHECK(dns_config != NULL);
+ DCHECK(res.options & RES_INIT);
+ dns_config->nameservers.clear();
+ for (int i = 0; i < res.nscount; ++i) {
+ IPEndPoint ipe;
+ if (ipe.FromSockAddr(
cbentzel 2011/08/04 18:09:27 You may want to handle IPv6 DNS servers here - or
szym 2011/08/04 18:55:10 I'll look into this.
+ reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
+ sizeof res.nsaddr_list[i])) {
+ dns_config->nameservers.push_back(ipe);
+ } else {
+ NOTREACHED() << "could not convert nsaddr";
cbentzel 2011/08/04 18:09:27 I think this function should return a boolean, and
szym 2011/08/04 18:55:10 Right. I was swayed by IPEndPoint::FromSockAddr wh
+ }
+ }
+
+ dns_config->search.clear();
+ for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
+ dns_config->search.push_back(std::string(res.dnsrch[i]));
+ }
+
+ dns_config->domain = res.defdname; // use only if |search| is empty
cbentzel 2011/08/04 18:09:27 This should be conditional on dns_config->search.e
szym 2011/08/04 18:55:10 Even though defdname stores all the strings in dns
cbentzel 2011/08/04 20:01:21 That seems good.
+ dns_config->ndots = res.ndots;
+ dns_config->timeout = res.retrans;
cbentzel 2011/08/04 18:09:27 I wish there was a way to distinguish whether thes
szym 2011/08/04 18:55:10 I can't think of a way to do that without parsing
cbentzel 2011/08/04 20:01:21 Yeah, agree.
+ dns_config->attempts = res.retry;
+ dns_config->rotate = res.options & RES_ROTATE;
+ dns_config->edns0 = res.options & RES_USE_EDNS0;
cbentzel 2011/08/04 18:09:27 It's probably good to pull in all of these things.
szym 2011/08/04 18:55:10 I'm not sure I understand. Should I remove those o
cbentzel 2011/08/04 20:01:21 No, keep them. I was musing about what we need to
+}
+
+// fills in |dns_config| using res_ninit, returns true on success
+bool ReadConfig(DnsConfig* dns_config) {
+ // res_ninit is available on Linux (glibc 2+), Mac OS X, and Android.
+ // However, it might not be thread-safe on OpenBSD.
cbentzel 2011/08/04 18:09:27 I think it would be good to limit to only one res_
szym 2011/08/04 18:55:10 Done.
+ struct __res_state res;
+ if (res_ninit(&res) != 0)
+ return false;
+
+ ConfigFromResolver(res, dns_config);
+ return true;
+}
+
+class DnsConfigServicePosix : public DnsConfigService {
+ public:
+ DnsConfigServicePosix() {}
+ virtual ~DnsConfigServicePosix() {
+ // we must be destroyed on the same thread that called Watch
+ if (watcher_delegate_.get())
+ watcher_delegate_->Cancel();
+ }
+ virtual bool Watch(DnsConfigService::Delegate* delegate) OVERRIDE;
+
+ private:
+ // FilePathWatcher::Delegate is refcounted, so we separate it from the Service
+ // We'll also use it on the WorkerPool
+ class WatcherDelegate : public FilePathWatcher::Delegate {
+ public:
+ explicit WatcherDelegate(DnsConfigService::Delegate* delegate)
+ : message_loop_(base::MessageLoopProxy::CreateForCurrentThread()),
+ delegate_(delegate) {}
+
+ // FilePathWatcher::Delegate interface
+ virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ if (!delegate_)
+ return;
+ if (!base::WorkerPool::PostTask(FROM_HERE, base::Bind(
+ &WatcherDelegate::DoRead, this), false)) {
+ delegate_->OnConfigError();
cbentzel 2011/08/04 18:09:27 Yeah, I don't think you need to escalate this to t
szym 2011/08/04 18:55:10 Done.
+ }
+ }
+
+ virtual void OnFilePathError(const FilePath& path) OVERRIDE {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ if (!delegate_)
+ return;
+ delegate_->OnConfigError();
+ }
+
+ // called on the worker thread
+ // reads DnsConfig and posts OnResultAvailable to |message_loop_|
+ void DoRead() {
+ // on the WorkerPool
+ DnsConfig config;
+ bool success = ReadConfig(&config);
+ message_loop_->PostTask(FROM_HERE, base::Bind(
cbentzel 2011/08/04 18:09:27 If it wasn't successful, I think we should just sk
szym 2011/08/04 18:55:10 Can I have a stupid while(!the_thing_to_do); or sh
cbentzel 2011/08/04 20:01:21 Well, if someone made a typo in resolv.conf and th
+ &WatcherDelegate::OnResultAvailable, this, config, success));
+ }
+
+ // called on the delegate's thread
+ // communicates result from DnsConfigReader to the delegate
+ void OnResultAvailable(const DnsConfig &config, bool success) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ if (!delegate_)
+ return;
+ if (!success) {
+ delegate_->OnConfigError();
+ } else if (config != last_config_) {
+ delegate_->OnConfigChanged(config);
+ last_config_ = config;
+ }
+ }
+
+ void Cancel() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ delegate_ = NULL;
+ }
+
+ private:
+ virtual ~WatcherDelegate() {}
+
+ // message loop for the thread on which Watch is called
+ // this should be an IO loop
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ DnsConfigService::Delegate* delegate_;
+ DnsConfig last_config_;
+ };
+
+ scoped_ptr<FilePathWatcher> resolv_file_watcher_;
+ scoped_refptr<WatcherDelegate> watcher_delegate_;
+};
cbentzel 2011/08/04 18:09:27 DISALLOW_COPY_AND_ASSIGN
szym 2011/08/04 18:55:10 Done.
+
+bool DnsConfigServicePosix::Watch(DnsConfigService::Delegate* delegate) {
+ DCHECK(!resolv_file_watcher_.get());
+ DCHECK(!watcher_delegate_.get());
+ DCHECK(MessageLoopForIO::current());
+
+ // start FilePathWatcher
+ FilePath path(FILE_PATH_LITERAL("/etc/resolv.conf"));
cbentzel 2011/08/04 18:24:56 Is _PATH_RESCONF defined correctly in the differen
szym 2011/08/04 18:55:10 I'll add a #ifndef _PATH_RESCONF #define _PATH_RES
+ watcher_delegate_ = new WatcherDelegate(delegate);
+ resolv_file_watcher_.reset(new FilePathWatcher());
+ bool result = resolv_file_watcher_->Watch(path, watcher_delegate_);
+ // make the initial read
+ watcher_delegate_->OnFilePathChanged(path);
+ return result;
+}
+
+} // namespace
+
+DnsConfigService* DnsConfigService::CreateSystemService() {
+ return new DnsConfigServicePosix();
+}
cbentzel 2011/08/04 18:09:27 Nit: newline between here and // namespace net.
szym 2011/08/04 18:55:10 Done.
+} // namespace net
+

Powered by Google App Engine
This is Rietveld 408576698