diff options
author | wangxianzhu@chromium.org <wangxianzhu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-17 23:57:42 +0000 |
---|---|---|
committer | wangxianzhu@chromium.org <wangxianzhu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-17 23:57:42 +0000 |
commit | ac65d3f4be81b217cd143fc14cf0c98cc200bffb (patch) | |
tree | b8222814afc03124ede0a6eec86bd307a90868dd /tools/android/fake_dns | |
parent | e09155d239cf6bfd49f6c1885da3a319502f09fe (diff) | |
download | chromium_src-ac65d3f4be81b217cd143fc14cf0c98cc200bffb.zip chromium_src-ac65d3f4be81b217cd143fc14cf0c98cc200bffb.tar.gz chromium_src-ac65d3f4be81b217cd143fc14cf0c98cc200bffb.tar.bz2 |
fake_dns tool for Android (fixed DEPS).
It runs on device to resolves any host name to 127.0.0.1.
With it and the forwarder, all HTTP requests are redirected to our replay server
running on the host machine.
Review URL: http://codereview.chromium.org/9419020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122630 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/android/fake_dns')
-rw-r--r-- | tools/android/fake_dns/DEPS | 3 | ||||
-rw-r--r-- | tools/android/fake_dns/fake_dns.cc | 222 | ||||
-rw-r--r-- | tools/android/fake_dns/fake_dns.gyp | 44 |
3 files changed, 269 insertions, 0 deletions
diff --git a/tools/android/fake_dns/DEPS b/tools/android/fake_dns/DEPS new file mode 100644 index 0000000..8fa9d48 --- /dev/null +++ b/tools/android/fake_dns/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+net", +] diff --git a/tools/android/fake_dns/fake_dns.cc b/tools/android/fake_dns/fake_dns.cc new file mode 100644 index 0000000..18430e2 --- /dev/null +++ b/tools/android/fake_dns/fake_dns.cc @@ -0,0 +1,222 @@ +// Copyright (c) 2012 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 <arpa/inet.h> +#include <errno.h> +#include <netinet/in.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "net/base/big_endian.h" +#include "net/base/net_util.h" +#include "net/dns/dns_protocol.h" +#include "tools/android/common/daemon.h" +#include "tools/android/common/net.h" + +namespace { + +// Mininum request size: 1 question containing 1 QNAME, 1 TYPE and 1 CLASS. +const size_t kMinRequestSize = sizeof(net::dns_protocol::Header) + 6; + +// The name reference in the answer pointing to the name in the query. +// Its format is: highest two bits set to 1, then the offset of the name +// which just follows the header. +const uint16 kPointerToQueryName = + static_cast<uint16>(0xc000 | sizeof(net::dns_protocol::Header)); + +const uint32 kTTL = 86400; // One day. + +void SendRefusedResponse(int sock, const sockaddr_in& client_addr, uint16 id) { + net::dns_protocol::Header response; + response.id = htons(id); + response.flags = htons(net::dns_protocol::kFlagResponse | + net::dns_protocol::kFlagAA | + net::dns_protocol::kFlagRD | + net::dns_protocol::kFlagRA | + net::dns_protocol::kRcodeREFUSED); + response.qdcount = 0; + response.ancount = 0; + response.nscount = 0; + response.arcount = 0; + HANDLE_EINTR(sendto(sock, &response, sizeof(response), 0, + reinterpret_cast<const sockaddr*>(&client_addr), + sizeof(client_addr))); +} + +void SendResponse(int sock, const sockaddr_in& client_addr, uint16 id, + uint16 qtype, const char* question, size_t question_length) { + net::dns_protocol::Header header; + header.id = htons(id); + header.flags = htons(net::dns_protocol::kFlagResponse | + net::dns_protocol::kFlagAA | + net::dns_protocol::kFlagRD | + net::dns_protocol::kFlagRA | + net::dns_protocol::kRcodeNOERROR); + header.qdcount = htons(1); + header.ancount = htons(1); + header.nscount = 0; + header.arcount = 0; + + // Size of RDATA which is a IPv4 or IPv6 address. + size_t rdata_size = qtype == net::dns_protocol::kTypeA ? + net::kIPv4AddressSize : net::kIPv6AddressSize; + + // Size of the whole response which contains the header, the question and + // the answer. 12 is the sum of sizes of the compressed name reference, TYPE, + // CLASS, TTL and RDLENGTH. + size_t response_size = sizeof(header) + question_length + 12 + rdata_size; + + if (response_size > net::dns_protocol::kMaxUDPSize) { + LOG(ERROR) << "Response is too large: " << response_size; + SendRefusedResponse(sock, client_addr, id); + return; + } + + char response[net::dns_protocol::kMaxUDPSize]; + net::BigEndianWriter writer(response, arraysize(response)); + writer.WriteBytes(&header, sizeof(header)); + + // Repeat the question in the response. Some clients (e.g. ping) needs this. + writer.WriteBytes(question, question_length); + + // Construct the answer. + writer.WriteU16(kPointerToQueryName); + writer.WriteU16(qtype); + writer.WriteU16(net::dns_protocol::kClassIN); + writer.WriteU32(kTTL); + writer.WriteU16(rdata_size); + if (qtype == net::dns_protocol::kTypeA) + writer.WriteU32(INADDR_LOOPBACK); + else + writer.WriteBytes(&in6addr_loopback, sizeof(in6_addr)); + DCHECK(writer.ptr() - response == response_size); + + HANDLE_EINTR(sendto(sock, response, response_size, 0, + reinterpret_cast<const sockaddr*>(&client_addr), + sizeof(client_addr))); +} + +void HandleRequest(int sock, const char* request, size_t size, + const sockaddr_in& client_addr) { + if (size < kMinRequestSize) { + LOG(ERROR) << "Request is too small " << size + << "\n" << tools::DumpBinary(request, size); + return; + } + + net::BigEndianReader reader(request, size); + net::dns_protocol::Header header; + reader.ReadBytes(&header, sizeof(header)); + uint16 id = ntohs(header.id); + uint16 flags = ntohs(header.flags); + uint16 qdcount = ntohs(header.qdcount); + uint16 ancount = ntohs(header.ancount); + uint16 nscount = ntohs(header.nscount); + uint16 arcount = ntohs(header.arcount); + + const uint16 kAllowedFlags = 0x07ff; + if ((flags & ~kAllowedFlags) || + qdcount != 1 || ancount || nscount || arcount) { + LOG(ERROR) << "Unsupported request: FLAGS=" << flags + << " QDCOUNT=" << qdcount + << " ANCOUNT=" << ancount + << " NSCOUNT=" << nscount + << " ARCOUNT=" << arcount + << "\n" << tools::DumpBinary(request, size); + SendRefusedResponse(sock, client_addr, id); + return; + } + + // request[size - 5] should be the end of the QNAME (a zero byte). + // We don't care about the validity of QNAME because we don't parse it. + const char* qname_end = &request[size - 5]; + if (*qname_end) { + LOG(ERROR) << "Error parsing QNAME\n" << tools::DumpBinary(request, size); + SendRefusedResponse(sock, client_addr, id); + return; + } + + reader.Skip(qname_end - reader.ptr() + 1); + + uint16 qtype; + uint16 qclass; + reader.ReadU16(&qtype); + reader.ReadU16(&qclass); + if ((qtype != net::dns_protocol::kTypeA && + qtype != net::dns_protocol::kTypeAAAA) || + qclass != net::dns_protocol::kClassIN) { + LOG(ERROR) << "Unsupported query: QTYPE=" << qtype << " QCLASS=" << qclass + << "\n" << tools::DumpBinary(request, size); + SendRefusedResponse(sock, client_addr, id); + return; + } + + SendResponse(sock, client_addr, id, qtype, + request + sizeof(header), size - sizeof(header)); +} + +} // namespace + +int main(int argc, char** argv) { + printf("Fake DNS server\n"); + + CommandLine command_line(argc, argv); + if (tools::HasHelpSwitch(command_line) || command_line.GetArgs().size()) { + tools::ShowHelp(argv[0], "", ""); + return 0; + } + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("create socket"); + return 1; + } + + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(53); + int reuse_addr = 1; + if (HANDLE_EINTR(bind(sock, reinterpret_cast<sockaddr*>(&addr), + sizeof(addr))) < 0) { + perror("server bind"); + HANDLE_EINTR(close(sock)); + return 1; + } + + if (!tools::HasNoSpawnDaemonSwitch(command_line)) + tools::SpawnDaemon(0); + + while (true) { + sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + char request[net::dns_protocol::kMaxUDPSize]; + int size = HANDLE_EINTR(recvfrom(sock, request, sizeof(request), + MSG_WAITALL, + reinterpret_cast<sockaddr*>(&client_addr), + &client_addr_len)); + if (size < 0) { + // Unrecoverable error, can only exit. + LOG(ERROR) << "Failed to receive a request: " << strerror(errno); + HANDLE_EINTR(close(sock)); + return 1; + } + + if (size > 0) + HandleRequest(sock, request, size, client_addr); + } +} + diff --git a/tools/android/fake_dns/fake_dns.gyp b/tools/android/fake_dns/fake_dns.gyp new file mode 100644 index 0000000..7b685a1 --- /dev/null +++ b/tools/android/fake_dns/fake_dns.gyp @@ -0,0 +1,44 @@ +# Copyright (c) 2012 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. + +{ + 'targets': [ + { + 'target_name': 'fake_dns', + 'type': 'none', + 'dependencies': [ + 'fake_dns_symbols', + ], + 'actions': [ + { + 'action_name': 'strip_fake_dns', + 'inputs': ['<(PRODUCT_DIR)/fake_dns_symbols'], + 'outputs': ['<(PRODUCT_DIR)/fake_dns'], + 'action': [ + '<!(/bin/echo -n $STRIP)', + '--strip-unneeded', + '<@(_inputs)', + '-o', + '<@(_outputs)', + ], + }, + ], + }, { + 'target_name': 'fake_dns_symbols', + 'type': 'executable', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../net/net.gyp:net', + '../common/common.gyp:android_tools_common', + ], + 'include_dirs': [ + '../../..', + ], + 'sources': [ + 'fake_dns.cc', + ], + }, + ], +} + |