shill: resolver: Use (even) smaller DNS timeout

Use the new glibc facility for sub-second timeouts, and choose
300 milliseconds for the timeout.  However, only do this by
default for Ethernet and WiFi networks, since VPN networks
in particular have trouble with this configuration (due to many
name servers and search domains, as well as the additional
latency inherent to such networks).  Also, increase the number
of attempts, so we still spend a reasonable amount of time
overall waiting for a DNS response.

Provide a means to change which technologies are setup in this
manner.

BUG=chromium-os:29124
TEST=Manual: Install connection manager, verify via strace that
gethostbyname now waits 300 ms, and retries 15 times per trial (*).
Ensure Chromium continues to work correctly under light loads.
Also, connect via Verizon and VPN and ensure DNS parameters
are back to the long timeout.
List manager properties and ensure it says "ethernet,wifi" for
ShortDNSTimeoutTechnologies and that this is saved out to the
profile.
New unit tests.
CQ-DEPENDS=Ib9ffc59bbfcd5bf3f57d146965c5a43a936348f8

*: Each trial consists of a nameserver / IP address pair, so
for example, if we have IPv6 connectivity and have two "server"
entries in resolv.conf, we first do 30 tries of IPv6 requests,
alternating between the two servers, then another 30 alternating
IPv4 requests between them.  This was tested by intentionally
making the DNS server unreachable and instrumenting the gethostbyname
request via strace.

Change-Id: Idd331b4a9fcf96d457ab9959537aefcb86328e12
Reviewed-on: https://gerrit.chromium.org/gerrit/26493
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/connection.cc b/connection.cc
index d258dd7..8bf3153 100644
--- a/connection.cc
+++ b/connection.cc
@@ -63,7 +63,8 @@
 Connection::Connection(int interface_index,
                        const std::string& interface_name,
                        Technology::Identifier technology,
-                       const DeviceInfo *device_info)
+                       const DeviceInfo *device_info,
+                       bool is_short_dns_timeout_enabled)
     : weak_ptr_factory_(this),
       is_default_(false),
       has_broadcast_domain_(false),
@@ -78,6 +79,7 @@
           // Connection owns a single instance of |lower_binder_| so it's safe
           // to use an Unretained callback.
           Bind(&Connection::OnLowerDisconnect, Unretained(this))),
+      dns_timeout_parameters_(Resolver::kDefaultTimeout),
       device_info_(device_info),
       resolver_(Resolver::GetInstance()),
       routing_table_(RoutingTable::GetInstance()),
@@ -85,6 +87,9 @@
   SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
                       << interface_name << ", "
                       << Technology::NameFromIdentifier(technology) << ")";
+  if (is_short_dns_timeout_enabled) {
+    dns_timeout_parameters_ = Resolver::kShortTimeout;
+  }
 }
 
 Connection::~Connection() {
@@ -178,7 +183,7 @@
   ipconfig_rpc_identifier_ = config->GetRpcIdentifier();
 
   if (is_default_) {
-    resolver_->SetDNSFromIPConfig(config);
+    resolver_->SetDNSFromIPConfig(config, dns_timeout_parameters_);
   }
 
   local_ = local;
@@ -199,7 +204,8 @@
   is_default_ = is_default;
 
   if (is_default) {
-    resolver_->SetDNSFromLists(dns_servers_, dns_domain_search_);
+    resolver_->SetDNSFromLists(dns_servers_, dns_domain_search_,
+                               dns_timeout_parameters_);
     DeviceRefPtr device = device_info_->GetDevice(interface_index_);
     if (device) {
       device->RequestPortalDetection();
diff --git a/connection.h b/connection.h
index 76d99a9..ba0c7cb 100644
--- a/connection.h
+++ b/connection.h
@@ -16,13 +16,13 @@
 #include "shill/ip_address.h"
 #include "shill/ipconfig.h"
 #include "shill/refptr_types.h"
+#include "shill/resolver.h"
 #include "shill/technology.h"
 
 namespace shill {
 
 class DeviceInfo;
 class RTNLHandler;
-class Resolver;
 class RoutingTable;
 struct RoutingTableEntry;
 
@@ -65,7 +65,8 @@
   Connection(int interface_index,
              const std::string &interface_name,
              Technology::Identifier technology_,
-             const DeviceInfo *device_info);
+             const DeviceInfo *device_info,
+             bool is_short_dns_timeout_enabled);
 
   // Add the contents of an IPConfig reference to the list of managed state.
   // This will replace all previous state for this address family.
@@ -157,6 +158,9 @@
   // devices.
   std::deque<Binder *> binders_;
 
+  // Keep track of what sort of DNS timeout to use for this connection.
+  Resolver::TimeoutParameters dns_timeout_parameters_;
+
   // Store cached copies of singletons for speed/ease of testing
   const DeviceInfo *device_info_;
   Resolver *resolver_;
diff --git a/connection_unittest.cc b/connection_unittest.cc
index e99f55a..735eb43 100644
--- a/connection_unittest.cc
+++ b/connection_unittest.cc
@@ -64,7 +64,8 @@
             kTestDeviceInterfaceIndex0,
             kTestDeviceName0,
             Technology::kUnknown,
-            device_info_.get())),
+            device_info_.get(),
+            false)),
         ipconfig_(new IPConfig(&control_, kTestDeviceName0)),
         local_address_(IPAddress::kFamilyIPv4),
         broadcast_address_(IPAddress::kFamilyIPv4),
