summaryrefslogtreecommitdiffstats
path: root/tools/android
diff options
context:
space:
mode:
authorwangxianzhu@chromium.org <wangxianzhu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-17 23:57:42 +0000
committerwangxianzhu@chromium.org <wangxianzhu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-17 23:57:42 +0000
commitac65d3f4be81b217cd143fc14cf0c98cc200bffb (patch)
treeb8222814afc03124ede0a6eec86bd307a90868dd /tools/android
parente09155d239cf6bfd49f6c1885da3a319502f09fe (diff)
downloadchromium_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')
-rw-r--r--tools/android/common/adb_connection.cc11
-rw-r--r--tools/android/common/net.cc15
-rw-r--r--tools/android/common/net.h5
-rw-r--r--tools/android/fake_dns/DEPS3
-rw-r--r--tools/android/fake_dns/fake_dns.cc222
-rw-r--r--tools/android/fake_dns/fake_dns.gyp44
6 files changed, 290 insertions, 10 deletions
diff --git a/tools/android/common/adb_connection.cc b/tools/android/common/adb_connection.cc
index c542d16..4739d1d 100644
--- a/tools/android/common/adb_connection.cc
+++ b/tools/android/common/adb_connection.cc
@@ -83,17 +83,8 @@ int ConnectAdbHostSocket(const char* forward_to) {
kBufferMaxLength, 0));
if (response_length < kAdbStatusLength ||
strncmp("OKAY", response, kAdbStatusLength) != 0) {
- char fail_msg_buffer[kBufferMaxLength * 3 + 1];
- char* p = fail_msg_buffer;
- for (int i = 0; i < response_length; ++i) {
- snprintf(p, 3, "%02x,", static_cast<unsigned char>(response[i]));
- p += 3;
- }
-
- if (p > fail_msg_buffer)
- *(--p) = 0; // Eliminate the last comma.
LOG(ERROR) << "Bad response from ADB: length: " << response_length
- << " data: " << fail_msg_buffer;
+ << " data: " << DumpBinary(response, response_length);
HANDLE_EINTR(close(host_socket));
return -1;
}
diff --git a/tools/android/common/net.cc b/tools/android/common/net.cc
index e50a18a..12bca8e 100644
--- a/tools/android/common/net.cc
+++ b/tools/android/common/net.cc
@@ -9,6 +9,8 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include "base/stringprintf.h"
+
namespace tools {
int DisableNagle(int socket) {
@@ -21,5 +23,18 @@ int DeferAccept(int socket) {
return setsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on));
}
+std::string DumpBinary(const char* buffer, size_t length) {
+ std::string result = "[";
+ for (int i = 0; i < length; ++i) {
+ base::StringAppendF(&result, "%02x,",
+ static_cast<unsigned char>(buffer[i]));
+ }
+
+ if (length)
+ result.erase(result.length() - 1);
+
+ return result + "]";
+}
+
} // namespace tools
diff --git a/tools/android/common/net.h b/tools/android/common/net.h
index d17b6f4..091f362 100644
--- a/tools/android/common/net.h
+++ b/tools/android/common/net.h
@@ -6,6 +6,8 @@
#define TOOLS_ANDROID_COMMON_NET_H_
#pragma once
+#include <string>
+
namespace tools {
// DisableNagle can improve TCP transmission performance. Both Chrome net stack
@@ -15,6 +17,9 @@ int DisableNagle(int socket);
// Wake up listener only when data arrive.
int DeferAccept(int socket);
+// Dumps a binary buffer into a string in a human-readable format.
+std::string DumpBinary(const char* buffer, size_t length);
+
} // namespace tools
#endif // TOOLS_ANDROID_COMMON_NET_H_
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',
+ ],
+ },
+ ],
+}
+