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 |