@@ -155,7 +156,8 @@
     ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
                                                kTestDeviceName0,
                                                Technology::kUnknown,
-                                               device_info_.get()));
+                                               device_info_.get(),
+                                               false));
     ReplaceSingletons(connection);
     return connection;
   }
@@ -236,7 +238,8 @@
                                                GetDefaultMetric()));
   EXPECT_CALL(resolver_, SetDNSFromLists(
       ipconfig_->properties().dns_servers,
-      ipconfig_->properties().domain_search));
+      ipconfig_->properties().domain_search,
+      Resolver::kDefaultTimeout));
 
   scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
       &control_,
@@ -348,7 +351,8 @@
   EXPECT_CALL(routing_table_, SetDefaultMetric(kTestDeviceInterfaceIndex0,
                                                GetDefaultMetric()));
   vector<string> empty_list;
-  EXPECT_CALL(resolver_, SetDNSFromLists(empty_list, empty_list));
+  EXPECT_CALL(resolver_, SetDNSFromLists(empty_list, empty_list,
+                                         Resolver::kDefaultTimeout));
   scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
       &control_,
       reinterpret_cast<EventDispatcher *>(NULL),
@@ -382,11 +386,51 @@
               ConfigureRoutes(kTestDeviceInterfaceIndex0,
                               ipconfig_,
                               GetDefaultMetric()));
-  EXPECT_CALL(resolver_, SetDNSFromIPConfig(ipconfig_));
+  EXPECT_CALL(resolver_, SetDNSFromIPConfig(ipconfig_,
+                                            Resolver::kDefaultTimeout));
 
   connection_->UpdateFromIPConfig(ipconfig_);
 }
 
