diff options
-rw-r--r-- | net/base/dns_transaction.cc | 199 | ||||
-rw-r--r-- | net/base/dns_transaction.h | 110 | ||||
-rw-r--r-- | net/base/dns_transaction_unittest.cc | 363 | ||||
-rw-r--r-- | net/base/net_error_list.h | 44 | ||||
-rw-r--r-- | net/net.gyp | 3 | ||||
-rw-r--r-- | net/udp/udp_socket_unittest.cc | 1 |
6 files changed, 701 insertions, 19 deletions
diff --git a/net/base/dns_transaction.cc b/net/base/dns_transaction.cc new file mode 100644 index 0000000..7710b96 --- /dev/null +++ b/net/base/dns_transaction.cc @@ -0,0 +1,199 @@ +// 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 "net/base/dns_transaction.h" + +#include "base/bind.h" +#include "base/rand_util.h" +#include "net/base/completion_callback.h" +#include "net/base/dns_query.h" +#include "net/base/dns_response.h" +#include "net/base/rand_callback.h" +#include "net/socket/client_socket_factory.h" +#include "net/udp/datagram_client_socket.h" + +namespace net { + +namespace { + +// Retry timeouts. +const int kTimeoutsMs[] = {3000, 5000, 11000}; +const int kMaxAttempts = arraysize(kTimeoutsMs); + +} + +DnsTransaction::DnsTransaction(const IPEndPoint& dns_server, + const std::string& dns_name, + uint16 query_type, + std::vector<IPAddressNumber>* results, + const RandIntCallback& rand_int, + ClientSocketFactory* socket_factory) + : dns_server_(dns_server), + results_(results), + user_callback_(NULL), + query_(new DnsQuery(dns_name, query_type, rand_int)), + attempts_(0), + next_state_(STATE_NONE), + socket_factory_(socket_factory ? socket_factory : + ClientSocketFactory::GetDefaultFactory()), + ALLOW_THIS_IN_INITIALIZER_LIST( + io_callback_(this, &DnsTransaction::OnIOComplete)) { + DCHECK(results); + DCHECK(!rand_int.is_null()); + for (size_t i = 0; i < arraysize(kTimeoutsMs); ++i) + timeouts_ms_.push_back(base::TimeDelta::FromMilliseconds(kTimeoutsMs[i])); +} + +DnsTransaction::~DnsTransaction() { +} + +int DnsTransaction::Start(CompletionCallback* callback) { + DCHECK(callback); + DCHECK_EQ(STATE_NONE, next_state_); + next_state_ = STATE_CONNECT; + int rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) + user_callback_ = callback; + return rv; +} + +int DnsTransaction::DoLoop(int result) { + DCHECK_NE(STATE_NONE, next_state_); + int rv = result; + do { + State state = next_state_; + next_state_ = STATE_NONE; + switch (state) { + case STATE_CONNECT: + rv = DoConnect(); + break; + case STATE_CONNECT_COMPLETE: + rv = DoConnectComplete(rv); + break; + case STATE_SEND_QUERY: + rv = DoSendQuery(); + break; + case STATE_SEND_QUERY_COMPLETE: + rv = DoSendQueryComplete(rv); + break; + case STATE_READ_RESPONSE: + rv = DoReadResponse(); + break; + case STATE_READ_RESPONSE_COMPLETE: + rv = DoReadResponseComplete(rv); + break; + default: + NOTREACHED(); + break; + } + } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); + + return rv; +} + +void DnsTransaction::DoCallback(int result) { + DCHECK_NE(result, ERR_IO_PENDING); + DCHECK(user_callback_); + CompletionCallback* callback = user_callback_; + user_callback_ = NULL; + callback->Run(result); +} + +void DnsTransaction::OnIOComplete(int result) { + int rv = DoLoop(result); + if (rv != ERR_IO_PENDING) + DoCallback(rv); +} + +int DnsTransaction::DoConnect() { + next_state_ = STATE_CONNECT_COMPLETE; + + DCHECK_LE(attempts_, timeouts_ms_.size()); + if (attempts_ == timeouts_ms_.size()) + return ERR_DNS_TIMED_OUT; + + StartTimer(timeouts_ms_[attempts_]); + attempts_++; + + // TODO(agayev): keep all sockets around in case the server responds + // after its timeout; state machine will need to change to handle that. + socket_.reset(socket_factory_->CreateDatagramClientSocket( + DatagramSocket::RANDOM_BIND, + base::Bind(&base::RandInt), + NULL, + NetLog::Source())); + return socket_->Connect(dns_server_); +} + +int DnsTransaction::DoConnectComplete(int rv) { + if (rv < 0) + return rv; + next_state_ = STATE_SEND_QUERY; + return OK; +} + +int DnsTransaction::DoSendQuery() { + next_state_ = STATE_SEND_QUERY_COMPLETE; + return socket_->Write(query_->io_buffer(), + query_->io_buffer()->size(), + &io_callback_); +} + +int DnsTransaction::DoSendQueryComplete(int rv) { + if (rv < 0) + return rv; + + // Writing to UDP should not result in a partial datagram. + if (rv != query_->io_buffer()->size()) + return ERR_NAME_NOT_RESOLVED; + + next_state_ = STATE_READ_RESPONSE; + return OK; +} + +int DnsTransaction::DoReadResponse() { + next_state_ = STATE_READ_RESPONSE_COMPLETE; + response_.reset(new DnsResponse(query_.get())); + return socket_->Read(response_->io_buffer(), + response_->io_buffer()->size(), + &io_callback_); +} + +int DnsTransaction::DoReadResponseComplete(int rv) { + DCHECK_NE(ERR_IO_PENDING, rv); + RevokeTimer(); + if (rv < 0) + return rv; + + DCHECK(rv); + // TODO(agayev): when supporting EDNS0 we may need to do multiple reads + // to read the whole response. + return response_->Parse(rv, results_); +} + +void DnsTransaction::StartTimer(base::TimeDelta delay) { + timer_.Start(delay, this, &DnsTransaction::OnTimeout); +} + +void DnsTransaction::RevokeTimer() { + timer_.Stop(); +} + +void DnsTransaction::OnTimeout() { + DCHECK(next_state_ == STATE_SEND_QUERY_COMPLETE || + next_state_ == STATE_READ_RESPONSE_COMPLETE); + next_state_ = STATE_CONNECT; + query_.reset(query_->CloneWithNewId()); + int rv = DoLoop(OK); + if (rv != ERR_IO_PENDING) + DoCallback(rv); +} + +void DnsTransaction::set_timeouts_ms( + const std::vector<base::TimeDelta>& timeouts_ms) { + DCHECK_EQ(0u, attempts_); + timeouts_ms_ = timeouts_ms; +} + +} // namespace net diff --git a/net/base/dns_transaction.h b/net/base/dns_transaction.h new file mode 100644 index 0000000..920d7ad --- /dev/null +++ b/net/base/dns_transaction.h @@ -0,0 +1,110 @@ +// 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. + +#ifndef NET_BASE_DNS_TRANSACTION_H_ +#define NET_BASE_DNS_TRANSACTION_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/timer.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/completion_callback.h" +#include "net/base/dns_query.h" +#include "net/base/dns_response.h" +#include "net/base/dns_transaction.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" + +namespace net { + +class ClientSocketFactory; +class DatagramClientSocket; + +// Performs (with fixed retries) a single asynchronous DNS transaction, +// which consists of sending out a DNS query, waiting for response, and +// parsing and returning the IP addresses that it matches. +class DnsTransaction : public base::NonThreadSafe { + public: + // |dns_server| is the address of the DNS server, |dns_name| is the + // hostname (in DNS format) to be resolved, |query_type| is the type of + // the query, either kDNS_A or kDNS_AAAA, |results| is where the IPs in + // the response are stored, |rand_int| is the PRNG used for generating + // DNS query IDs. + DnsTransaction(const IPEndPoint& dns_server, + const std::string& dns_name, + uint16 query_type, + std::vector<IPAddressNumber>* results, + const RandIntCallback& rand_int, + ClientSocketFactory* socket_factory); + ~DnsTransaction(); + + // Starts the resolution process. Will return ERR_IO_PENDING and will + // notify the caller via |callback| once the resolution is complete. + // Should only be called once. + int Start(CompletionCallback* callback); + + private: + FRIEND_TEST_ALL_PREFIXES(DnsTransactionTest, FirstTimeoutTest); + FRIEND_TEST_ALL_PREFIXES(DnsTransactionTest, SecondTimeoutTest); + FRIEND_TEST_ALL_PREFIXES(DnsTransactionTest, ThirdTimeoutTest); + + enum State { + STATE_CONNECT, + STATE_CONNECT_COMPLETE, + STATE_SEND_QUERY, + STATE_SEND_QUERY_COMPLETE, + STATE_READ_RESPONSE, + STATE_READ_RESPONSE_COMPLETE, + STATE_NONE, + }; + + int DoLoop(int result); + void DoCallback(int result); + void OnIOComplete(int result); + + int DoConnect(); + int DoConnectComplete(int result); + int DoSendQuery(); + int DoSendQueryComplete(int result); + int DoReadResponse(); + int DoReadResponseComplete(int result); + + // Fixed number of attempts are made to send a query and read a response, + // and at the start of each, a timer is started with increasing delays. + void StartTimer(base::TimeDelta delay); + void RevokeTimer(); + void OnTimeout(); + + // This is to be used by unit tests only. + void set_timeouts_ms(const std::vector<base::TimeDelta>& timeouts_ms); + + const IPEndPoint dns_server_; + std::vector<IPAddressNumber>* results_; + CompletionCallback* user_callback_; + + scoped_ptr<DnsQuery> query_; + scoped_ptr<DnsResponse> response_; + scoped_ptr<DatagramClientSocket> socket_; + + // Number of retry attempts so far. + size_t attempts_; + + // Timeouts in milliseconds. + std::vector<base::TimeDelta> timeouts_ms_; + + State next_state_; + ClientSocketFactory* socket_factory_; + base::OneShotTimer<DnsTransaction> timer_; + CompletionCallbackImpl<DnsTransaction> io_callback_; + + DISALLOW_COPY_AND_ASSIGN(DnsTransaction); +}; + +} // namespace net + +#endif // NET_BASE_DNS_TRANSACTION_H_ diff --git a/net/base/dns_transaction_unittest.cc b/net/base/dns_transaction_unittest.cc new file mode 100644 index 0000000..f51b1ca --- /dev/null +++ b/net/base/dns_transaction_unittest.cc @@ -0,0 +1,363 @@ +// 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 <vector> + +#include "base/bind.h" +#include "base/rand_util.h" +#include "net/base/dns_transaction.h" +#include "net/base/dns_query.h" +#include "net/base/dns_util.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_util.h" +#include "net/base/test_completion_callback.h" +#include "net/socket/socket_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace net { + +namespace { + +int TestRng1(int /* min */, int /* max */) { return 1; } + +std::vector<IPAddressNumber> CStringsToIPAddressList( + const char* const ip_strings[], size_t size) { + std::vector<IPAddressNumber> ip_addresses; + for (size_t i = 0; i < size; ++i) { + IPAddressNumber ip; + EXPECT_TRUE(ParseIPLiteralToNumber(ip_strings[i], &ip)); + ip_addresses.push_back(ip); + } + return ip_addresses; +} + +IPEndPoint CreateDnsAddress(const char* ip_string, uint16 port) { + IPAddressNumber ip_address; + DCHECK(ParseIPLiteralToNumber(ip_string, &ip_address)); + return IPEndPoint(ip_address, port); +} + +static const char* kDnsIp = "192.168.1.1"; +static const uint16 kDnsPort = 53; +static const base::TimeDelta kTimeoutsMs[] = { + base::TimeDelta::FromMilliseconds(20), + base::TimeDelta::FromMilliseconds(20), + base::TimeDelta::FromMilliseconds(20), +}; + +//----------------------------------------------------------------------------- +// Query/response set for www.google.com, ID is fixed to 1. + +static const uint16 kT1Qtype = kDNS_A; + +static const char kT1DnsName[] = { + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00 +}; + +static const uint8 kT1QueryDatagram[] = { + // query for www.google.com, type A. + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, + 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 +}; + +static const uint8 kT1ResponseDatagram[] = { + // response contains one CNAME for www.l.google.com and the following + // IP addresses: 74.125.226.{179,180,176,177,178} + 0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, + 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, + 0x4d, 0x13, 0x00, 0x08, 0x03, 0x77, 0x77, 0x77, + 0x01, 0x6c, 0xc0, 0x10, 0xc0, 0x2c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, + 0x4a, 0x7d, 0xe2, 0xb3, 0xc0, 0x2c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, + 0x4a, 0x7d, 0xe2, 0xb4, 0xc0, 0x2c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, + 0x4a, 0x7d, 0xe2, 0xb0, 0xc0, 0x2c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, + 0x4a, 0x7d, 0xe2, 0xb1, 0xc0, 0x2c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, + 0x4a, 0x7d, 0xe2, 0xb2 +}; + +static const char* const kT1IpAddresses[] = { + "74.125.226.179", "74.125.226.180", "74.125.226.176", + "74.125.226.177", "74.125.226.178" +}; + +//----------------------------------------------------------------------------- +// Query/response set for codereview.chromium.org, ID is fixed to 2. +static const uint16 kT2Qtype = kDNS_A; + +static const char kT2DnsName[] = { + 0x12, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', + 0x10, 'c', 'h', 'r', 'o', 'm', 'i', 'u', 'm', + 0x03, 'o', 'r', 'g', + 0x00 +}; + +static const uint8 kT2QueryDatagram[] = { + // query for codereview.chromium.org, type A. + 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x64, + 0x65, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x08, + 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, + 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, + 0x01 +}; + +static const uint8 kT2ResponseDatagram[] = { + // response contains one CNAME for ghs.l.google.com and the following + // IP address: 64.233.169.121 + 0x00, 0x02, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x64, + 0x65, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x08, + 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, + 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, + 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, + 0x01, 0x41, 0x75, 0x00, 0x12, 0x03, 0x67, 0x68, + 0x73, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, + 0x35, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x0b, 0x00, 0x04, 0x40, 0xe9, 0xa9, 0x79 +}; + +//----------------------------------------------------------------------------- + +} // namespace + +TEST(DnsTransactionTest, NormalQueryResponseTest1) { + MockWrite writes1[] = { + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), + arraysize(kT1QueryDatagram)) + }; + + MockRead reads1[] = { + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), + arraysize(kT1ResponseDatagram)) + }; + + StaticSocketDataProvider data(reads1, arraysize(reads1), + writes1, arraysize(writes1)); + MockClientSocketFactory factory; + factory.AddSocketDataProvider(&data); + + std::vector<IPAddressNumber> actual_ip_addresses; + std::vector<IPAddressNumber> expected_ip_addresses = + CStringsToIPAddressList(kT1IpAddresses, arraysize(kT1IpAddresses)); + + DnsTransaction t(CreateDnsAddress(kDnsIp, kDnsPort), + std::string(kT1DnsName, arraysize(kT1DnsName)), + kT1Qtype, + &actual_ip_addresses, + base::Bind(&TestRng1), + &factory); + TestCompletionCallback callback; + int rv = t.Start(&callback); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + EXPECT_TRUE(data.at_read_eof()); + EXPECT_TRUE(data.at_write_eof()); + + EXPECT_TRUE(actual_ip_addresses == expected_ip_addresses); +} + +TEST(DnsTransactionTest, MismatchedQueryResponseTest) { + MockWrite writes1[] = { + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), + arraysize(kT1QueryDatagram)) + }; + + MockRead reads2[] = { + MockRead(true, reinterpret_cast<const char*>(kT2ResponseDatagram), + arraysize(kT2ResponseDatagram)) + }; + + StaticSocketDataProvider data(reads2, arraysize(reads2), + writes1, arraysize(writes1)); + MockClientSocketFactory factory; + factory.AddSocketDataProvider(&data); + + std::vector<IPAddressNumber> ip_addresses; + DnsTransaction t(CreateDnsAddress(kDnsIp, kDnsPort), + std::string(kT1DnsName, arraysize(kT1DnsName)), + kT1Qtype, + &ip_addresses, + base::Bind(&TestRng1), + &factory); + TestCompletionCallback callback; + int rv = t.Start(&callback); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + EXPECT_TRUE(data.at_read_eof()); + EXPECT_TRUE(data.at_write_eof()); + + EXPECT_EQ(ERR_DNS_MALFORMED_RESPONSE, rv); +} + +// Test that after the first timeout we do a fresh connection and if we get +// a response on the new connection, we return it. +TEST(DnsTransactionTest, FirstTimeoutTest) { + MockWrite writes1[] = { + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), + arraysize(kT1QueryDatagram)) + }; + + MockRead reads1[] = { + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), + arraysize(kT1ResponseDatagram)) + }; + + scoped_refptr<DelayedSocketData> socket1_data( + new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + scoped_refptr<DelayedSocketData> socket2_data( + new DelayedSocketData(0, reads1, arraysize(reads1), + writes1, arraysize(writes1))); + MockClientSocketFactory factory; + factory.AddSocketDataProvider(socket1_data.get()); + factory.AddSocketDataProvider(socket2_data.get()); + + std::vector<IPAddressNumber> expected_ip_addresses = + CStringsToIPAddressList(kT1IpAddresses, arraysize(kT1IpAddresses)); + + std::vector<IPAddressNumber> actual_ip_addresses; + DnsTransaction transaction(CreateDnsAddress(kDnsIp, kDnsPort), + std::string(kT1DnsName, arraysize(kT1DnsName)), + kT1Qtype, + &actual_ip_addresses, + base::Bind(&TestRng1), + &factory); + transaction.set_timeouts_ms( + std::vector<base::TimeDelta>(kTimeoutsMs, + kTimeoutsMs + arraysize(kTimeoutsMs))); + + TestCompletionCallback callback; + int rv = transaction.Start(&callback); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + + EXPECT_TRUE(socket1_data->at_read_eof()); + EXPECT_TRUE(socket1_data->at_write_eof()); + EXPECT_TRUE(socket2_data->at_read_eof()); + EXPECT_TRUE(socket2_data->at_write_eof()); + EXPECT_EQ(2u, factory.udp_client_sockets().size()); + + EXPECT_TRUE(actual_ip_addresses == expected_ip_addresses); +} + +// Test that after the first timeout we do a fresh connection, and after +// the second timeout we do another fresh connection, and if we get a +// response on the second connection, we return it. +TEST(DnsTransactionTest, SecondTimeoutTest) { + MockWrite writes1[] = { + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), + arraysize(kT1QueryDatagram)) + }; + + MockRead reads1[] = { + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), + arraysize(kT1ResponseDatagram)) + }; + + scoped_refptr<DelayedSocketData> socket1_data( + new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + scoped_refptr<DelayedSocketData> socket2_data( + new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + scoped_refptr<DelayedSocketData> socket3_data( + new DelayedSocketData(0, reads1, arraysize(reads1), + writes1, arraysize(writes1))); + MockClientSocketFactory factory; + factory.AddSocketDataProvider(socket1_data.get()); + factory.AddSocketDataProvider(socket2_data.get()); + factory.AddSocketDataProvider(socket3_data.get()); + + std::vector<IPAddressNumber> expected_ip_addresses = + CStringsToIPAddressList(kT1IpAddresses, arraysize(kT1IpAddresses)); + + std::vector<IPAddressNumber> actual_ip_addresses; + DnsTransaction transaction(CreateDnsAddress(kDnsIp, kDnsPort), + std::string(kT1DnsName, arraysize(kT1DnsName)), + kT1Qtype, + &actual_ip_addresses, + base::Bind(&TestRng1), + &factory); + transaction.set_timeouts_ms( + std::vector<base::TimeDelta>(kTimeoutsMs, + kTimeoutsMs + arraysize(kTimeoutsMs))); + + TestCompletionCallback callback; + int rv = transaction.Start(&callback); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + + EXPECT_TRUE(socket1_data->at_read_eof()); + EXPECT_TRUE(socket1_data->at_write_eof()); + EXPECT_TRUE(socket2_data->at_read_eof()); + EXPECT_TRUE(socket2_data->at_write_eof()); + EXPECT_TRUE(socket3_data->at_read_eof()); + EXPECT_TRUE(socket3_data->at_write_eof()); + EXPECT_EQ(3u, factory.udp_client_sockets().size()); + + EXPECT_TRUE(actual_ip_addresses == expected_ip_addresses); +} + +// Test that after the first timeout we do a fresh connection, and after +// the second timeout we do another fresh connection and after the third +// timeout we give up and return a timeout error. +TEST(DnsTransactionTest, ThirdTimeoutTest) { + MockWrite writes1[] = { + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), + arraysize(kT1QueryDatagram)) + }; + + scoped_refptr<DelayedSocketData> socket1_data( + new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + scoped_refptr<DelayedSocketData> socket2_data( + new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + scoped_refptr<DelayedSocketData> socket3_data( + new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + MockClientSocketFactory factory; + factory.AddSocketDataProvider(socket1_data.get()); + factory.AddSocketDataProvider(socket2_data.get()); + factory.AddSocketDataProvider(socket3_data.get()); + + std::vector<IPAddressNumber> expected_ip_addresses = + CStringsToIPAddressList(kT1IpAddresses, arraysize(kT1IpAddresses)); + + std::vector<IPAddressNumber> actual_ip_addresses; + DnsTransaction transaction(CreateDnsAddress(kDnsIp, kDnsPort), + std::string(kT1DnsName, arraysize(kT1DnsName)), + kT1Qtype, + &actual_ip_addresses, + base::Bind(&TestRng1), + &factory); + transaction.set_timeouts_ms( + std::vector<base::TimeDelta>(kTimeoutsMs, + kTimeoutsMs + arraysize(kTimeoutsMs))); + + TestCompletionCallback callback; + int rv = transaction.Start(&callback); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + + EXPECT_TRUE(socket1_data->at_read_eof()); + EXPECT_TRUE(socket1_data->at_write_eof()); + EXPECT_TRUE(socket2_data->at_read_eof()); + EXPECT_TRUE(socket2_data->at_write_eof()); + EXPECT_TRUE(socket3_data->at_read_eof()); + EXPECT_TRUE(socket3_data->at_write_eof()); + EXPECT_EQ(3u, factory.udp_client_sockets().size()); + + EXPECT_EQ(ERR_DNS_TIMED_OUT, rv); +} + +} // namespace net diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index b6aaa5b..ef44b46 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -17,7 +17,7 @@ // 500-599 ? // 600-699 FTP errors // 700-799 Certificate manager errors -// +// 800-899 DNS resolver errors // An asynchronous IO operation is not yet complete. This usually does not // indicate a fatal error. Typically this error will be generated as a @@ -231,23 +231,6 @@ NET_ERROR(SSL_CLIENT_AUTH_SIGNATURE_FAILED, -141) // which exceeds size threshold). NET_ERROR(MSG_TOO_BIG, -142) -// DNS resolver received a malformed response. -NET_ERROR(DNS_MALFORMED_RESPONSE, -143) - -// DNS server requires TCP -NET_ERROR(DNS_SERVER_REQUIRES_TCP, -144) - -// DNS server failed. This error is returned for all of the following -// error conditions: -// 1 - Format error - The name server was unable to interpret the query. -// 2 - Server failure - The name server was unable to process this query -// due to a problem with the name server. -// 4 - Not Implemented - The name server does not support the requested -// kind of query. -// 5 - Refused - The name server refuses to perform the specified -// operation for policy reasons. -NET_ERROR(DNS_SERVER_FAILED, -145) - // Connection was aborted for switching to another ptotocol. // WebSocket abort SocketStream connection when alternate protocol is found. NET_ERROR(PROTOCOL_SWITCHED, -146) @@ -255,6 +238,9 @@ NET_ERROR(PROTOCOL_SWITCHED, -146) // Returned when attempting to bind an address that is already in use. NET_ERROR(ADDRESS_IN_USE, -147) +// NOTE: error codes 143-145 are available, please use those before adding +// 148. + // Certificate error codes // // The values of certificate error codes must be consecutive. @@ -560,3 +546,25 @@ NET_ERROR(IMPORT_CA_CERT_FAILED, -705) // Server certificate import failed due to some internal error. NET_ERROR(IMPORT_SERVER_CERT_FAILED, -706) + +// DNS error codes. + +// DNS resolver received a malformed response. +NET_ERROR(DNS_MALFORMED_RESPONSE, -800) + +// DNS server requires TCP +NET_ERROR(DNS_SERVER_REQUIRES_TCP, -801) + +// DNS server failed. This error is returned for all of the following +// error conditions: +// 1 - Format error - The name server was unable to interpret the query. +// 2 - Server failure - The name server was unable to process this query +// due to a problem with the name server. +// 4 - Not Implemented - The name server does not support the requested +// kind of query. +// 5 - Refused - The name server refuses to perform the specified +// operation for policy reasons. +NET_ERROR(DNS_SERVER_FAILED, -802) + +// DNS transaction timed out. +NET_ERROR(DNS_TIMED_OUT, -803) diff --git a/net/net.gyp b/net/net.gyp index 9134b72..97386b9 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -72,6 +72,8 @@ 'base/dns_query.cc', 'base/dns_query.h', 'base/dns_response.cc', + 'base/dns_transaction.h', + 'base/dns_transaction.cc', 'base/dns_response.h', 'base/dns_reload_timer.cc', 'base/dns_reload_timer.h', @@ -858,6 +860,7 @@ 'base/directory_lister_unittest.cc', 'base/dns_query_unittest.cc', 'base/dns_response_unittest.cc', + 'base/dns_transaction_unittest.cc', 'base/dnssec_unittest.cc', 'base/dns_util_unittest.cc', 'base/dnsrr_resolver_unittest.cc', diff --git a/net/udp/udp_socket_unittest.cc b/net/udp/udp_socket_unittest.cc index 9fe870f..9df38f6 100644 --- a/net/udp/udp_socket_unittest.cc +++ b/net/udp/udp_socket_unittest.cc @@ -7,7 +7,6 @@ #include "base/basictypes.h" #include "base/bind.h" -#include "base/callback.h" #include "base/metrics/histogram.h" #include "base/stl_util-inl.h" #include "net/base/io_buffer.h" |