普通文本  |  153行  |  4.35 KB

// Copyright 2013 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 "chrome/browser/net/dns_probe_runner.h"

#include "base/bind.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
#include "net/dns/dns_client.h"
#include "net/dns/dns_protocol.h"
#include "net/dns/dns_response.h"
#include "net/dns/dns_transaction.h"

using base::TimeDelta;
using content::BrowserThread;
using net::AddressList;
using net::BoundNetLog;
using net::DnsClient;
using net::DnsResponse;
using net::DnsTransaction;
using net::DnsTransactionFactory;
using net::IPAddressNumber;
using net::IPEndPoint;
using net::NetLog;
using net::NetworkChangeNotifier;
using net::ParseIPLiteralToNumber;

namespace chrome_browser_net {

const char* DnsProbeRunner::kKnownGoodHostname = "google.com";

namespace {

DnsProbeRunner::Result EvaluateResponse(
    int net_error,
    const DnsResponse* response) {
  switch (net_error) {
    case net::OK:
      break;

    // ERR_NAME_NOT_RESOLVED maps to NXDOMAIN, which means the server is working
    // but returned a wrong answer.
    case net::ERR_NAME_NOT_RESOLVED:
      return DnsProbeRunner::INCORRECT;

    // These results mean we heard *something* from the DNS server, but it was
    // unsuccessful (SERVFAIL) or malformed.
    case net::ERR_DNS_MALFORMED_RESPONSE:
    case net::ERR_DNS_SERVER_REQUIRES_TCP:  // Shouldn't happen; DnsTransaction
                                            // will retry with TCP.
    case net::ERR_DNS_SERVER_FAILED:
    case net::ERR_DNS_SORT_ERROR:  // Can only happen if the server responds.
      return DnsProbeRunner::FAILING;

    // Any other error means we never reached the DNS server in the first place.
    case net::ERR_DNS_TIMED_OUT:
    default:
      // Something else happened, probably at a network level.
      return DnsProbeRunner::UNREACHABLE;
  }

  AddressList addr_list;
  TimeDelta ttl;
  DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl);

  if (result != DnsResponse::DNS_PARSE_OK)
    return DnsProbeRunner::FAILING;
  else if (addr_list.empty())
    return DnsProbeRunner::INCORRECT;
  else
    return DnsProbeRunner::CORRECT;
}

}  // namespace

DnsProbeRunner::DnsProbeRunner() : weak_factory_(this), result_(UNKNOWN) {}

DnsProbeRunner::~DnsProbeRunner() {}

void DnsProbeRunner::SetClient(scoped_ptr<net::DnsClient> client) {
  client_ = client.Pass();
}

void DnsProbeRunner::RunProbe(const base::Closure& callback) {
  DCHECK(!callback.is_null());
  DCHECK(client_.get());
  DCHECK(callback_.is_null());
  DCHECK(!transaction_.get());

  callback_ = callback;
  DnsTransactionFactory* factory = client_->GetTransactionFactory();
  if (!factory) {
    // If the DnsTransactionFactory is NULL, then the DnsConfig is invalid, so
    // the runner can't run a transaction.  Return UNKNOWN asynchronously.
    result_ = UNKNOWN;
    BrowserThread::PostTask(
        BrowserThread::IO,
        FROM_HERE,
        base::Bind(&DnsProbeRunner::CallCallback,
                   weak_factory_.GetWeakPtr()));
    return;
  }

  transaction_ = factory->CreateTransaction(
      kKnownGoodHostname,
      net::dns_protocol::kTypeA,
      base::Bind(&DnsProbeRunner::OnTransactionComplete,
                 weak_factory_.GetWeakPtr()),
      BoundNetLog());

  transaction_->Start();
}

bool DnsProbeRunner::IsRunning() const {
  return !callback_.is_null();
}

void DnsProbeRunner::OnTransactionComplete(
    DnsTransaction* transaction,
    int net_error,
    const DnsResponse* response) {
  DCHECK(!callback_.is_null());
  DCHECK(transaction_.get());
  DCHECK_EQ(transaction_.get(), transaction);

  result_ = EvaluateResponse(net_error, response);
  transaction_.reset();

  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      base::Bind(&DnsProbeRunner::CallCallback,
                 weak_factory_.GetWeakPtr()));
}

void DnsProbeRunner::CallCallback() {
  DCHECK(!callback_.is_null());
  DCHECK(!transaction_.get());

  // Clear callback in case it starts a new probe immediately.
  const base::Closure callback = callback_;
  callback_.Reset();
  callback.Run();
}

}  // namespace chrome_browser_net