text: // Copyright 2014 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 "chromeos/network/host_resolver_impl_chromeos.h"
  
  #include "base/location.h"
  #include "base/macros.h"
  #include "base/single_thread_task_runner.h"
  #include "base/sys_info.h"
  #include "base/threading/thread_task_runner_handle.h"
  #include "base/values.h"
  #include "chromeos/network/device_state.h"
  #include "chromeos/network/network_handler.h"
  #include "chromeos/network/network_state.h"
  #include "chromeos/network/network_state_handler.h"
  #include "chromeos/network/network_state_handler_observer.h"
  #include "net/base/address_list.h"
  #include "net/base/ip_address.h"
  #include "net/base/net_errors.h"
  #include "third_party/cros_system_api/dbus/service_constants.h"
  
  namespace chromeos {
  
  // HostResolverImplChromeOS::NetworkStateHandlerObserver
  //
  // An instance of this class is created on the NetworkHandler (UI) thread and
  // manages its own lifetime, destroying itself when NetworkStateHandlerObserver
  // ::OnShuttingDown() gets called.
  
  class HostResolverImplChromeOS::NetworkObserver
      : public chromeos::NetworkStateHandlerObserver {
   public:
    static void Create(
        const base::WeakPtr<HostResolverImplChromeOS>& resolver,
        scoped_refptr<base::SingleThreadTaskRunner> resolver_task_runner,
        NetworkStateHandler* network_state_handler) {
      new NetworkObserver(resolver, resolver_task_runner, network_state_handler);
    }
  
    NetworkObserver(
        const base::WeakPtr<HostResolverImplChromeOS>& resolver,
        scoped_refptr<base::SingleThreadTaskRunner> resolver_task_runner,
        NetworkStateHandler* network_state_handler)
        : resolver_(resolver),
          resolver_task_runner_(resolver_task_runner),
          network_state_handler_(network_state_handler),
          weak_ptr_factory_resolver_thread_(this) {
      network_state_handler_->AddObserver(this, FROM_HERE);
      DefaultNetworkChanged(network_state_handler_->DefaultNetwork());
    }
  
   private:
    ~NetworkObserver() override {
      network_state_handler_->RemoveObserver(this, FROM_HERE);
    }
  
    // NetworkStateHandlerObserver
    void DefaultNetworkChanged(const NetworkState* network) override {
      if (!network) {
        DVLOG(2) << "DefaultNetworkChanged: No Network.";
        CallResolverSetIpAddress("", "");
        return;
      }
      std::string ipv4_address, ipv6_address;
      const DeviceState* device_state =
          network_state_handler_->GetDeviceState(network->device_path());
      if (!device_state) {
        LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
            << "DefaultNetworkChanged: Network missing device: "
            << network->path();
        CallResolverSetIpAddress("", "");
        return;
      }
      for (base::DictionaryValue::Iterator iter(device_state->ip_configs());
           !iter.IsAtEnd(); iter.Advance()) {
        const base::DictionaryValue* ip_config;
        if (!iter.value().GetAsDictionary(&ip_config)) {
          LOG(ERROR) << "Badly formatted IPConfigs: " << network->path();
          continue;
        }
        std::string method, address;
        if (ip_config->GetString(shill::kMethodProperty, &method) &&
            ip_config->GetString(shill::kAddressProperty, &address)) {
          if (method == shill::kTypeIPv4 || method == shill::kTypeDHCP)
            ipv4_address = address;
          else if (method == shill::kTypeIPv6 || method == shill::kTypeDHCP6)
            ipv6_address = address;
        } else {
          LOG(ERROR) << "DefaultNetworkChanged: IPConfigs missing properties: "
                     << network->path();
        }
      }
      DVLOG(2) << "DefaultNetworkChanged: " << network->name()
               << " IPv4: " << ipv4_address << " IPv6: " << ipv6_address;
      CallResolverSetIpAddress(ipv4_address, ipv6_address);
    }
  
    void OnShuttingDown() override { delete this; }
  
    void CallResolverSetIpAddress(const std::string& ipv4_address,
                                  const std::string& ipv6_address) {
      resolver_task_runner_->PostTask(
          FROM_HERE, base::Bind(&NetworkObserver::SetIpAddressOnResolverThread,
                                weak_ptr_factory_resolver_thread_.GetWeakPtr(),
                                ipv4_address, ipv6_address));
    }
  
    void SetIpAddressOnResolverThread(const std::string& ipv4_address,
                                      const std::string& ipv6_address) {
      if (resolver_)
        resolver_->SetIPAddresses(ipv4_address, ipv6_address);
    }
  
    base::WeakPtr<HostResolverImplChromeOS> resolver_;
    scoped_refptr<base::SingleThreadTaskRunner> resolver_task_runner_;
    NetworkStateHandler* network_state_handler_;
    base::WeakPtrFactory<NetworkObserver> weak_ptr_factory_resolver_thread_;
  
    DISALLOW_COPY_AND_ASSIGN(NetworkObserver);
  };
  
  // HostResolverImplChromeOS
  
  HostResolverImplChromeOS::HostResolverImplChromeOS(
      scoped_refptr<base::SingleThreadTaskRunner> network_handler_task_runner,
      NetworkStateHandler* network_state_handler,
      const Options& options,
      net::NetLog* net_log)
      : HostResolverImpl(options, net_log),
        network_handler_task_runner_(network_handler_task_runner),
        weak_ptr_factory_(this) {
    network_handler_task_runner->PostTask(
        FROM_HERE,
        base::Bind(&NetworkObserver::Create, weak_ptr_factory_.GetWeakPtr(),
                   base::ThreadTaskRunnerHandle::Get(), network_state_handler));
  }
  
  HostResolverImplChromeOS::~HostResolverImplChromeOS() {
  }
  
  int HostResolverImplChromeOS::Resolve(
      const RequestInfo& info,
      net::RequestPriority priority,
      net::AddressList* addresses,
      const net::CompletionCallback& callback,
      std::unique_ptr<Request>* out_req,
      const net::NetLogWithSource& source_net_log) {
    DCHECK(thread_checker_.CalledOnValidThread());
    if (ResolveLocalIPAddress(info, addresses))
      return net::OK;
    return net::HostResolverImpl::Resolve(
        info, priority, addresses, callback, out_req, source_net_log);
  }
  
  void HostResolverImplChromeOS::SetIPAddresses(const std::string& ipv4_address,
                                                const std::string& ipv6_address) {
    DCHECK(thread_checker_.CalledOnValidThread());
    ipv4_address_ = ipv4_address;
    ipv6_address_ = ipv6_address;
  }
  
  bool HostResolverImplChromeOS::ResolveLocalIPAddress(
      const RequestInfo& info,
      net::AddressList* addresses) {
    DCHECK(thread_checker_.CalledOnValidThread());
    if (!info.is_my_ip_address() || ipv4_address_.empty())
      return false;
  
    // Use IPConfig data for localhost address lookup.
    addresses->clear();
  
    if (info.address_family() != net::ADDRESS_FAMILY_IPV4 &&
        !ipv6_address_.empty()) {
      net::IPAddress ipv6;
      if (ipv6.AssignFromIPLiteral(ipv6_address_))
        addresses->push_back(net::IPEndPoint(ipv6, 0));
    }
  
    net::IPAddress ipv4;
    if (ipv4.AssignFromIPLiteral(ipv4_address_))
      addresses->push_back(net::IPEndPoint(ipv4, 0));
  
    DVLOG(2) << "ResolveLocalIPAddress("
             << static_cast<int>(info.address_family()) << "): "
             << addresses->size()
             << " IPv4: " << ipv4_address_ << " IPv6: " << ipv6_address_;
    addresses->SetDefaultCanonicalName();
    return true;
  }
  
  // static
  std::unique_ptr<net::HostResolver>
  HostResolverImplChromeOS::CreateSystemResolver(const Options& options,
                                                 net::NetLog* net_log) {
    return std::unique_ptr<net::HostResolver>(new HostResolverImplChromeOS(
        NetworkHandler::Get()->task_runner(),
        NetworkHandler::Get()->network_state_handler(), options, net_log));
  }
  
  // static
  std::unique_ptr<net::HostResolver>
  HostResolverImplChromeOS::CreateHostResolverForTest(
      scoped_refptr<base::SingleThreadTaskRunner> network_handler_task_runner,
      NetworkStateHandler* network_state_handler) {
    Options options;
    return std::unique_ptr<net::HostResolver>(new HostResolverImplChromeOS(
        network_handler_task_runner, network_state_handler, options, NULL));
  }
  
  }  // namespace chromeos
  