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/big_endian.h" |
| 23 #include "net/base/net_util.h" |
| 24 #include "net/dns/dns_protocol.h" |
| 25 #include "tools/android/common/daemon.h" |
| 26 #include "tools/android/common/net.h" |
| 27 |
| 28 namespace { |
| 29 |
| 30 // Mininum request size: 1 question containing 1 QNAME, 1 TYPE and 1 CLASS. |
| 31 const size_t kMinRequestSize = sizeof(net::dns_protocol::Header) + 6; |
| 32 |
| 33 // The name reference in the answer pointing to the name in the query. |
| 34 // Its format is: highest two bits set to 1, then the offset of the name |
| 35 // which just follows the header. |
| 36 const uint16 kPointerToQueryName = |
| 37 static_cast<uint16>(0xc000 | sizeof(net::dns_protocol::Header)); |
| 38 |
| 39 const uint32 kTTL = 86400; // One day. |
| 40 |
| 41 void SendRefusedResponse(int sock, const sockaddr_in& client_addr, uint16 id) { |
| 42 net::dns_protocol::Header response; |
| 43 response.id = htons(id); |
| 44 response.flags = htons(net::dns_protocol::kFlagResponse | |
| 45 net::dns_protocol::kFlagAA | |
| 46 net::dns_protocol::kFlagRD | |
| 47 net::dns_protocol::kFlagRA | |
| 48 net::dns_protocol::kRcodeREFUSED); |
| 49 response.qdcount = 0; |
| 50 response.ancount = 0; |
| 51 response.nscount = 0; |
| 52 response.arcount = 0; |
| 53 HANDLE_EINTR(sendto(sock, &response, sizeof(response), 0, |
| 54 reinterpret_cast<const sockaddr*>(&client_addr), |
| 55 sizeof(client_addr))); |
| 56 } |
| 57 |
| 58 void SendResponse(int sock, const sockaddr_in& client_addr, uint16 id, |
| 59 uint16 qtype, const char* question, size_t question_length) { |
| 60 net::dns_protocol::Header header; |
| 61 header.id = htons(id); |
| 62 header.flags = htons(net::dns_protocol::kFlagResponse | |
| 63 net::dns_protocol::kFlagAA | |
| 64 net::dns_protocol::kFlagRD | |
| 65 net::dns_protocol::kFlagRA | |
| 66 net::dns_protocol::kRcodeNOERROR); |
| 67 header.qdcount = htons(1); |
| 68 header.ancount = htons(1); |
| 69 header.nscount = 0; |
| 70 header.arcount = 0; |
| 71 |
| 72 // Size of RDATA which is a IPv4 or IPv6 address. |
| 73 size_t rdata_size = qtype == net::dns_protocol::kTypeA ? |
| 74 net::kIPv4AddressSize : net::kIPv6AddressSize; |
| 75 |
| 76 // Size of the whole response which contains the header, the question and |
| 77 // the answer. 12 is the sum of sizes of the compressed name reference, TYPE, |
| 78 // CLASS, TTL and RDLENGTH. |
| 79 size_t response_size = sizeof(header) + question_length + 12 + rdata_size; |
| 80 |
| 81 if (response_size > net::dns_protocol::kMaxUDPSize) { |
| 82 LOG(ERROR) << "Response is too large: " << response_size; |
| 83 SendRefusedResponse(sock, client_addr, id); |
| 84 return; |
| 85 } |
| 86 |
| 87 char response[net::dns_protocol::kMaxUDPSize]; |
| 88 net::BigEndianWriter writer(response, arraysize(response)); |
| 89 writer.WriteBytes(&header, sizeof(header)); |
| 90 |
| 91 // Repeat the question in the response. Some clients (e.g. ping) needs this. |
| 92 writer.WriteBytes(question, question_length); |
| 93 |
| 94 // Construct the answer. |
| 95 writer.WriteU16(kPointerToQueryName); |
| 96 writer.WriteU16(qtype); |
| 97 writer.WriteU16(net::dns_protocol::kClassIN); |
| 98 writer.WriteU32(kTTL); |
| 99 writer.WriteU16(rdata_size); |
| 100 if (qtype == net::dns_protocol::kTypeA) |
| 101 writer.WriteU32(INADDR_LOOPBACK); |
| 102 else |
| 103 writer.WriteBytes(&in6addr_loopback, sizeof(in6_addr)); |
| 104 DCHECK(writer.ptr() - response == response_size); |
| 105 |
| 106 HANDLE_EINTR(sendto(sock, response, response_size, 0, |
| 107 reinterpret_cast<const sockaddr*>(&client_addr), |
| 108 sizeof(client_addr))); |
| 109 } |
| 110 |
| 111 void HandleRequest(int sock, const char* request, size_t size, |
| 112 const sockaddr_in& client_addr) { |
| 113 if (size < kMinRequestSize) { |
| 114 LOG(ERROR) << "Request is too small " << size |
| 115 << "\n" << tools::DumpBinary(request, size); |
| 116 return; |
| 117 } |
| 118 |
| 119 net::BigEndianReader reader(request, size); |
| 120 net::dns_protocol::Header header; |
| 121 reader.ReadBytes(&header, sizeof(header)); |
| 122 uint16 id = ntohs(header.id); |
| 123 uint16 flags = ntohs(header.flags); |
| 124 uint16 qdcount = ntohs(header.qdcount); |
| 125 uint16 ancount = ntohs(header.ancount); |
| 126 uint16 nscount = ntohs(header.nscount); |
| 127 uint16 arcount = ntohs(header.arcount); |
| 128 |
| 129 const uint16 kAllowedFlags = 0x07ff; |
| 130 if ((flags & ~kAllowedFlags) || |
| 131 qdcount != 1 || ancount || nscount || arcount) { |
| 132 LOG(ERROR) << "Unsupported request: FLAGS=" << flags |
| 133 << " QDCOUNT=" << qdcount |
| 134 << " ANCOUNT=" << ancount |
| 135 << " NSCOUNT=" << nscount |
| 136 << " ARCOUNT=" << arcount |
| 137 << "\n" << tools::DumpBinary(request, size); |
| 138 SendRefusedResponse(sock, client_addr, id); |
| 139 return; |
| 140 } |
| 141 |
| 142 // request[size - 5] should be the end of the QNAME (a zero byte). |
| 143 // We don't care about the validity of QNAME because we don't parse it. |
| 144 const char* qname_end = &request[size - 5]; |
| 145 if (*qname_end) { |
| 146 LOG(ERROR) << "Error parsing QNAME\n" << tools::DumpBinary(request, size); |
| 147 SendRefusedResponse(sock, client_addr, id); |
| 148 return; |
| 149 } |
| 150 |
| 151 reader.Skip(qname_end - reader.ptr() + 1); |
| 152 |
| 153 uint16 qtype; |
| 154 uint16 qclass; |
| 155 reader.ReadU16(&qtype); |
| 156 reader.ReadU16(&qclass); |
| 157 if ((qtype != net::dns_protocol::kTypeA && |
| 158 qtype != net::dns_protocol::kTypeAAAA) || |
| 159 qclass != net::dns_protocol::kClassIN) { |
| 160 LOG(ERROR) << "Unsupported query: QTYPE=" << qtype << " QCLASS=" << qclass |
| 161 << "\n" << tools::DumpBinary(request, size); |
| 162 SendRefusedResponse(sock, client_addr, id); |
| 163 return; |
| 164 } |
| 165 |
| 166 SendResponse(sock, client_addr, id, qtype, |
| 167 request + sizeof(header), size - sizeof(header)); |
| 168 } |
| 169 |
| 170 } // namespace |
| 171 |
| 172 int main(int argc, char** argv) { |
| 173 printf("Fake DNS server\n"); |
| 174 |
| 175 CommandLine command_line(argc, argv); |
| 176 if (tools::HasHelpSwitch(command_line) || command_line.GetArgs().size()) { |
| 177 tools::ShowHelp(argv[0], "", ""); |
| 178 return 0; |
| 179 } |
| 180 |
| 181 int sock = socket(AF_INET, SOCK_DGRAM, 0); |
| 182 if (sock < 0) { |
| 183 perror("create socket"); |
| 184 return 1; |
| 185 } |
| 186 |
| 187 sockaddr_in addr; |
| 188 memset(&addr, 0, sizeof(addr)); |
| 189 addr.sin_family = AF_INET; |
| 190 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| 191 addr.sin_port = htons(53); |
| 192 int reuse_addr = 1; |
| 193 if (HANDLE_EINTR(bind(sock, reinterpret_cast<sockaddr*>(&addr), |
| 194 sizeof(addr))) < 0) { |
| 195 perror("server bind"); |
| 196 HANDLE_EINTR(close(sock)); |
| 197 return 1; |
| 198 } |
| 199 |
| 200 if (!tools::HasNoSpawnDaemonSwitch(command_line)) |
| 201 tools::SpawnDaemon(0); |
| 202 |
| 203 while (true) { |
| 204 sockaddr_in client_addr; |
| 205 socklen_t client_addr_len = sizeof(client_addr); |
| 206 char request[net::dns_protocol::kMaxUDPSize]; |
| 207 int size = HANDLE_EINTR(recvfrom(sock, request, sizeof(request), |
| 208 MSG_WAITALL, |
| 209 reinterpret_cast<sockaddr*>(&client_addr), |
| 210 &client_addr_len)); |
| 211 if (size < 0) { |
| 212 // Unrecoverable error, can only exit. |
| 213 LOG(ERROR) << "Failed to receive a request: " << strerror(errno); |
| 214 HANDLE_EINTR(close(sock)); |
| 215 return 1; |
| 216 } |
| 217 |
| 218 if (size > 0) |
| 219 HandleRequest(sock, request, size, client_addr); |
| 220 } |
| 221 } |
| 222 |
OLD | NEW |