+TEST_F(ConnectionTest, AddConfigShortTimeout) {
+  ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex0,
+                                             kTestDeviceName0,
+                                             Technology::kUnknown,
+                                             device_info_.get(),
+                                             true));
+  ReplaceSingletons(connection);
+  EXPECT_CALL(*device_info_, HasOtherAddress(_, _)).WillOnce(Return(false));
+  EXPECT_CALL(rtnl_handler_, AddInterfaceAddress(_, _, _, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(routing_table_, SetDefaultRoute(_, _, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(routing_table_, ConfigureRoutes(_, _, _))
+      .WillRepeatedly(Return(true));
+  connection->UpdateFromIPConfig(ipconfig_);
+  EXPECT_CALL(routing_table_, SetDefaultMetric(_, _));
+  EXPECT_CALL(resolver_, SetDNSFromLists(
+      ipconfig_->properties().dns_servers,
+      ipconfig_->properties().domain_search,
+      Resolver::kShortTimeout));
+  scoped_refptr<MockDevice> device(new StrictMock<MockDevice>(
+      &control_,
+      reinterpret_cast<EventDispatcher *>(NULL),
+      reinterpret_cast<Metrics *>(NULL),
+      reinterpret_cast<Manager *>(NULL),
+      kTestDeviceName0,
+      string(),
+      kTestDeviceInterfaceIndex0));
+  EXPECT_CALL(*device_info_, GetDevice(_)).WillOnce(Return(device));
+  EXPECT_CALL(*device.get(), RequestPortalDetection()).WillOnce(Return(true));
+  EXPECT_CALL(routing_table_, FlushCache()).WillOnce(Return(true));
+  connection->SetIsDefault(true);
+  EXPECT_CALL(*device_info_, HasOtherAddress(_, _)).WillOnce(Return(false));
+  EXPECT_CALL(resolver_, SetDNSFromIPConfig(ipconfig_,
+                                            Resolver::kShortTimeout));
+  connection->UpdateFromIPConfig(ipconfig_);
+  AddDestructorExpectations();
+}
+
 TEST_F(ConnectionTest, HasOtherAddress) {
   EXPECT_CALL(*device_info_,
               HasOtherAddress(kTestDeviceInterfaceIndex0,
@@ -443,7 +487,8 @@
   ConnectionRefPtr connection(new Connection(kTestDeviceInterfaceIndex1,
                                              kTestDeviceName1,
                                              Technology::kUnknown,
-                                             device_info_.get()));
+                                             device_info_.get(),
+                                             false));
   connection->resolver_ = &resolver_;
   connection->routing_table_ = &routing_table_;
   connection->rtnl_handler_ = &rtnl_handler_;
diff --git a/default_profile.cc b/default_profile.cc
index 7708d57..0b7bdbd 100644
--- a/default_profile.cc
+++ b/default_profile.cc
@@ -15,6 +15,7 @@
 #include "shill/control_interface.h"
 #include "shill/manager.h"
 #include "shill/portal_detector.h"
+#include "shill/resolver.h"
 #include "shill/store_interface.h"
 
 using std::string;
@@ -40,6 +41,9 @@
 // static
 const char DefaultProfile::kStoragePortalCheckInterval[] =
     "PortalCheckInterval";
+// static
+const char DefaultProfile::kStorageShortDNSTimeoutTechnologies[] =
+    "ShortDNSTimeoutTechnologies";
 
 DefaultProfile::DefaultProfile(ControlInterface *control,
                                Manager *manager,
@@ -63,6 +67,8 @@
                              &manager_props.portal_url);
   store->RegisterConstInt32(shill::kPortalCheckIntervalProperty,
                             &manager_props.portal_check_interval_seconds);
+  store->RegisterConstString(shill::kShortDNSTimeoutTechnologiesProperty,
+                             &manager_props.short_dns_timeout_technologies);
 }
 
 DefaultProfile::~DefaultProfile() {}
@@ -90,6 +96,12 @@
     manager_props->portal_check_interval_seconds =
         PortalDetector::kDefaultCheckIntervalSeconds;
   }
+  if (!storage()->GetString(kStorageId,
+                            kStorageShortDNSTimeoutTechnologies,
+                            &manager_props->short_dns_timeout_technologies)) {
+    manager_props->short_dns_timeout_technologies =
+        Resolver::kDefaultShortTimeoutTechnologies;
+  }
   return true;
 }
 
@@ -122,6 +134,9 @@
   storage()->SetString(kStorageId,
                        kStoragePortalCheckInterval,
                        base::IntToString(props_.portal_check_interval_seconds));
+  storage()->SetString(kStorageId,
+                       kStorageShortDNSTimeoutTechnologies,
+                       props_.short_dns_timeout_technologies);
   return Profile::Save();
 }
 
diff --git a/default_profile.h b/default_profile.h
index 4c3091f..894c06c 100644
--- a/default_profile.h
+++ b/default_profile.h
@@ -71,6 +71,7 @@
   static const char kStorageOfflineMode[];
   static const char kStoragePortalCheckInterval[];
   static const char kStoragePortalURL[];
