diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/android/common/adb_connection.cc | 11 | ||||
-rw-r--r-- | tools/android/common/net.cc | 15 | ||||
-rw-r--r-- | tools/android/common/net.h | 5 | ||||
-rw-r--r-- | tools/android/fake_dns/fake_dns.cc | 222 | ||||
-rw-r--r-- | tools/android/fake_dns/fake_dns.gyp | 44 |
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', + ], + }, + ], +} + |