Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Unified Diff: tools/android/fake_dns/fake_dns.cc

Issue 9401014: fake_dns tool for Android. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/all_android.gyp ('k') | tools/android/fake_dns/fake_dns.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/android/fake_dns/fake_dns.cc
===================================================================
--- tools/android/fake_dns/fake_dns.cc (revision 0)
+++ tools/android/fake_dns/fake_dns.cc (revision 0)
@@ -0,0 +1,236 @@
+// 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/dns_util.h"
szym 2012/02/15 21:44:04 What is this needed for? If only kClassIN, then re
Xianzhu 2012/02/16 01:19:36 Now use net/dns/dns_protocol.h, net/base/net_util.
+#include "tools/android/common/daemon.h"
+
+namespace {
+
+const size_t kMaxMessageSize = 512;
szym 2012/02/15 21:44:04 net::dns_protocol::kMaxUDPSize
Xianzhu 2012/02/16 01:19:36 Done.
+const size_t kHeaderSize = 12;
szym 2012/02/15 21:44:04 sizeof(net::dns_protocol::Header)
Xianzhu 2012/02/16 01:19:36 Done.
+
+// DNS resource record types. See
+// http://www.iana.org/assignments/dns-parameters
+const uint16 kDNS_A = 1;
szym 2012/02/15 21:44:04 net::dns_protocol::kTypeA
Xianzhu 2012/02/16 01:19:36 Done.
+const uint16 kDNS_AAAA = 28;
+
+// Mininum request size: 1 question containing 1 QNAME, 1 TYPE and 1 CLASS.
+const size_t kMinRequestSize = kHeaderSize + 6;
+
+// Sum of sizes of compressed name reference, TYPE, CLASS, TTL, RDLENGTH and
+// RDATA of the fake answer.
+const size_t kAnswerSizeV4 = 12 + sizeof(in_addr_t);
+const size_t kAnswerSizeV6 = 12 + sizeof(in6_addr);
szym 2012/02/15 21:44:04 net::kIPv4AddressSize (in net/base/net_util.h)
Xianzhu 2012/02/16 01:19:36 Done.
+
+// The name reference in answer, format is: highest two bits set to 1,
+// then the offset of the qname which just follows the header.
szym 2012/02/15 21:44:04 It would help to specifically say that this is ref
Xianzhu 2012/02/16 01:19:36 Done.
+const uint16 kNameReference = static_cast<uint16>(0xc000 | kHeaderSize);
+
+const uint32 kTTL = 86400;
szym 2012/02/15 21:44:04 Comment that this equals 1 day.
Xianzhu 2012/02/16 01:19:36 Done.
+
+const char* DumpBinary(const char* message, size_t length) {
+ static char buffer[kMaxMessageSize * 3 + 1];
+ for (size_t i = 0; i < length; i++)
+ snprintf(buffer + i * 3, 3, "%02x,", static_cast<uint8>(message[i]));
+ buffer[length * 3 - 1] = 0;
+ return buffer;
+}
+
+uint16 getU16(const char* buffer) {
szym 2012/02/15 21:44:04 See "net/base/big_endian.h" for this and the next
Xianzhu 2012/02/16 01:19:36 Done.
+ return static_cast<uint16>(buffer[0]) << 8 | static_cast<uint8>(buffer[1]);
+}
+
+void putU16(char* buffer, uint16 data) {
+ buffer[0] = static_cast<char>(data >> 8);
+ buffer[1] = static_cast<char>(data);
+}
+
+void putU32(char* buffer, uint32 data) {
+ buffer[0] = static_cast<char>(data >> 24);
+ buffer[1] = static_cast<char>(data >> 16);
+ buffer[2] = static_cast<char>(data >> 8);
+ buffer[3] = static_cast<char>(data);
+}
+
+bool SendPacketTo(int sock,
szym 2012/02/15 21:44:04 I think it wouldn't be much effort to make it asyn
Xianzhu 2012/02/16 01:19:36 We don't want to introduce much dependency to Chro
+ const char* data,
+ size_t size,
+ const sockaddr_in& target_addr) {
+ size_t bytes_sent = 0;
+ size_t bytes_remaining = size;
+ while (bytes_remaining > 0) {
szym 2012/02/15 21:44:04 This loop should not be necessary for datagram soc
Xianzhu 2012/02/16 01:19:36 Changed back to non-loop version.
+ ssize_t ret = HANDLE_EINTR(sendto(
+ sock, data + bytes_sent, bytes_remaining, 0,
+ reinterpret_cast<const sockaddr*>(&target_addr),
+ sizeof(target_addr)));
+ if (ret < 0)
+ return false;
+
+ bytes_sent += ret;
+ bytes_remaining -= ret;
+ }
+ return true;
+}
+
+void SendEmptyResponse(int sock, const sockaddr_in& client_addr, uint16 id) {
szym 2012/02/15 21:44:04 Rename to SendRefusedResponse. "Empty" suggests it
Xianzhu 2012/02/16 01:19:36 Done.
+ char response[kHeaderSize];
+ memset(response, 0, kHeaderSize);
+ putU16(response, id);
+ putU16(response + 2, 0x8585); // QR:1, AA:1, RD:1, RA:1, RCODE:5 (Refused).
szym 2012/02/15 21:44:04 With dns_protocol this would be kFlagResponse | kF
Xianzhu 2012/02/16 01:19:36 Done.
+ SendPacketTo(sock, response, kHeaderSize, client_addr);
+}
+
+void SendResponse(int sock, const sockaddr_in& client_addr, uint16 id,
+ uint16 qtype, const char* question, size_t question_length) {
+ size_t response_size = kHeaderSize + question_length +
+ (qtype == kDNS_A ? kAnswerSizeV4 : kAnswerSizeV6);
+ if (response_size > kMaxMessageSize) {
+ LOG(ERROR) << "Response is too large: " << response_size;
+ SendEmptyResponse(sock, client_addr, id);
+ return;
+ }
+
+ char response[kMaxMessageSize];
+ putU16(response, id);
+ putU16(response + 2, 0x8580); // QR:1, AA:1, RD:1, RA:1, RCODE:0 (No error).
szym 2012/02/15 21:44:04 As above.
Xianzhu 2012/02/16 01:19:36 Done.
+ putU16(response + 4, 1); // QDCOUNT
+ putU16(response + 6, 1); // ANCOUNT
+ putU16(response + 8, 0); // NSCOUNT
+ putU16(response + 10, 0); // ARCOUNT
szym 2012/02/15 21:44:04 See BigEndianWriter in "net/base/big_endian.h".
Xianzhu 2012/02/16 01:19:36 Done.
+
+ // Repeat the question in the response. Some clients (e.g. ping) needs this.
+ memcpy(response + kHeaderSize, question, question_length);
+
+ // Construct the answer.
+ char* answer = response + kHeaderSize + question_length;
+ putU16(answer, kNameReference); // References the QNAME in the question.
+ putU16(answer + 2, qtype);
+ putU16(answer + 4, net::kClassIN);
+ putU32(answer + 6, kTTL);
+ putU16(answer + 10, 4);
szym 2012/02/15 21:44:04 As above.
szym 2012/02/15 21:57:23 BUG: This should be |response_size|.
Xianzhu 2012/02/16 01:19:36 Done.
Xianzhu 2012/02/16 01:19:36 Good Catch! I think it should be the sizeof RDATA.
+ if (qtype == kDNS_A)
+ putU32(answer + 12, INADDR_LOOPBACK);
+ else
+ memcpy(answer + 12, &in6addr_loopback, sizeof(in6_addr));
+
+ SendPacketTo(sock, response, response_size, 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" << DumpBinary(request, size);
+ SendEmptyResponse(sock, client_addr, 0);
szym 2012/02/15 21:44:04 If id is set to 0, the client will ignore this, so
Xianzhu 2012/02/16 01:19:36 Done.
+ return;
+ }
+
+ uint16 id = getU16(request);
+ uint16 flags = getU16(request + 2);
szym 2012/02/15 21:44:04 See BigEndianReader in "net/base/big_endian.h"
szym 2012/02/15 21:46:43 There's also the option to use dns_protocol::Heade
Xianzhu 2012/02/16 01:19:36 Done.
Xianzhu 2012/02/16 01:19:36 Done.
+ uint16 qdcount = getU16(request + 4);
+ uint16 ancount = getU16(request + 6);
+ uint16 nscount = getU16(request + 8);
+ uint16 arcount = getU16(request + 10);
+ if ((flags & 0xf800) || qdcount != 1 || ancount || nscount || arcount) {
szym 2012/02/15 21:44:04 0xf800 looks arbitrary. Maybe: const uint16 kAllow
Xianzhu 2012/02/16 01:19:36 Done.
+ LOG(ERROR) << "Unsupported request: FLAGS=" << flags
+ << " QDCOUNT=" << qdcount
+ << " ANCOUNT=" << ancount
+ << " NSCOUNT=" << nscount
+ << " ARCOUNT=" << arcount
+ << "\n" << DumpBinary(request, size);
+ SendEmptyResponse(sock, client_addr, id);
+ return;
+ }
+
+ // Should be the end of the QNAME.
+ if (request[size - 5]) {
szym 2012/02/15 21:44:04 This is only a partial check. You're already inclu
Xianzhu 2012/02/16 01:19:36 We don't actually parse the QNAME, so it seems unn
+ LOG(ERROR) << "Error parsing QNAME" << "\n" << DumpBinary(request, size);
+ SendEmptyResponse(sock, client_addr, id);
+ return;
+ }
+
+ uint16 qtype = getU16(request + size - 4);
+ uint16 qclass = getU16(request + size - 2);
+ if ((qtype != kDNS_A && qtype != kDNS_AAAA) || qclass != net::kClassIN) {
+ LOG(ERROR) << "Unsupported query: QTYPE=" << qtype << " QCLASS=" << qclass
+ << "\n" << DumpBinary(request, size);
+ SendEmptyResponse(sock, client_addr, id);
+ return;
+ }
+
+ SendResponse(sock, client_addr, id, qtype,
+ request + kHeaderSize, size - kHeaderSize);
+}
+
+} // 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");
szym 2012/02/15 21:44:04 Why perror here and LOG(ERROR) above?
Xianzhu 2012/02/16 01:19:36 Because this is in the main process which can be s
+ 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;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
szym 2012/02/15 21:44:04 I don't think SO_REUSEADDR is necessary. AFAIK, TI
Xianzhu 2012/02/16 01:19:36 Verified you are right.
+ 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[kMaxMessageSize];
+ 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);
+ }
+}
+
Property changes on: tools/android/fake_dns/fake_dns.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« no previous file with comments | « build/all_android.gyp ('k') | tools/android/fake_dns/fake_dns.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698