+  static const char kStorageShortDNSTimeoutTechnologies[];
 
   const FilePath storage_path_;
   const std::string profile_id_;
diff --git a/default_profile_unittest.cc b/default_profile_unittest.cc
index adbe161..4271401 100644
--- a/default_profile_unittest.cc
+++ b/default_profile_unittest.cc
@@ -22,6 +22,7 @@
 #include "shill/mock_store.h"
 #include "shill/portal_detector.h"
 #include "shill/property_store_unittest.h"
+#include "shill/resolver.h"
 
 using std::map;
 using std::string;
@@ -131,6 +132,11 @@
                         DefaultProfile::kStoragePortalCheckInterval,
                         "0"))
       .WillOnce(Return(true));
+  EXPECT_CALL(*storage.get(),
+              SetString(DefaultProfile::kStorageId,
+                        DefaultProfile::kStorageShortDNSTimeoutTechnologies,
+                        ""))
+      .WillOnce(Return(true));
   EXPECT_CALL(*storage.get(), Flush()).WillOnce(Return(true));
 
   EXPECT_CALL(*device_.get(), Save(storage.get())).Times(0);
@@ -169,6 +175,11 @@
                         DefaultProfile::kStoragePortalCheckInterval,
                         _))
       .WillOnce(Return(false));
+  EXPECT_CALL(*storage.get(),
+              GetString(DefaultProfile::kStorageId,
+                        DefaultProfile::kStorageShortDNSTimeoutTechnologies,
+                        _))
+      .WillOnce(Return(false));
   profile_->set_storage(storage.release());
 
   ASSERT_TRUE(profile_->LoadManagerProperties(&manager_props));
@@ -180,6 +191,8 @@
   EXPECT_EQ(PortalDetector::kDefaultURL, manager_props.portal_url);
   EXPECT_EQ(PortalDetector::kDefaultCheckIntervalSeconds,
             manager_props.portal_check_interval_seconds);
+  EXPECT_EQ(Resolver::kDefaultShortTimeoutTechnologies,
+            manager_props.short_dns_timeout_technologies);
 }
 
 TEST_F(DefaultProfileTest, LoadManagerProperties) {
@@ -215,6 +228,13 @@
                         _))
       .WillOnce(DoAll(SetArgumentPointee<2>(portal_check_interval_string),
                       Return(true)));
+  const string short_dns_timeout_technologies("wimax,cellular");
+  EXPECT_CALL(*storage.get(),
+              GetString(DefaultProfile::kStorageId,
+                        DefaultProfile::kStorageShortDNSTimeoutTechnologies,
+                        _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(short_dns_timeout_technologies),
+                      Return(true)));
   profile_->set_storage(storage.release());
 
   Manager::Properties manager_props;
@@ -226,6 +246,8 @@
   EXPECT_EQ(portal_url, manager_props.portal_url);
   EXPECT_EQ(portal_check_interval_int,
             manager_props.portal_check_interval_seconds);
