summaryrefslogtreecommitdiffstats
path: root/tools/android
diff options
context:
space:
mode:
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/fake_dns.cc222
-rw-r--r--tools/android/fake_dns/fake_dns.gyp44
5 files changed, 287 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/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',
+ ],
+ },
+ ],
+}
+