Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <arpa/inet.h> | |
| 6 #include <errno.h> | |
| 7 #include <netinet/in.h> | |
| 8 #include <signal.h> | |
| 9 #include <stdio.h> | |
| 10 #include <stdlib.h> | |
| 11 #include <string.h> | |
| 12 #include <sys/socket.h> | |
| 13 #include <sys/types.h> | |
| 14 #include <unistd.h> | |
| 15 | |
| 16 #include <string> | |
| 17 | |
| 18 #include "base/basictypes.h" | |
| 19 #include "base/command_line.h" | |
| 20 #include "base/eintr_wrapper.h" | |
| 21 #include "base/logging.h" | |
| 22 #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.
| |
| 23 #include "tools/android/common/daemon.h" | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 const size_t kMaxMessageSize = 512; | |
|
szym
2012/02/15 21:44:04
net::dns_protocol::kMaxUDPSize
Xianzhu
2012/02/16 01:19:36
Done.
| |
| 28 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.
| |
| 29 | |
| 30 // DNS resource record types. See | |
| 31 // http://www.iana.org/assignments/dns-parameters | |
| 32 const uint16 kDNS_A = 1; | |
|
szym
2012/02/15 21:44:04
net::dns_protocol::kTypeA
Xianzhu
2012/02/16 01:19:36
Done.
| |
| 33 const uint16 kDNS_AAAA = 28; | |
| 34 | |
| 35 // Mininum request size: 1 question containing 1 QNAME, 1 TYPE and 1 CLASS. | |
| 36 const size_t kMinRequestSize = kHeaderSize + 6; | |
| 37 | |
| 38 // Sum of sizes of compressed name reference, TYPE, CLASS, TTL, RDLENGTH and | |
| 39 // RDATA of the fake answer. | |
| 40 const size_t kAnswerSizeV4 = 12 + sizeof(in_addr_t); | |
| 41 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.
| |
| 42 | |
| 43 // The name reference in answer, format is: highest two bits set to 1, | |
| 44 // 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.
| |
| 45 const uint16 kNameReference = static_cast<uint16>(0xc000 | kHeaderSize); | |
| 46 | |
| 47 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.
| |
| 48 | |
| 49 const char* DumpBinary(const char* message, size_t length) { | |
| 50 static char buffer[kMaxMessageSize * 3 + 1]; | |
| 51 for (size_t i = 0; i < length; i++) | |
| 52 snprintf(buffer + i * 3, 3, "%02x,", static_cast<uint8>(message[i])); | |
| 53 buffer[length * 3 - 1] = 0; | |
| 54 return buffer; | |
| 55 } | |
| 56 | |
| 57 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.
| |
| 58 return static_cast<uint16>(buffer[0]) << 8 | static_cast<uint8>(buffer[1]); | |
| 59 } | |
| 60 | |
| 61 void putU16(char* buffer, uint16 data) { | |
| 62 buffer[0] = static_cast<char>(data >> 8); | |
| 63 buffer[1] = static_cast<char>(data); | |
| 64 } | |
| 65 | |
| 66 void putU32(char* buffer, uint32 data) { | |
| 67 buffer[0] = static_cast<char>(data >> 24); | |
| 68 buffer[1] = static_cast<char>(data >> 16); | |
| 69 buffer[2] = static_cast<char>(data >> 8); | |
| 70 buffer[3] = static_cast<char>(data); | |
| 71 } | |
| 72 | |
| 73 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
| |
| 74 const char* data, | |
| 75 size_t size, | |
| 76 const sockaddr_in& target_addr) { | |
| 77 size_t bytes_sent = 0; | |
| 78 size_t bytes_remaining = size; | |
| 79 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.
| |
| 80 ssize_t ret = HANDLE_EINTR(sendto( | |
| 81 sock, data + bytes_sent, bytes_remaining, 0, | |
| 82 reinterpret_cast<const sockaddr*>(&target_addr), | |
| 83 sizeof(target_addr))); | |
| 84 if (ret < 0) | |
| 85 return false; | |
| 86 | |
| 87 bytes_sent += ret; | |
| 88 bytes_remaining -= ret; | |
| 89 } | |
| 90 return true; | |
| 91 } | |
| 92 | |
| 93 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.
| |
| 94 char response[kHeaderSize]; | |
| 95 memset(response, 0, kHeaderSize); | |
| 96 putU16(response, id); | |
| 97 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.
| |
| 98 SendPacketTo(sock, response, kHeaderSize, client_addr); | |
| 99 } | |
| 100 | |
| 101 void SendResponse(int sock, const sockaddr_in& client_addr, uint16 id, | |
| 102 uint16 qtype, const char* question, size_t question_length) { | |
| 103 size_t response_size = kHeaderSize + question_length + | |
| 104 (qtype == kDNS_A ? kAnswerSizeV4 : kAnswerSizeV6); | |
| 105 if (response_size > kMaxMessageSize) { | |
| 106 LOG(ERROR) << "Response is too large: " << response_size; | |
| 107 SendEmptyResponse(sock, client_addr, id); | |
| 108 return; | |
| 109 } | |
| 110 | |
| 111 char response[kMaxMessageSize]; | |
| 112 putU16(response, id); | |
| 113 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.
| |
| 114 putU16(response + 4, 1); // QDCOUNT | |
| 115 putU16(response + 6, 1); // ANCOUNT | |
| 116 putU16(response + 8, 0); // NSCOUNT | |
| 117 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.
| |
| 118 | |
| 119 // Repeat the question in the response. Some clients (e.g. ping) needs this. | |
| 120 memcpy(response + kHeaderSize, question, question_length); | |
| 121 | |
| 122 // Construct the answer. | |
| 123 char* answer = response + kHeaderSize + question_length; | |
| 124 putU16(answer, kNameReference); // References the QNAME in the question. | |
| 125 putU16(answer + 2, qtype); | |
| 126 putU16(answer + 4, net::kClassIN); | |
| 127 putU32(answer + 6, kTTL); | |
| 128 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.
| |
| 129 if (qtype == kDNS_A) | |
| 130 putU32(answer + 12, INADDR_LOOPBACK); | |
| 131 else | |
| 132 memcpy(answer + 12, &in6addr_loopback, sizeof(in6_addr)); | |
| 133 | |
| 134 SendPacketTo(sock, response, response_size, client_addr); | |
| 135 } | |
| 136 | |
| 137 void HandleRequest(int sock, const char* request, size_t size, | |
| 138 const sockaddr_in& client_addr) { | |
| 139 if (size < kMinRequestSize) { | |
| 140 LOG(ERROR) << "Request is too small " << size | |
| 141 << "\n" << DumpBinary(request, size); | |
| 142 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.
| |
| 143 return; | |
| 144 } | |
| 145 | |
| 146 uint16 id = getU16(request); | |
| 147 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.
| |
| 148 uint16 qdcount = getU16(request + 4); | |
| 149 uint16 ancount = getU16(request + 6); | |
| 150 uint16 nscount = getU16(request + 8); | |
| 151 uint16 arcount = getU16(request + 10); | |
| 152 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.
| |
| 153 LOG(ERROR) << "Unsupported request: FLAGS=" << flags | |
| 154 << " QDCOUNT=" << qdcount | |
| 155 << " ANCOUNT=" << ancount | |
| 156 << " NSCOUNT=" << nscount | |
| 157 << " ARCOUNT=" << arcount | |
| 158 << "\n" << DumpBinary(request, size); | |
| 159 SendEmptyResponse(sock, client_addr, id); | |
| 160 return; | |
| 161 } | |
| 162 | |
| 163 // Should be the end of the QNAME. | |
| 164 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
| |
| 165 LOG(ERROR) << "Error parsing QNAME" << "\n" << DumpBinary(request, size); | |
| 166 SendEmptyResponse(sock, client_addr, id); | |
| 167 return; | |
| 168 } | |
| 169 | |
| 170 uint16 qtype = getU16(request + size - 4); | |
| 171 uint16 qclass = getU16(request + size - 2); | |
| 172 if ((qtype != kDNS_A && qtype != kDNS_AAAA) || qclass != net::kClassIN) { | |
| 173 LOG(ERROR) << "Unsupported query: QTYPE=" << qtype << " QCLASS=" << qclass | |
| 174 << "\n" << DumpBinary(request, size); | |
| 175 SendEmptyResponse(sock, client_addr, id); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 SendResponse(sock, client_addr, id, qtype, | |
| 180 request + kHeaderSize, size - kHeaderSize); | |
| 181 } | |
| 182 | |
| 183 } // namespace | |
| 184 | |
| 185 int main(int argc, char** argv) { | |
| 186 printf("Fake DNS server\n"); | |
| 187 | |
| 188 CommandLine command_line(argc, argv); | |
| 189 if (tools::HasHelpSwitch(command_line) || command_line.GetArgs().size()) { | |
| 190 tools::ShowHelp(argv[0], "", ""); | |
| 191 return 0; | |
| 192 } | |
| 193 | |
| 194 int sock = socket(AF_INET, SOCK_DGRAM, 0); | |
| 195 if (sock < 0) { | |
| 196 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
| |
| 197 return 1; | |
| 198 } | |
| 199 | |
| 200 sockaddr_in addr; | |
| 201 memset(&addr, 0, sizeof(addr)); | |
| 202 addr.sin_family = AF_INET; | |
| 203 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
| 204 addr.sin_port = htons(53); | |
| 205 int reuse_addr = 1; | |
| 206 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.
| |
| 207 if (HANDLE_EINTR(bind(sock, reinterpret_cast<sockaddr*>(&addr), | |
| 208 sizeof(addr))) < 0) { | |
| 209 perror("server bind"); | |
| 210 HANDLE_EINTR(close(sock)); | |
| 211 return 1; | |
| 212 } | |
| 213 | |
| 214 if (!tools::HasNoSpawnDaemonSwitch(command_line)) | |
| 215 tools::SpawnDaemon(0); | |
| 216 | |
| 217 while (true) { | |
| 218 sockaddr_in client_addr; | |
| 219 socklen_t client_addr_len = sizeof(client_addr); | |
| 220 char request[kMaxMessageSize]; | |
| 221 int size = HANDLE_EINTR(recvfrom(sock, request, sizeof(request), | |
| 222 MSG_WAITALL, | |
| 223 reinterpret_cast<sockaddr*>(&client_addr), | |
| 224 &client_addr_len)); | |
| 225 if (size < 0) { | |
| 226 // Unrecoverable error, can only exit. | |
| 227 LOG(ERROR) << "Failed to receive a request: " << strerror(errno); | |
| 228 HANDLE_EINTR(close(sock)); | |
| 229 return 1; | |
| 230 } | |
| 231 | |
| 232 if (size > 0) | |
| 233 HandleRequest(sock, request, size, client_addr); | |
| 234 } | |
| 235 } | |
| 236 | |
| OLD | NEW |