+  EXPECT_EQ(short_dns_timeout_technologies,
+            manager_props.short_dns_timeout_technologies);
 }
 
 TEST_F(DefaultProfileTest, GetStoragePath) {
diff --git a/device.cc b/device.cc
index a654811..4f0851f 100644
--- a/device.cc
+++ b/device.cc
@@ -471,10 +471,12 @@
 void Device::CreateConnection() {
   SLOG(Device, 2) << __func__;
   if (!connection_.get()) {
-    connection_ = new Connection(interface_index_,
-                                 link_name_,
-                                 technology_,
-                                 manager_->device_info());
+    connection_ = new Connection(
+        interface_index_,
+        link_name_,
+        technology_,
+        manager_->device_info(),
+        manager_->IsTechnologyShortDNSTimeoutEnabled(technology_));
   }
 }
 
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
index 3ce8db4..1e003bc 100644
--- a/doc/manager-api.txt
+++ b/doc/manager-api.txt
@@ -406,6 +406,13 @@
 			Note: Currently shill reports the entire service
 			list, unfiltered.
 
+		string ShortDNSTimeoutTechnologies [readwrite]
+
+			The list of technologies for which a short DNS
+			timeout will be used, in order to improve retry
+			performance.  This is a comma-separated string;
+			e.g. "wifi,wimax,vpn".
+
 		string State [readonly]
 
 			The global connection state of a system. Possible
diff --git a/ipconfig.h b/ipconfig.h
index 9e063b6..21def33 100644
--- a/ipconfig.h
+++ b/ipconfig.h
@@ -125,6 +125,7 @@
   FRIEND_TEST(IPConfigTest, UpdateProperties);
   FRIEND_TEST(ResolverTest, Empty);
   FRIEND_TEST(ResolverTest, NonEmpty);
+  FRIEND_TEST(ResolverTest, ShortTimeout);
   FRIEND_TEST(RoutingTableTest, ConfigureRoutes);
   FRIEND_TEST(RoutingTableTest, RouteAddDelete);
   FRIEND_TEST(RoutingTableTest, RouteDeleteForeign);
diff --git a/manager.cc b/manager.cc
index b4e4a5b..1b4aaf4 100644
--- a/manager.cc
+++ b/manager.cc
@@ -123,6 +123,8 @@
                                          &Manager::EnumerateAvailableServices);
   HelpRegisterConstDerivedRpcIdentifiers(flimflam::kServiceWatchListProperty,
                                          &Manager::EnumerateWatchedServices);
+  store_.RegisterString(shill::kShortDNSTimeoutTechnologiesProperty,
+                        &props_.short_dns_timeout_technologies);
 
   // Set default technology order "by hand", to avoid invoking side
   // effects of SetTechnologyOrder.
@@ -531,6 +533,18 @@
   return service->profile() == ephemeral_profile_;
 }
 
+bool Manager::IsTechnologyShortDNSTimeoutEnabled(
+    Technology::Identifier tech) const {
+  Error error;
+  vector<Technology::Identifier> short_dns_technologies;
+  return Technology::GetTechnologyVectorFromString(
+      props_.short_dns_timeout_technologies,
+      &short_dns_technologies,
+      &error) &&
+      std::find(short_dns_technologies.begin(), short_dns_technologies.end(),
+                tech) != short_dns_technologies.end();
+}
+
 const ProfileRefPtr &Manager::ActiveProfile() const {
   DCHECK_NE(profiles_.size(), 0U);
   return profiles_.back();
diff --git a/manager.h b/manager.h
index 3bc846c..e0abdeb 100644
--- a/manager.h
+++ b/manager.h
@@ -57,6 +57,9 @@
     // Whether to ARP for the default gateway in the DHCP client after
     // acquiring a lease.
     bool arp_gateway;
+    // Comma separated list of technologies on which to use a short DNS
+    // timeout to improve performance.
+    std::string short_dns_timeout_technologies;
   };
 
   Manager(ControlInterface *control_interface,
@@ -175,6 +178,9 @@
   // Return whether a service belongs to the ephemeral profile.
   virtual bool IsServiceEphemeral(const ServiceConstRefPtr &service) const;
 
+  // Return whether a technology is enabled for using short DNS timeouts.
+  bool IsTechnologyShortDNSTimeoutEnabled(Technology::Identifier tech) const;
+
   std::string CalculateState(Error *error);
 
   virtual int GetPortalCheckInterval() const {
diff --git a/mock_connection.cc b/mock_connection.cc
index 4648fa4..8de2415 100644
--- a/mock_connection.cc
+++ b/mock_connection.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,7 +9,7 @@
 namespace shill {
 
 MockConnection::MockConnection(const DeviceInfo *device_info)
-    : Connection(0, std::string(), Technology::kUnknown, device_info) {}
+    : Connection(0, std::string(), Technology::kUnknown, device_info, false) {}
 
 MockConnection::~MockConnection() {}
 
diff --git a/mock_resolver.h b/mock_resolver.h
index b787dca..5850ec5 100644
--- a/mock_resolver.h
+++ b/mock_resolver.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,10 +20,12 @@
   MockResolver();
   virtual ~MockResolver();
 
-  MOCK_METHOD1(SetDNSFromIPConfig, bool(const IPConfigRefPtr &ipconfig));
-  MOCK_METHOD2(SetDNSFromLists,
+  MOCK_METHOD2(SetDNSFromIPConfig, bool(const IPConfigRefPtr &ipconfig,
+                                        TimeoutParameters timeout));
+  MOCK_METHOD3(SetDNSFromLists,
                bool(const std::vector<std::string> &dns_servers,
-                    const std::vector<std::string> &domain_search));
+                    const std::vector<std::string> &domain_search,
+                    TimeoutParameters timeout));
   MOCK_METHOD0(ClearDNS, bool());
 
  private:
diff --git a/resolver.cc b/resolver.cc
index b0bc41c..7cbe6d1 100644
--- a/resolver.cc
+++ b/resolver.cc
@@ -24,6 +24,12 @@
 base::LazyInstance<Resolver> g_resolver = LAZY_INSTANCE_INITIALIZER;
 }  // namespace
 
+const char Resolver::kDefaultShortTimeoutTechnologies[] = "ethernet,wifi";
+const char Resolver::kDefaultTimeoutOptions[] =
+    "options single-request timeout:1 attempts:3";
+const char Resolver::kShortTimeoutOptions[] =
+    "options single-request timeout-ms:300 attempts:15";
+
 Resolver::Resolver() {}
 
 Resolver::~Resolver() {}
@@ -32,18 +38,20 @@
   return g_resolver.Pointer();
 }
 
