blob: a99f55b35b0eba8e5406522646e99a722a8067cc [file] [log] [blame]
// 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/dns/dns_transaction.h"
#include <deque>
#include <vector>
#include "base/bind.h"
#include "net/dns/dns_test_util.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
static const base::TimeDelta kTimeoutsMs[] = {
base::TimeDelta::FromMilliseconds(20),
base::TimeDelta::FromMilliseconds(20),
base::TimeDelta::FromMilliseconds(20),
};
} // namespace
class TestDelegate : public DnsTransaction::Delegate {
public:
TestDelegate() : result_(ERR_UNEXPECTED), transaction_(NULL) {}
virtual ~TestDelegate() {}
virtual void OnTransactionComplete(
int result,
const DnsTransaction* transaction,
const IPAddressList& ip_addresses) {
result_ = result;
transaction_ = transaction;
ip_addresses_ = ip_addresses;
MessageLoop::current()->Quit();
}
int result() const { return result_; }
const DnsTransaction* transaction() const { return transaction_; }
const IPAddressList& ip_addresses() const {
return ip_addresses_;
}
private:
int result_;
const DnsTransaction* transaction_;
IPAddressList ip_addresses_;
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
TEST(DnsTransactionTest, NormalQueryResponseTest) {
MockWrite writes0[] = {
MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
arraysize(kT0QueryDatagram))
};
MockRead reads0[] = {
MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
arraysize(kT0ResponseDatagram))
};
StaticSocketDataProvider data(reads0, arraysize(reads0),
writes0, arraysize(writes0));
MockClientSocketFactory factory;
factory.AddSocketDataProvider(&data);
TestPrng test_prng(std::deque<int>(1, 0));
RandIntCallback rand_int_cb =
base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng));
std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName));
IPEndPoint dns_server;
bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
ASSERT_TRUE(rv);
DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory,
BoundNetLog(), NULL);
TestDelegate delegate;
t.SetDelegate(&delegate);
IPAddressList expected_ip_addresses;
rv = ConvertStringsToIPAddressList(kT0IpAddresses,
arraysize(kT0IpAddresses),
&expected_ip_addresses);
ASSERT_TRUE(rv);
int rv0 = t.Start();
EXPECT_EQ(ERR_IO_PENDING, rv0);
MessageLoop::current()->Run();
EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key());
EXPECT_EQ(OK, delegate.result());
EXPECT_EQ(&t, delegate.transaction());
EXPECT_TRUE(expected_ip_addresses == delegate.ip_addresses());
EXPECT_TRUE(data.at_read_eof());
EXPECT_TRUE(data.at_write_eof());
}
TEST(DnsTransactionTest, MismatchedQueryResponseTest) {
MockWrite writes0[] = {
MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
arraysize(kT0QueryDatagram))
};
MockRead reads1[] = {
MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram),
arraysize(kT1ResponseDatagram))
};
StaticSocketDataProvider data(reads1, arraysize(reads1),
writes0, arraysize(writes0));
MockClientSocketFactory factory;
factory.AddSocketDataProvider(&data);
TestPrng test_prng(std::deque<int>(1, 0));
RandIntCallback rand_int_cb =
base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng));
std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName));
IPEndPoint dns_server;
bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
ASSERT_TRUE(rv);
DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory,
BoundNetLog(), NULL);
TestDelegate delegate;
t.SetDelegate(&delegate);
int rv0 = t.Start();
EXPECT_EQ(ERR_IO_PENDING, rv0);
MessageLoop::current()->Run();
EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key());
EXPECT_EQ(ERR_DNS_MALFORMED_RESPONSE, delegate.result());
EXPECT_EQ(0u, delegate.ip_addresses().size());
EXPECT_EQ(&t, delegate.transaction());
EXPECT_TRUE(data.at_read_eof());
EXPECT_TRUE(data.at_write_eof());
}
// 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 writes0[] = {
MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
arraysize(kT0QueryDatagram))
};
MockRead reads0[] = {
MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
arraysize(kT0ResponseDatagram))
};
scoped_refptr<DelayedSocketData> socket0_data(
new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
scoped_refptr<DelayedSocketData> socket1_data(
new DelayedSocketData(0, reads0, arraysize(reads0),
writes0, arraysize(writes0)));
MockClientSocketFactory factory;
factory.AddSocketDataProvider(socket0_data.get());
factory.AddSocketDataProvider(socket1_data.get());
TestPrng test_prng(std::deque<int>(2, 0));
RandIntCallback rand_int_cb =
base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng));
std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName));
IPEndPoint dns_server;
bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
ASSERT_TRUE(rv);
DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory,
BoundNetLog(), NULL);
TestDelegate delegate;
t.SetDelegate(&delegate);
t.set_timeouts_ms(
std::vector<base::TimeDelta>(kTimeoutsMs,
kTimeoutsMs + arraysize(kTimeoutsMs)));
IPAddressList expected_ip_addresses;
rv = ConvertStringsToIPAddressList(kT0IpAddresses,
arraysize(kT0IpAddresses),
&expected_ip_addresses);
ASSERT_TRUE(rv);
int rv0 = t.Start();
EXPECT_EQ(ERR_IO_PENDING, rv0);
MessageLoop::current()->Run();
EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key());
EXPECT_EQ(OK, delegate.result());
EXPECT_EQ(&t, delegate.transaction());
EXPECT_TRUE(expected_ip_addresses == delegate.ip_addresses());
EXPECT_TRUE(socket0_data->at_read_eof());
EXPECT_TRUE(socket0_data->at_write_eof());
EXPECT_TRUE(socket1_data->at_read_eof());
EXPECT_TRUE(socket1_data->at_write_eof());
EXPECT_EQ(2u, factory.udp_client_sockets().size());
}
// 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 writes0[] = {
MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
arraysize(kT0QueryDatagram))
};
MockRead reads0[] = {
MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
arraysize(kT0ResponseDatagram))
};
scoped_refptr<DelayedSocketData> socket0_data(
new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
scoped_refptr<DelayedSocketData> socket1_data(
new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
scoped_refptr<DelayedSocketData> socket2_data(
new DelayedSocketData(0, reads0, arraysize(reads0),
writes0, arraysize(writes0)));
MockClientSocketFactory factory;
factory.AddSocketDataProvider(socket0_data.get());
factory.AddSocketDataProvider(socket1_data.get());
factory.AddSocketDataProvider(socket2_data.get());
TestPrng test_prng(std::deque<int>(3, 0));
RandIntCallback rand_int_cb =
base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng));
std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName));
IPEndPoint dns_server;
bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
ASSERT_TRUE(rv);
DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory,
BoundNetLog(), NULL);
TestDelegate delegate;
t.SetDelegate(&delegate);
t.set_timeouts_ms(
std::vector<base::TimeDelta>(kTimeoutsMs,
kTimeoutsMs + arraysize(kTimeoutsMs)));
IPAddressList expected_ip_addresses;
rv = ConvertStringsToIPAddressList(kT0IpAddresses,
arraysize(kT0IpAddresses),
&expected_ip_addresses);
ASSERT_TRUE(rv);
int rv0 = t.Start();
EXPECT_EQ(ERR_IO_PENDING, rv0);
MessageLoop::current()->Run();
EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT1Qtype) == t.key());
EXPECT_EQ(OK, delegate.result());
EXPECT_EQ(&t, delegate.transaction());
EXPECT_TRUE(expected_ip_addresses == delegate.ip_addresses());
EXPECT_TRUE(socket0_data->at_read_eof());
EXPECT_TRUE(socket0_data->at_write_eof());
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(3u, factory.udp_client_sockets().size());
}
// 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 writes0[] = {
MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
arraysize(kT0QueryDatagram))
};
scoped_refptr<DelayedSocketData> socket0_data(
new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
scoped_refptr<DelayedSocketData> socket1_data(
new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
scoped_refptr<DelayedSocketData> socket2_data(
new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
MockClientSocketFactory factory;
factory.AddSocketDataProvider(socket0_data.get());
factory.AddSocketDataProvider(socket1_data.get());
factory.AddSocketDataProvider(socket2_data.get());
TestPrng test_prng(std::deque<int>(3, 0));
RandIntCallback rand_int_cb =
base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng));
std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName));
IPEndPoint dns_server;
bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
ASSERT_TRUE(rv);
DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory,
BoundNetLog(), NULL);
TestDelegate delegate;
t.SetDelegate(&delegate);
t.set_timeouts_ms(
std::vector<base::TimeDelta>(kTimeoutsMs,
kTimeoutsMs + arraysize(kTimeoutsMs)));
int rv0 = t.Start();
EXPECT_EQ(ERR_IO_PENDING, rv0);
MessageLoop::current()->Run();
EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key());
EXPECT_EQ(ERR_DNS_TIMED_OUT, delegate.result());
EXPECT_EQ(&t, delegate.transaction());
EXPECT_TRUE(socket0_data->at_read_eof());
EXPECT_TRUE(socket0_data->at_write_eof());
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(3u, factory.udp_client_sockets().size());
}
} // namespace net