| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "dns_responder.h" |
| |
| #include <arpa/inet.h> |
| #include <fcntl.h> |
| #include <netdb.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/epoll.h> |
| #include <sys/eventfd.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <chrono> |
| #include <iostream> |
| #include <set> |
| #include <vector> |
| |
| #define LOG_TAG "DNSResponder" |
| #include <android-base/logging.h> |
| #include <android-base/strings.h> |
| #include <netdutils/InternetAddresses.h> |
| #include <netdutils/Slice.h> |
| #include <netdutils/SocketOption.h> |
| |
| using android::netdutils::enableSockopt; |
| using android::netdutils::ScopedAddrinfo; |
| using android::netdutils::Slice; |
| |
| namespace test { |
| |
| std::string errno2str() { |
| char error_msg[512] = {0}; |
| // It actually calls __gnu_strerror_r() which returns the type |char*| rather than |int|. |
| // PLOG is an option though it requires lots of changes from ALOGx() to LOG(x). |
| return strerror_r(errno, error_msg, sizeof(error_msg)); |
| } |
| |
| std::string str2hex(const char* buffer, size_t len) { |
| std::string str(len * 2, '\0'); |
| for (size_t i = 0; i < len; ++i) { |
| static const char* hex = "0123456789ABCDEF"; |
| uint8_t c = buffer[i]; |
| str[i * 2] = hex[c >> 4]; |
| str[i * 2 + 1] = hex[c & 0x0F]; |
| } |
| return str; |
| } |
| |
| std::string addr2str(const sockaddr* sa, socklen_t sa_len) { |
| char host_str[NI_MAXHOST] = {0}; |
| int rv = getnameinfo(sa, sa_len, host_str, sizeof(host_str), nullptr, 0, NI_NUMERICHOST); |
| if (rv == 0) return std::string(host_str); |
| return std::string(); |
| } |
| |
| /* DNS struct helpers */ |
| |
| const char* dnstype2str(unsigned dnstype) { |
| static std::unordered_map<unsigned, const char*> kTypeStrs = { |
| {ns_type::ns_t_a, "A"}, |
| {ns_type::ns_t_ns, "NS"}, |
| {ns_type::ns_t_md, "MD"}, |
| {ns_type::ns_t_mf, "MF"}, |
| {ns_type::ns_t_cname, "CNAME"}, |
| {ns_type::ns_t_soa, "SOA"}, |
| {ns_type::ns_t_mb, "MB"}, |
| {ns_type::ns_t_mb, "MG"}, |
| {ns_type::ns_t_mr, "MR"}, |
| {ns_type::ns_t_null, "NULL"}, |
| {ns_type::ns_t_wks, "WKS"}, |
| {ns_type::ns_t_ptr, "PTR"}, |
| {ns_type::ns_t_hinfo, "HINFO"}, |
| {ns_type::ns_t_minfo, "MINFO"}, |
| {ns_type::ns_t_mx, "MX"}, |
| {ns_type::ns_t_txt, "TXT"}, |
| {ns_type::ns_t_rp, "RP"}, |
| {ns_type::ns_t_afsdb, "AFSDB"}, |
| {ns_type::ns_t_x25, "X25"}, |
| {ns_type::ns_t_isdn, "ISDN"}, |
| {ns_type::ns_t_rt, "RT"}, |
| {ns_type::ns_t_nsap, "NSAP"}, |
| {ns_type::ns_t_nsap_ptr, "NSAP-PTR"}, |
| {ns_type::ns_t_sig, "SIG"}, |
| {ns_type::ns_t_key, "KEY"}, |
| {ns_type::ns_t_px, "PX"}, |
| {ns_type::ns_t_gpos, "GPOS"}, |
| {ns_type::ns_t_aaaa, "AAAA"}, |
| {ns_type::ns_t_loc, "LOC"}, |
| {ns_type::ns_t_nxt, "NXT"}, |
| {ns_type::ns_t_eid, "EID"}, |
| {ns_type::ns_t_nimloc, "NIMLOC"}, |
| {ns_type::ns_t_srv, "SRV"}, |
| {ns_type::ns_t_naptr, "NAPTR"}, |
| {ns_type::ns_t_kx, "KX"}, |
| {ns_type::ns_t_cert, "CERT"}, |
| {ns_type::ns_t_a6, "A6"}, |
| {ns_type::ns_t_dname, "DNAME"}, |
| {ns_type::ns_t_sink, "SINK"}, |
| {ns_type::ns_t_opt, "OPT"}, |
| {ns_type::ns_t_apl, "APL"}, |
| {ns_type::ns_t_tkey, "TKEY"}, |
| {ns_type::ns_t_tsig, "TSIG"}, |
| {ns_type::ns_t_ixfr, "IXFR"}, |
| {ns_type::ns_t_axfr, "AXFR"}, |
| {ns_type::ns_t_mailb, "MAILB"}, |
| {ns_type::ns_t_maila, "MAILA"}, |
| {ns_type::ns_t_any, "ANY"}, |
| {ns_type::ns_t_zxfr, "ZXFR"}, |
| }; |
| auto it = kTypeStrs.find(dnstype); |
| static const char* kUnknownStr{"UNKNOWN"}; |
| if (it == kTypeStrs.end()) return kUnknownStr; |
| return it->second; |
| } |
| |
| const char* dnsclass2str(unsigned dnsclass) { |
| static std::unordered_map<unsigned, const char*> kClassStrs = { |
| {ns_class::ns_c_in, "Internet"}, {2, "CSNet"}, |
| {ns_class::ns_c_chaos, "ChaosNet"}, {ns_class::ns_c_hs, "Hesiod"}, |
| {ns_class::ns_c_none, "none"}, {ns_class::ns_c_any, "any"}}; |
| auto it = kClassStrs.find(dnsclass); |
| static const char* kUnknownStr{"UNKNOWN"}; |
| if (it == kClassStrs.end()) return kUnknownStr; |
| return it->second; |
| } |
| |
| const char* dnsproto2str(int protocol) { |
| switch (protocol) { |
| case IPPROTO_TCP: |
| return "TCP"; |
| case IPPROTO_UDP: |
| return "UDP"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| const char* DNSName::read(const char* buffer, const char* buffer_end) { |
| const char* cur = buffer; |
| bool last = false; |
| do { |
| cur = parseField(cur, buffer_end, &last); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| } while (!last); |
| return cur; |
| } |
| |
| char* DNSName::write(char* buffer, const char* buffer_end) const { |
| char* buffer_cur = buffer; |
| for (size_t pos = 0; pos < name.size();) { |
| size_t dot_pos = name.find('.', pos); |
| if (dot_pos == std::string::npos) { |
| // Sanity check, should never happen unless parseField is broken. |
| LOG(ERROR) << "logic error: all names are expected to end with a '.'"; |
| return nullptr; |
| } |
| const size_t len = dot_pos - pos; |
| if (len >= 256) { |
| LOG(ERROR) << "name component '" << name.substr(pos, dot_pos - pos) << "' is " << len |
| << " long, but max is 255"; |
| return nullptr; |
| } |
| if (buffer_cur + sizeof(uint8_t) + len > buffer_end) { |
| LOG(ERROR) << "buffer overflow at line " << __LINE__; |
| return nullptr; |
| } |
| *buffer_cur++ = len; |
| buffer_cur = std::copy(std::next(name.begin(), pos), std::next(name.begin(), dot_pos), |
| buffer_cur); |
| pos = dot_pos + 1; |
| } |
| // Write final zero. |
| *buffer_cur++ = 0; |
| return buffer_cur; |
| } |
| |
| const char* DNSName::parseField(const char* buffer, const char* buffer_end, bool* last) { |
| if (buffer + sizeof(uint8_t) > buffer_end) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| unsigned field_type = *buffer >> 6; |
| unsigned ofs = *buffer & 0x3F; |
| const char* cur = buffer + sizeof(uint8_t); |
| if (field_type == 0) { |
| // length + name component |
| if (ofs == 0) { |
| *last = true; |
| return cur; |
| } |
| if (cur + ofs > buffer_end) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| name.append(cur, ofs); |
| name.push_back('.'); |
| return cur + ofs; |
| } else if (field_type == 3) { |
| LOG(ERROR) << "name compression not implemented"; |
| return nullptr; |
| } |
| LOG(ERROR) << "invalid name field type"; |
| return nullptr; |
| } |
| |
| const char* DNSQuestion::read(const char* buffer, const char* buffer_end) { |
| const char* cur = qname.read(buffer, buffer_end); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| if (cur + 2 * sizeof(uint16_t) > buffer_end) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| qtype = ntohs(*reinterpret_cast<const uint16_t*>(cur)); |
| qclass = ntohs(*reinterpret_cast<const uint16_t*>(cur + sizeof(uint16_t))); |
| return cur + 2 * sizeof(uint16_t); |
| } |
| |
| char* DNSQuestion::write(char* buffer, const char* buffer_end) const { |
| char* buffer_cur = qname.write(buffer, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| if (buffer_cur + 2 * sizeof(uint16_t) > buffer_end) { |
| LOG(ERROR) << "buffer overflow on line " << __LINE__; |
| return nullptr; |
| } |
| *reinterpret_cast<uint16_t*>(buffer_cur) = htons(qtype); |
| *reinterpret_cast<uint16_t*>(buffer_cur + sizeof(uint16_t)) = htons(qclass); |
| return buffer_cur + 2 * sizeof(uint16_t); |
| } |
| |
| std::string DNSQuestion::toString() const { |
| char buffer[4096]; |
| int len = snprintf(buffer, sizeof(buffer), "Q<%s,%s,%s>", qname.name.c_str(), |
| dnstype2str(qtype), dnsclass2str(qclass)); |
| return std::string(buffer, len); |
| } |
| |
| const char* DNSRecord::read(const char* buffer, const char* buffer_end) { |
| const char* cur = name.read(buffer, buffer_end); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| unsigned rdlen = 0; |
| cur = readIntFields(cur, buffer_end, &rdlen); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| if (cur + rdlen > buffer_end) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| rdata.assign(cur, cur + rdlen); |
| return cur + rdlen; |
| } |
| |
| char* DNSRecord::write(char* buffer, const char* buffer_end) const { |
| char* buffer_cur = name.write(buffer, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| buffer_cur = writeIntFields(rdata.size(), buffer_cur, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| if (buffer_cur + rdata.size() > buffer_end) { |
| LOG(ERROR) << "buffer overflow on line " << __LINE__; |
| return nullptr; |
| } |
| return std::copy(rdata.begin(), rdata.end(), buffer_cur); |
| } |
| |
| std::string DNSRecord::toString() const { |
| char buffer[4096]; |
| int len = snprintf(buffer, sizeof(buffer), "R<%s,%s,%s>", name.name.c_str(), dnstype2str(rtype), |
| dnsclass2str(rclass)); |
| return std::string(buffer, len); |
| } |
| |
| const char* DNSRecord::readIntFields(const char* buffer, const char* buffer_end, unsigned* rdlen) { |
| if (buffer + sizeof(IntFields) > buffer_end) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| const auto& intfields = *reinterpret_cast<const IntFields*>(buffer); |
| rtype = ntohs(intfields.rtype); |
| rclass = ntohs(intfields.rclass); |
| ttl = ntohl(intfields.ttl); |
| *rdlen = ntohs(intfields.rdlen); |
| return buffer + sizeof(IntFields); |
| } |
| |
| char* DNSRecord::writeIntFields(unsigned rdlen, char* buffer, const char* buffer_end) const { |
| if (buffer + sizeof(IntFields) > buffer_end) { |
| LOG(ERROR) << "buffer overflow on line " << __LINE__; |
| return nullptr; |
| } |
| auto& intfields = *reinterpret_cast<IntFields*>(buffer); |
| intfields.rtype = htons(rtype); |
| intfields.rclass = htons(rclass); |
| intfields.ttl = htonl(ttl); |
| intfields.rdlen = htons(rdlen); |
| return buffer + sizeof(IntFields); |
| } |
| |
| const char* DNSHeader::read(const char* buffer, const char* buffer_end) { |
| unsigned qdcount; |
| unsigned ancount; |
| unsigned nscount; |
| unsigned arcount; |
| const char* cur = readHeader(buffer, buffer_end, &qdcount, &ancount, &nscount, &arcount); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| if (qdcount) { |
| questions.resize(qdcount); |
| for (unsigned i = 0; i < qdcount; ++i) { |
| cur = questions[i].read(cur, buffer_end); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| } |
| } |
| if (ancount) { |
| answers.resize(ancount); |
| for (unsigned i = 0; i < ancount; ++i) { |
| cur = answers[i].read(cur, buffer_end); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| } |
| } |
| if (nscount) { |
| authorities.resize(nscount); |
| for (unsigned i = 0; i < nscount; ++i) { |
| cur = authorities[i].read(cur, buffer_end); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| } |
| } |
| if (arcount) { |
| additionals.resize(arcount); |
| for (unsigned i = 0; i < arcount; ++i) { |
| cur = additionals[i].read(cur, buffer_end); |
| if (cur == nullptr) { |
| LOG(ERROR) << "parsing failed at line " << __LINE__; |
| return nullptr; |
| } |
| } |
| } |
| return cur; |
| } |
| |
| char* DNSHeader::write(char* buffer, const char* buffer_end) const { |
| if (buffer + sizeof(Header) > buffer_end) { |
| LOG(ERROR) << "buffer overflow on line " << __LINE__; |
| return nullptr; |
| } |
| Header& header = *reinterpret_cast<Header*>(buffer); |
| // bytes 0-1 |
| header.id = htons(id); |
| // byte 2: 7:qr, 3-6:opcode, 2:aa, 1:tr, 0:rd |
| header.flags0 = (qr << 7) | (opcode << 3) | (aa << 2) | (tr << 1) | rd; |
| // byte 3: 7:ra, 6:zero, 5:ad, 4:cd, 0-3:rcode |
| // Fake behavior: if the query set the "ad" bit, set it in the response too. |
| // In a real server, this should be set only if the data is authentic and the |
| // query contained an "ad" bit or DNSSEC extensions. |
| header.flags1 = (ad << 5) | rcode; |
| // rest of header |
| header.qdcount = htons(questions.size()); |
| header.ancount = htons(answers.size()); |
| header.nscount = htons(authorities.size()); |
| header.arcount = htons(additionals.size()); |
| char* buffer_cur = buffer + sizeof(Header); |
| for (const DNSQuestion& question : questions) { |
| buffer_cur = question.write(buffer_cur, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| } |
| for (const DNSRecord& answer : answers) { |
| buffer_cur = answer.write(buffer_cur, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| } |
| for (const DNSRecord& authority : authorities) { |
| buffer_cur = authority.write(buffer_cur, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| } |
| for (const DNSRecord& additional : additionals) { |
| buffer_cur = additional.write(buffer_cur, buffer_end); |
| if (buffer_cur == nullptr) return nullptr; |
| } |
| return buffer_cur; |
| } |
| |
| // TODO: convert all callers to this interface, then delete the old one. |
| bool DNSHeader::write(std::vector<uint8_t>* out) const { |
| char buffer[4096]; |
| char* end = this->write(buffer, buffer + sizeof buffer); |
| if (end == nullptr) return false; |
| out->insert(out->end(), buffer, end); |
| return true; |
| } |
| |
| std::string DNSHeader::toString() const { |
| // TODO |
| return std::string(); |
| } |
| |
| const char* DNSHeader::readHeader(const char* buffer, const char* buffer_end, unsigned* qdcount, |
| unsigned* ancount, unsigned* nscount, unsigned* arcount) { |
| if (buffer + sizeof(Header) > buffer_end) return nullptr; |
| const auto& header = *reinterpret_cast<const Header*>(buffer); |
| // bytes 0-1 |
| id = ntohs(header.id); |
| // byte 2: 7:qr, 3-6:opcode, 2:aa, 1:tr, 0:rd |
| qr = header.flags0 >> 7; |
| opcode = (header.flags0 >> 3) & 0x0F; |
| aa = (header.flags0 >> 2) & 1; |
| tr = (header.flags0 >> 1) & 1; |
| rd = header.flags0 & 1; |
| // byte 3: 7:ra, 6:zero, 5:ad, 4:cd, 0-3:rcode |
| ra = header.flags1 >> 7; |
| ad = (header.flags1 >> 5) & 1; |
| rcode = header.flags1 & 0xF; |
| // rest of header |
| *qdcount = ntohs(header.qdcount); |
| *ancount = ntohs(header.ancount); |
| *nscount = ntohs(header.nscount); |
| *arcount = ntohs(header.arcount); |
| return buffer + sizeof(Header); |
| } |
| |
| /* DNS responder */ |
| |
| DNSResponder::DNSResponder(std::string listen_address, std::string listen_service, |
| ns_rcode error_rcode, MappingType mapping_type) |
| : listen_address_(std::move(listen_address)), |
| listen_service_(std::move(listen_service)), |
| error_rcode_(error_rcode), |
| mapping_type_(mapping_type) {} |
| |
| DNSResponder::~DNSResponder() { |
| stopServer(); |
| } |
| |
| void DNSResponder::addMapping(const std::string& name, ns_type type, const std::string& addr) { |
| std::lock_guard lock(mappings_mutex_); |
| mappings_[{name, type}] = addr; |
| } |
| |
| void DNSResponder::addMappingDnsHeader(const std::string& name, ns_type type, |
| const DNSHeader& header) { |
| std::lock_guard lock(mappings_mutex_); |
| dnsheader_mappings_[{name, type}] = header; |
| } |
| |
| void DNSResponder::addMappingBinaryPacket(const std::vector<uint8_t>& query, |
| const std::vector<uint8_t>& response) { |
| std::lock_guard lock(mappings_mutex_); |
| packet_mappings_[query] = response; |
| } |
| |
| void DNSResponder::removeMapping(const std::string& name, ns_type type) { |
| std::lock_guard lock(mappings_mutex_); |
| if (!mappings_.erase({name, type})) { |
| LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) |
| << "), not present in registered mappings"; |
| } |
| } |
| |
| void DNSResponder::removeMappingDnsHeader(const std::string& name, ns_type type) { |
| std::lock_guard lock(mappings_mutex_); |
| if (!dnsheader_mappings_.erase({name, type})) { |
| LOG(ERROR) << "Cannot remove mapping from (" << name << ", " << dnstype2str(type) |
| << "), not present in registered DnsHeader mappings"; |
| } |
| } |
| |
| void DNSResponder::removeMappingBinaryPacket(const std::vector<uint8_t>& query) { |
| std::lock_guard lock(mappings_mutex_); |
| if (!packet_mappings_.erase(query)) { |
| LOG(ERROR) << "Cannot remove mapping, not present in registered BinaryPacket mappings"; |
| LOG(INFO) << "Hex dump:"; |
| LOG(INFO) << android::netdutils::toHex( |
| Slice(const_cast<uint8_t*>(query.data()), query.size()), 32); |
| } |
| } |
| |
| // Set response probability on all supported protocols. |
| void DNSResponder::setResponseProbability(double response_probability) { |
| setResponseProbability(response_probability, IPPROTO_TCP); |
| setResponseProbability(response_probability, IPPROTO_UDP); |
| } |
| |
| void DNSResponder::setResponseDelayMs(unsigned timeMs) { |
| response_delayed_ms_ = timeMs; |
| } |
| |
| // Set response probability on specific protocol. It's caller's duty to ensure that the |protocol| |
| // can be supported by DNSResponder. |
| void DNSResponder::setResponseProbability(double response_probability, int protocol) { |
| switch (protocol) { |
| case IPPROTO_TCP: |
| response_probability_tcp_ = response_probability; |
| break; |
| case IPPROTO_UDP: |
| response_probability_udp_ = response_probability; |
| break; |
| default: |
| LOG(FATAL) << "Unsupported protocol " << protocol; // abort() by log level FATAL |
| } |
| } |
| |
| double DNSResponder::getResponseProbability(int protocol) const { |
| switch (protocol) { |
| case IPPROTO_TCP: |
| return response_probability_tcp_; |
| case IPPROTO_UDP: |
| return response_probability_udp_; |
| default: |
| LOG(FATAL) << "Unsupported protocol " << protocol; // abort() by log level FATAL |
| // unreachable |
| return -1; |
| } |
| } |
| |
| void DNSResponder::setEdns(Edns edns) { |
| edns_ = edns; |
| } |
| |
| void DNSResponder::setTtl(unsigned ttl) { |
| answer_record_ttl_sec_ = ttl; |
| } |
| |
| bool DNSResponder::running() const { |
| return (udp_socket_.ok()) && (tcp_socket_.ok()); |
| } |
| |
| bool DNSResponder::startServer() { |
| if (running()) { |
| LOG(ERROR) << "server already running"; |
| return false; |
| } |
| |
| // Create UDP, TCP socket |
| if (udp_socket_ = createListeningSocket(SOCK_DGRAM); udp_socket_.get() < 0) { |
| PLOG(ERROR) << "failed to create UDP socket"; |
| return false; |
| } |
| |
| if (tcp_socket_ = createListeningSocket(SOCK_STREAM); tcp_socket_.get() < 0) { |
| PLOG(ERROR) << "failed to create TCP socket"; |
| return false; |
| } |
| |
| if (listen(tcp_socket_.get(), 1) < 0) { |
| PLOG(ERROR) << "failed to listen TCP socket"; |
| return false; |
| } |
| |
| // Set up eventfd socket. |
| event_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); |
| if (event_fd_.get() == -1) { |
| PLOG(ERROR) << "failed to create eventfd"; |
| return false; |
| } |
| |
| // Set up epoll socket. |
| epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC)); |
| if (epoll_fd_.get() < 0) { |
| PLOG(ERROR) << "epoll_create1() failed on fd"; |
| return false; |
| } |
| |
| LOG(INFO) << "adding UDP socket to epoll"; |
| if (!addFd(udp_socket_.get(), EPOLLIN)) { |
| LOG(ERROR) << "failed to add the UDP socket to epoll"; |
| return false; |
| } |
| |
| LOG(INFO) << "adding TCP socket to epoll"; |
| if (!addFd(tcp_socket_.get(), EPOLLIN)) { |
| LOG(ERROR) << "failed to add the TCP socket to epoll"; |
| return false; |
| } |
| |
| LOG(INFO) << "adding eventfd to epoll"; |
| if (!addFd(event_fd_.get(), EPOLLIN)) { |
| LOG(ERROR) << "failed to add the eventfd to epoll"; |
| return false; |
| } |
| |
| { |
| std::lock_guard lock(update_mutex_); |
| handler_thread_ = std::thread(&DNSResponder::requestHandler, this); |
| } |
| LOG(INFO) << "server started successfully"; |
| return true; |
| } |
| |
| bool DNSResponder::stopServer() { |
| std::lock_guard lock(update_mutex_); |
| if (!running()) { |
| LOG(ERROR) << "server not running"; |
| return false; |
| } |
| LOG(INFO) << "stopping server"; |
| if (!sendToEventFd()) { |
| return false; |
| } |
| handler_thread_.join(); |
| epoll_fd_.reset(); |
| event_fd_.reset(); |
| udp_socket_.reset(); |
| tcp_socket_.reset(); |
| LOG(INFO) << "server stopped successfully"; |
| return true; |
| } |
| |
| std::vector<DNSResponder::QueryInfo> DNSResponder::queries() const { |
| std::lock_guard lock(queries_mutex_); |
| return queries_; |
| } |
| |
| std::string DNSResponder::dumpQueries() const { |
| std::lock_guard lock(queries_mutex_); |
| std::string out; |
| |
| for (const auto& q : queries_) { |
| out += "{\"" + q.name + "\", " + std::to_string(q.type) + "\", " + |
| dnsproto2str(q.protocol) + "} "; |
| } |
| return out; |
| } |
| |
| void DNSResponder::clearQueries() { |
| std::lock_guard lock(queries_mutex_); |
| queries_.clear(); |
| } |
| |
| bool DNSResponder::hasOptPseudoRR(DNSHeader* header) const { |
| if (header->additionals.empty()) return false; |
| |
| // OPT RR may be placed anywhere within the additional section. See RFC 6891 section 6.1.1. |
| auto found = std::find_if(header->additionals.begin(), header->additionals.end(), |
| [](const auto& a) { return a.rtype == ns_type::ns_t_opt; }); |
| return found != header->additionals.end(); |
| } |
| |
| void DNSResponder::requestHandler() { |
| epoll_event evs[EPOLL_MAX_EVENTS]; |
| while (true) { |
| int n = epoll_wait(epoll_fd_.get(), evs, EPOLL_MAX_EVENTS, -1); |
| if (n <= 0) { |
| PLOG(ERROR) << "epoll_wait() failed, n=" << n; |
| return; |
| } |
| |
| for (int i = 0; i < n; i++) { |
| const int fd = evs[i].data.fd; |
| const uint32_t events = evs[i].events; |
| if (fd == event_fd_.get() && (events & (EPOLLIN | EPOLLERR))) { |
| handleEventFd(); |
| return; |
| } else if (fd == udp_socket_.get() && (events & (EPOLLIN | EPOLLERR))) { |
| handleQuery(IPPROTO_UDP); |
| } else if (fd == tcp_socket_.get() && (events & (EPOLLIN | EPOLLERR))) { |
| handleQuery(IPPROTO_TCP); |
| } else { |
| LOG(WARNING) << "unexpected epoll events " << events << " on fd " << fd; |
| } |
| } |
| } |
| } |
| |
| bool DNSResponder::handleDNSRequest(const char* buffer, ssize_t len, int protocol, char* response, |
| size_t* response_len) const { |
| LOG(DEBUG) << "request: '" << str2hex(buffer, len) << "', on " << dnsproto2str(protocol); |
| const char* buffer_end = buffer + len; |
| DNSHeader header; |
| const char* cur = header.read(buffer, buffer_end); |
| // TODO(imaipi): for now, unparsable messages are silently dropped, fix. |
| if (cur == nullptr) { |
| LOG(ERROR) << "failed to parse query"; |
| return false; |
| } |
| if (header.qr) { |
| LOG(ERROR) << "response received instead of a query"; |
| return false; |
| } |
| if (header.opcode != ns_opcode::ns_o_query) { |
| LOG(INFO) << "unsupported request opcode received"; |
| return makeErrorResponse(&header, ns_rcode::ns_r_notimpl, response, response_len); |
| } |
| if (header.questions.empty()) { |
| LOG(INFO) << "no questions present"; |
| return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response, response_len); |
| } |
| if (!header.answers.empty()) { |
| LOG(INFO) << "already " << header.answers.size() << " answers present in query"; |
| return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response, response_len); |
| } |
| |
| if (edns_ == Edns::FORMERR_UNCOND) { |
| LOG(INFO) << "force to return RCODE FORMERR"; |
| return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response, response_len); |
| } |
| |
| if (!header.additionals.empty() && edns_ != Edns::ON) { |
| LOG(INFO) << "DNS request has an additional section (assumed EDNS). Simulating an ancient " |
| "(pre-EDNS) server, and returning " |
| << (edns_ == Edns::FORMERR_ON_EDNS ? "RCODE FORMERR." : "no response."); |
| if (edns_ == Edns::FORMERR_ON_EDNS) { |
| return makeErrorResponse(&header, ns_rcode::ns_r_formerr, response, response_len); |
| } |
| // No response. |
| return false; |
| } |
| { |
| std::lock_guard lock(queries_mutex_); |
| for (const DNSQuestion& question : header.questions) { |
| queries_.push_back({question.qname.name, ns_type(question.qtype), protocol}); |
| } |
| } |
| // Ignore requests with the preset probability. |
| auto constexpr bound = std::numeric_limits<unsigned>::max(); |
| if (arc4random_uniform(bound) > bound * getResponseProbability(protocol)) { |
| if (error_rcode_ < 0) { |
| LOG(ERROR) << "Returning no response"; |
| return false; |
| } else { |
| LOG(INFO) << "returning RCODE " << static_cast<int>(error_rcode_) |
| << " in accordance with probability distribution"; |
| return makeErrorResponse(&header, error_rcode_, response, response_len); |
| } |
| } |
| |
| // Make the response. The query has been read into |header| which is used to build and return |
| // the response as well. |
| return makeResponse(&header, protocol, response, response_len); |
| } |
| |
| bool DNSResponder::addAnswerRecords(const DNSQuestion& question, |
| std::vector<DNSRecord>* answers) const { |
| std::lock_guard guard(mappings_mutex_); |
| std::string rname = question.qname.name; |
| std::vector<int> rtypes; |
| |
| if (question.qtype == ns_type::ns_t_a || question.qtype == ns_type::ns_t_aaaa || |
| question.qtype == ns_type::ns_t_ptr) |
| rtypes.push_back(ns_type::ns_t_cname); |
| rtypes.push_back(question.qtype); |
| for (int rtype : rtypes) { |
| std::set<std::string> cnames_Loop; |
| std::unordered_map<QueryKey, std::string, QueryKeyHash>::const_iterator it; |
| while ((it = mappings_.find(QueryKey(rname, rtype))) != mappings_.end()) { |
| if (rtype == ns_type::ns_t_cname) { |
| // When detect CNAME infinite loops by cnames_Loop, it won't save the duplicate one. |
| // As following, the query will stop on loop3 by detecting the same cname. |
| // loop1.{"a.xxx.com", ns_type::ns_t_cname, "b.xxx.com"}(insert in answer record) |
| // loop2.{"b.xxx.com", ns_type::ns_t_cname, "a.xxx.com"}(insert in answer record) |
| // loop3.{"a.xxx.com", ns_type::ns_t_cname, "b.xxx.com"}(When the same cname record |
| // is found in cnames_Loop already, break the query loop.) |
| if (cnames_Loop.find(it->first.name) != cnames_Loop.end()) break; |
| cnames_Loop.insert(it->first.name); |
| } |
| DNSRecord record{ |
| .name = {.name = it->first.name}, |
| .rtype = it->first.type, |
| .rclass = ns_class::ns_c_in, |
| .ttl = answer_record_ttl_sec_, // seconds |
| }; |
| if (!fillRdata(it->second, record)) return false; |
| answers->push_back(std::move(record)); |
| if (rtype != ns_type::ns_t_cname) break; |
| rname = it->second; |
| } |
| } |
| |
| if (answers->size() == 0) { |
| // TODO(imaipi): handle correctly |
| LOG(INFO) << "no mapping found for " << question.qname.name << " " |
| << dnstype2str(question.qtype) << ", lazily refusing to add an answer"; |
| } |
| |
| return true; |
| } |
| |
| bool DNSResponder::fillRdata(const std::string& rdatastr, DNSRecord& record) { |
| if (record.rtype == ns_type::ns_t_a) { |
| record.rdata.resize(4); |
| if (inet_pton(AF_INET, rdatastr.c_str(), record.rdata.data()) != 1) { |
| LOG(ERROR) << "inet_pton(AF_INET, " << rdatastr << ") failed"; |
| return false; |
| } |
| } else if (record.rtype == ns_type::ns_t_aaaa) { |
| record.rdata.resize(16); |
| if (inet_pton(AF_INET6, rdatastr.c_str(), record.rdata.data()) != 1) { |
| LOG(ERROR) << "inet_pton(AF_INET6, " << rdatastr << ") failed"; |
| return false; |
| } |
| } else if ((record.rtype == ns_type::ns_t_ptr) || (record.rtype == ns_type::ns_t_cname) || |
| (record.rtype == ns_type::ns_t_ns)) { |
| constexpr char delimiter = '.'; |
| std::string name = rdatastr; |
| std::vector<char> rdata; |
| |
| // Generating PTRDNAME field(section 3.3.12) or CNAME field(section 3.3.1) in rfc1035. |
| // The "name" should be an absolute domain name which ends in a dot. |
| if (name.back() != delimiter) { |
| LOG(ERROR) << "invalid absolute domain name"; |
| return false; |
| } |
| name.pop_back(); // remove the dot in tail |
| for (const std::string& label : android::base::Split(name, {delimiter})) { |
| // The length of label is limited to 63 octets or less. See RFC 1035 section 3.1. |
| if (label.length() == 0 || label.length() > 63) { |
| LOG(ERROR) << "invalid label length"; |
| return false; |
| } |
| |
| rdata.push_back(label.length()); |
| rdata.insert(rdata.end(), label.begin(), label.end()); |
| } |
| rdata.push_back(0); // Length byte of zero terminates the label list |
| |
| // The length of domain name is limited to 255 octets or less. See RFC 1035 section 3.1. |
| if (rdata.size() > 255) { |
| LOG(ERROR) << "invalid name length"; |
| return false; |
| } |
| record.rdata = move(rdata); |
| } else { |
| LOG(ERROR) << "unhandled qtype " << dnstype2str(record.rtype); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DNSResponder::writePacket(const DNSHeader* header, char* response, |
| size_t* response_len) const { |
| char* response_cur = header->write(response, response + *response_len); |
| if (response_cur == nullptr) { |
| return false; |
| } |
| *response_len = response_cur - response; |
| return true; |
| } |
| |
| bool DNSResponder::makeErrorResponse(DNSHeader* header, ns_rcode rcode, char* response, |
| size_t* response_len) const { |
| header->answers.clear(); |
| header->authorities.clear(); |
| header->additionals.clear(); |
| header->rcode = rcode; |
| header->qr = true; |
| return writePacket(header, response, response_len); |
| } |
| |
| bool DNSResponder::makeTruncatedResponse(DNSHeader* header, char* response, |
| size_t* response_len) const { |
| // Build a minimal response for non-EDNS response over UDP. Truncate all stub RRs in answer, |
| // authority and additional section. EDNS response truncation has not supported here yet |
| // because the EDNS response must have an OPT record. See RFC 6891 section 7. |
| header->answers.clear(); |
| header->authorities.clear(); |
| header->additionals.clear(); |
| header->qr = true; |
| header->tr = true; |
| return writePacket(header, response, response_len); |
| } |
| |
| bool DNSResponder::makeResponse(DNSHeader* header, int protocol, char* response, |
| size_t* response_len) const { |
| char buffer[4096]; |
| size_t buffer_len = sizeof(buffer); |
| bool ret; |
| |
| switch (mapping_type_) { |
| case MappingType::DNS_HEADER: |
| ret = makeResponseFromDnsHeader(header, buffer, &buffer_len); |
| break; |
| case MappingType::BINARY_PACKET: |
| ret = makeResponseFromBinaryPacket(header, buffer, &buffer_len); |
| break; |
| case MappingType::ADDRESS_OR_HOSTNAME: |
| default: |
| ret = makeResponseFromAddressOrHostname(header, buffer, &buffer_len); |
| } |
| |
| if (!ret) return false; |
| |
| // Return truncated response if the built non-EDNS response size which is larger than 512 bytes |
| // will be responded over UDP. The truncated response implementation here just simply set up |
| // the TC bit and truncate all stub RRs in answer, authority and additional section. It is |
| // because the resolver will retry DNS query over TCP and use the full TCP response. See also |
| // RFC 1035 section 4.2.1 for UDP response truncation and RFC 6891 section 4.3 for EDNS larger |
| // response size capability. |
| // TODO: Perhaps keep the stub RRs as possible. |
| // TODO: Perhaps truncate the EDNS based response over UDP. See also RFC 6891 section 4.3, |
| // section 6.2.5 and section 7. |
| if (protocol == IPPROTO_UDP && buffer_len > kMaximumUdpSize && |
| !hasOptPseudoRR(header) /* non-EDNS */) { |
| LOG(INFO) << "Return truncated response because original response length " << buffer_len |
| << " is larger than " << kMaximumUdpSize << " bytes."; |
| return makeTruncatedResponse(header, response, response_len); |
| } |
| |
| if (buffer_len > *response_len) { |
| LOG(ERROR) << "buffer overflow on line " << __LINE__; |
| return false; |
| } |
| memcpy(response, buffer, buffer_len); |
| *response_len = buffer_len; |
| return true; |
| } |
| |
| bool DNSResponder::makeResponseFromAddressOrHostname(DNSHeader* header, char* response, |
| size_t* response_len) const { |
| for (const DNSQuestion& question : header->questions) { |
| if (question.qclass != ns_class::ns_c_in && question.qclass != ns_class::ns_c_any) { |
| LOG(INFO) << "unsupported question class " << question.qclass; |
| return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); |
| } |
| |
| if (!addAnswerRecords(question, &header->answers)) { |
| return makeErrorResponse(header, ns_rcode::ns_r_servfail, response, response_len); |
| } |
| } |
| header->qr = true; |
| return writePacket(header, response, response_len); |
| } |
| |
| bool DNSResponder::makeResponseFromDnsHeader(DNSHeader* header, char* response, |
| size_t* response_len) const { |
| std::lock_guard guard(mappings_mutex_); |
| |
| // Support single question record only. It should be okay because res_mkquery() sets "qdcount" |
| // as one for the operation QUERY and handleDNSRequest() checks ns_opcode::ns_o_query before |
| // making a response. In other words, only need to handle the query which has single question |
| // section. See also res_mkquery() in system/netd/resolv/res_mkquery.cpp. |
| // TODO: Perhaps add support for multi-question records. |
| const std::vector<DNSQuestion>& questions = header->questions; |
| if (questions.size() != 1) { |
| LOG(INFO) << "unsupported question count " << questions.size(); |
| return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); |
| } |
| |
| if (questions[0].qclass != ns_class::ns_c_in && questions[0].qclass != ns_class::ns_c_any) { |
| LOG(INFO) << "unsupported question class " << questions[0].qclass; |
| return makeErrorResponse(header, ns_rcode::ns_r_notimpl, response, response_len); |
| } |
| |
| const std::string name = questions[0].qname.name; |
| const int qtype = questions[0].qtype; |
| const auto it = dnsheader_mappings_.find(QueryKey(name, qtype)); |
| if (it != dnsheader_mappings_.end()) { |
| // Store both "id" and "rd" which comes from query. |
| const unsigned id = header->id; |
| const bool rd = header->rd; |
| |
| // Build a response from the registered DNSHeader mapping. |
| *header = it->second; |
| // Assign both "ID" and "RD" fields from query to response. See RFC 1035 section 4.1.1. |
| header->id = id; |
| header->rd = rd; |
| } else { |
| // TODO: handle correctly. See also TODO in addAnswerRecords(). |
| LOG(INFO) << "no mapping found for " << name << " " << dnstype2str(qtype) |
| << ", couldn't build a response from DNSHeader mapping"; |
| |
| // Note that do nothing as makeResponseFromAddressOrHostname() if no mapping is found. It |
| // just changes the QR flag from query (0) to response (1) in the query. Then, send the |
| // modified query back as a response. |
| header->qr = true; |
| } |
| return writePacket(header, response, response_len); |
| } |
| |
| bool DNSResponder::makeResponseFromBinaryPacket(DNSHeader* header, char* response, |
| size_t* response_len) const { |
| std::lock_guard guard(mappings_mutex_); |
| |
| // Build a search key of mapping from the query. |
| // TODO: Perhaps pass the query packet buffer directly from the caller. |
| std::vector<uint8_t> queryKey; |
| if (!header->write(&queryKey)) return false; |
| // Clear ID field (byte 0-1) because it is not required by the mapping key. |
| queryKey[0] = 0; |
| queryKey[1] = 0; |
| |
| const auto it = packet_mappings_.find(queryKey); |
| if (it != packet_mappings_.end()) { |
| if (it->second.size() > *response_len) { |
| LOG(ERROR) << "buffer overflow on line " << __LINE__; |
| return false; |
| } else { |
| std::copy(it->second.begin(), it->second.end(), response); |
| // Leave the "RD" flag assignment for testing. The "RD" flag of the response keep |
| // using the one from the raw packet mapping but the received query. |
| // Assign "ID" field from query to response. See RFC 1035 section 4.1.1. |
| reinterpret_cast<uint16_t*>(response)[0] = htons(header->id); // bytes 0-1: id |
| *response_len = it->second.size(); |
| return true; |
| } |
| } else { |
| // TODO: handle correctly. See also TODO in addAnswerRecords(). |
| // TODO: Perhaps dump packet content to indicate which query failed. |
| LOG(INFO) << "no mapping found, couldn't build a response from BinaryPacket mapping"; |
| // Note that do nothing as makeResponseFromAddressOrHostname() if no mapping is found. It |
| // just changes the QR flag from query (0) to response (1) in the query. Then, send the |
| // modified query back as a response. |
| header->qr = true; |
| return writePacket(header, response, response_len); |
| } |
| } |
| |
| void DNSResponder::setDeferredResp(bool deferred_resp) { |
| std::lock_guard<std::mutex> guard(cv_mutex_for_deferred_resp_); |
| deferred_resp_ = deferred_resp; |
| if (!deferred_resp_) { |
| cv_for_deferred_resp_.notify_one(); |
| } |
| } |
| |
| bool DNSResponder::addFd(int fd, uint32_t events) { |
| epoll_event ev; |
| ev.events = events; |
| ev.data.fd = fd; |
| if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fd, &ev) < 0) { |
| PLOG(ERROR) << "epoll_ctl() for socket " << fd << " failed"; |
| return false; |
| } |
| return true; |
| } |
| |
| void DNSResponder::handleQuery(int protocol) { |
| char buffer[4096]; |
| sockaddr_storage sa; |
| socklen_t sa_len = sizeof(sa); |
| ssize_t len = 0; |
| android::base::unique_fd tcpFd; |
| switch (protocol) { |
| case IPPROTO_UDP: |
| do { |
| len = recvfrom(udp_socket_.get(), buffer, sizeof(buffer), 0, (sockaddr*)&sa, |
| &sa_len); |
| } while (len < 0 && (errno == EAGAIN || errno == EINTR)); |
| if (len <= 0) { |
| PLOG(ERROR) << "recvfrom() failed, len=" << len; |
| return; |
| } |
| break; |
| case IPPROTO_TCP: |
| tcpFd.reset(accept4(tcp_socket_.get(), reinterpret_cast<sockaddr*>(&sa), &sa_len, |
| SOCK_CLOEXEC)); |
| if (tcpFd.get() < 0) { |
| PLOG(ERROR) << "failed to accept client socket"; |
| return; |
| } |
| // Get the message length from two byte length field. |
| // See also RFC 1035, section 4.2.2 and RFC 7766, section 8 |
| uint8_t queryMessageLengthField[2]; |
| if (read(tcpFd.get(), &queryMessageLengthField, 2) != 2) { |
| PLOG(ERROR) << "Not enough length field bytes"; |
| return; |
| } |
| |
| const uint16_t qlen = (queryMessageLengthField[0] << 8) | queryMessageLengthField[1]; |
| while (len < qlen) { |
| ssize_t ret = read(tcpFd.get(), buffer + len, qlen - len); |
| if (ret <= 0) { |
| PLOG(ERROR) << "Error while reading query"; |
| return; |
| } |
| len += ret; |
| } |
| break; |
| } |
| LOG(DEBUG) << "read " << len << " bytes on " << dnsproto2str(protocol); |
| std::lock_guard lock(cv_mutex_); |
| char response[4096]; |
| size_t response_len = sizeof(response); |
| // TODO: check whether sending malformed packets to DnsResponder |
| if (handleDNSRequest(buffer, len, protocol, response, &response_len) && response_len > 0) { |
| std::this_thread::sleep_for(std::chrono::milliseconds(response_delayed_ms_)); |
| // place wait_for after handleDNSRequest() so we can check the number of queries in |
| // test case before it got responded. |
| std::unique_lock guard(cv_mutex_for_deferred_resp_); |
| cv_for_deferred_resp_.wait( |
| guard, [this]() REQUIRES(cv_mutex_for_deferred_resp_) { return !deferred_resp_; }); |
| len = 0; |
| |
| switch (protocol) { |
| case IPPROTO_UDP: |
| len = sendto(udp_socket_.get(), response, response_len, 0, |
| reinterpret_cast<const sockaddr*>(&sa), sa_len); |
| if (len < 0) { |
| PLOG(ERROR) << "Failed to send response"; |
| } |
| break; |
| case IPPROTO_TCP: |
| // Get the message length from two byte length field. |
| // See also RFC 1035, section 4.2.2 and RFC 7766, section 8 |
| uint8_t responseMessageLengthField[2]; |
| responseMessageLengthField[0] = response_len >> 8; |
| responseMessageLengthField[1] = response_len; |
| if (write(tcpFd.get(), responseMessageLengthField, 2) != 2) { |
| PLOG(ERROR) << "Failed to write response length field"; |
| break; |
| } |
| if (write(tcpFd.get(), response, response_len) != |
| static_cast<ssize_t>(response_len)) { |
| PLOG(ERROR) << "Failed to write response"; |
| break; |
| } |
| len = response_len; |
| break; |
| } |
| const std::string host_str = addr2str(reinterpret_cast<const sockaddr*>(&sa), sa_len); |
| if (len > 0) { |
| LOG(DEBUG) << "sent " << len << " bytes to " << host_str; |
| } else { |
| const char* method_str = (protocol == IPPROTO_TCP) ? "write()" : "sendto()"; |
| LOG(ERROR) << method_str << " failed for " << host_str; |
| } |
| // Test that the response is actually a correct DNS message. |
| // TODO: Perhaps make DNS message validation to support name compression. Or it throws |
| // a warning for a valid DNS message with name compression while the binary packet mapping |
| // is used. |
| const char* response_end = response + len; |
| DNSHeader header; |
| const char* cur = header.read(response, response_end); |
| if (cur == nullptr) LOG(WARNING) << "response is flawed"; |
| } else { |
| LOG(WARNING) << "not responding"; |
| } |
| cv.notify_one(); |
| return; |
| } |
| |
| bool DNSResponder::sendToEventFd() { |
| const uint64_t data = 1; |
| if (const ssize_t rt = write(event_fd_.get(), &data, sizeof(data)); rt != sizeof(data)) { |
| PLOG(ERROR) << "failed to write eventfd, rt=" << rt; |
| return false; |
| } |
| return true; |
| } |
| |
| void DNSResponder::handleEventFd() { |
| int64_t data; |
| if (const ssize_t rt = read(event_fd_.get(), &data, sizeof(data)); rt != sizeof(data)) { |
| PLOG(INFO) << "ignore reading eventfd failed, rt=" << rt; |
| } |
| } |
| |
| android::base::unique_fd DNSResponder::createListeningSocket(int socket_type) { |
| addrinfo ai_hints{ |
| .ai_flags = AI_PASSIVE, |
| .ai_family = AF_UNSPEC, |
| .ai_socktype = socket_type, |
| }; |
| addrinfo* ai_res = nullptr; |
| const int rv = |
| getaddrinfo(listen_address_.c_str(), listen_service_.c_str(), &ai_hints, &ai_res); |
| ScopedAddrinfo ai_res_cleanup(ai_res); |
| if (rv) { |
| LOG(ERROR) << "getaddrinfo(" << listen_address_ << ", " << listen_service_ |
| << ") failed: " << gai_strerror(rv); |
| return {}; |
| } |
| for (const addrinfo* ai = ai_res; ai; ai = ai->ai_next) { |
| android::base::unique_fd fd( |
| socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol)); |
| if (fd.get() < 0) { |
| PLOG(ERROR) << "ignore creating socket failed"; |
| continue; |
| } |
| enableSockopt(fd.get(), SOL_SOCKET, SO_REUSEPORT).ignoreError(); |
| enableSockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR).ignoreError(); |
| const std::string host_str = addr2str(ai->ai_addr, ai->ai_addrlen); |
| const char* socket_str = (socket_type == SOCK_STREAM) ? "TCP" : "UDP"; |
| |
| if (bind(fd.get(), ai->ai_addr, ai->ai_addrlen)) { |
| PLOG(ERROR) << "failed to bind " << socket_str << " " << host_str << ":" |
| << listen_service_; |
| continue; |
| } |
| LOG(INFO) << "bound to " << socket_str << " " << host_str << ":" << listen_service_; |
| return fd; |
| } |
| return {}; |
| } |
| |
| } // namespace test |