-bool Resolver::SetDNSFromIPConfig(const IPConfigRefPtr &ipconfig) {
+bool Resolver::SetDNSFromIPConfig(const IPConfigRefPtr &ipconfig,
+                                  TimeoutParameters timeout) {
   SLOG(Resolver, 2) << __func__;
 
   CHECK(!path_.empty());
 
   const IPConfig::Properties &props = ipconfig->properties();
 
-  return SetDNSFromLists(props.dns_servers, props.domain_search);
+  return SetDNSFromLists(props.dns_servers, props.domain_search, timeout);
 }
 
 bool Resolver::SetDNSFromLists(const std::vector<std::string> &dns_servers,
-                               const std::vector<std::string> &domain_search) {
+                               const std::vector<std::string> &domain_search,
+                               TimeoutParameters timeout) {
   SLOG(Resolver, 2) << __func__;
 
   if (dns_servers.empty() && domain_search.empty()) {
@@ -64,8 +72,14 @@
 
   // Send queries one-at-a-time, rather than parallelizing IPv4
   // and IPv6 queries for a single host.  Also override the default
-  // 5-second request timeout and use a 1-second tiemout instead.
-  lines.push_back("options single-request timeout:1");
+  // 5-second request timeout and 2 retries.
+  if (timeout == kDefaultTimeout) {
+    lines.push_back(kDefaultTimeoutOptions);
+  } else if (timeout == kShortTimeout) {
+    lines.push_back(kShortTimeoutOptions);
+  } else {
+    NOTIMPLEMENTED() << "Unknown Resolver timeout parameters";
+  }
 
   // Newline at end of file
   lines.push_back("");
diff --git a/resolver.h b/resolver.h
index 382fdd9..8c56712 100644
--- a/resolver.h
+++ b/resolver.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,6 +20,15 @@
 // of an ipconfig into a "resolv.conf" formatted file.
 class Resolver {
  public:
+  enum TimeoutParameters {
+    kDefaultTimeout,
+    kShortTimeout
+  };
+
+  // The default comma-separated list of technologies for which short
+  // DNS timeouts should be enabled.
+  static const char kDefaultShortTimeoutTechnologies[];
+
   virtual ~Resolver();
 
   // Since this is a singleton, use Resolver::GetInstance()->Foo()
@@ -27,14 +36,16 @@
 
   virtual void set_path(const FilePath &path) { path_ = path; }
 
-  // Set the default domain name service parameters, given an ipconfig entry
-  virtual bool SetDNSFromIPConfig(const IPConfigRefPtr &ipconfig);
+  // Set the default domain name service parameters, given an ipconfig entry.
+  virtual bool SetDNSFromIPConfig(const IPConfigRefPtr &ipconfig,
+                                  TimeoutParameters timeout);
 
-  // Set the default domain name service parameters, given an ipconfig entry
+  // Set the default domain name service parameters, given an ipconfig entry.
   virtual bool SetDNSFromLists(const std::vector<std::string> &dns_servers,
-                               const std::vector<std::string> &domain_search);
+                               const std::vector<std::string> &domain_search,
+                               TimeoutParameters timeout);
 
-  // Remove any created domain name service file
+  // Remove any created domain name service file.
   virtual bool ClearDNS();
 
  protected:
@@ -44,6 +55,9 @@
   friend struct base::DefaultLazyInstanceTraits<Resolver>;
   friend class ResolverTest;
 
+  static const char kDefaultTimeoutOptions[];
+  static const char kShortTimeoutOptions[];
+
   FilePath path_;
 
   DISALLOW_COPY_AND_ASSIGN(Resolver);
diff --git a/resolver_unittest.cc b/resolver_unittest.cc
index 0885e44..31501e2 100644
--- a/resolver_unittest.cc
+++ b/resolver_unittest.cc
@@ -29,7 +29,12 @@
   "nameserver 8.8.8.8\n"
   "nameserver 8.8.9.9\n"
   "search chromium.org google.com\n"
-  "options single-request timeout:1\n";
+  "options single-request timeout:1 attempts:3\n";
+const char kExpectedShortTimeoutOutput[] =
+  "nameserver 8.8.8.8\n"
+  "nameserver 8.8.9.9\n"
+  "search chromium.org google.com\n"
+  "options single-request timeout-ms:300 attempts:15\n";
 }  // namespace {}
 
 class ResolverTest : public Test {
@@ -75,13 +80,36 @@
   properties.domain_search.push_back(kSearchDomain1);
   ipconfig->UpdateProperties(properties, true);
 
-  EXPECT_TRUE(resolver_->SetDNSFromIPConfig(ipconfig));
+  EXPECT_TRUE(resolver_->SetDNSFromIPConfig(
+      ipconfig, Resolver::kDefaultTimeout));
   EXPECT_TRUE(file_util::PathExists(path_));
   EXPECT_EQ(kExpectedOutput, ReadFile());
 
   EXPECT_TRUE(resolver_->ClearDNS());
 }
 
+TEST_F(ResolverTest, ShortTimeout) {
+  EXPECT_FALSE(file_util::PathExists(path_));
+  EXPECT_TRUE(resolver_->ClearDNS());
+
+  // Add DNS info from an IPConfig entry
+  MockControl control;
+  IPConfigRefPtr ipconfig(new IPConfig(&control, kTestDeviceName0));
+  IPConfig::Properties properties;
+  properties.dns_servers.push_back(kNameServer0);
+  properties.dns_servers.push_back(kNameServer1);
+  properties.domain_search.push_back(kSearchDomain0);
+  properties.domain_search.push_back(kSearchDomain1);
+  ipconfig->UpdateProperties(properties, true);
+
+  EXPECT_TRUE(resolver_->SetDNSFromIPConfig(
+      ipconfig, Resolver::kShortTimeout));
+  EXPECT_TRUE(file_util::PathExists(path_));
+  EXPECT_EQ(kExpectedShortTimeoutOutput, ReadFile());
+
+  EXPECT_TRUE(resolver_->ClearDNS());
+}
+
 TEST_F(ResolverTest, Empty) {
   EXPECT_FALSE(file_util::PathExists(path_));
 
@@ -91,7 +119,8 @@
   IPConfig::Properties properties;
   ipconfig->UpdateProperties(properties, true);
 
-  EXPECT_TRUE(resolver_->SetDNSFromIPConfig(ipconfig));
+  EXPECT_TRUE(resolver_->SetDNSFromIPConfig(
+      ipconfig, Resolver::kDefaultTimeout));
   EXPECT_FALSE(file_util::PathExists(path_));
 }