├── README.md ├── hosts.txt ├── Makefile ├── LICENSE.txt ├── dns.h ├── fernmelder.cc ├── net-headers.h └── dns.cc /README.md: -------------------------------------------------------------------------------- 1 | fernmelder 2 | ========== 3 | 4 | async mass DNS resolver 5 | -------------------------------------------------------------------------------- /hosts.txt: -------------------------------------------------------------------------------- 1 | google.de 2 | google.com 3 | heise.de 4 | www.ccc.de 5 | nsa.gov 6 | www.nsa.gov 7 | nxdomain 8 | 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX=c++ 2 | CFLAGS=-Wall -O2 -c -std=c++11 -pedantic 3 | 4 | all: fernmelder 5 | 6 | fernmelder: dns.o fm.o 7 | $(CXX) dns.o fm.o -lresolv -o fernmelder 8 | 9 | dns.o: dns.cc dns.h 10 | $(CXX) $(CFLAGS) dns.cc 11 | 12 | fm.o: fernmelder.cc 13 | $(CXX) $(CFLAGS) fernmelder.cc -o fm.o 14 | 15 | clean: 16 | rm -rf *.o 17 | 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of fernmelder. 3 | * 4 | * (C) 2014 by Sebastian Krahmer, sebastian [dot] krahmer [at] gmail [dot] com 5 | * 6 | * fernmelder is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * fernmelder is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with fernmelder. If not, see . 18 | */ 19 | 20 | -------------------------------------------------------------------------------- /dns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of fernmelder. 3 | * 4 | * (C) 2014 by Sebastian Krahmer, sebastian [dot] krahmer [at] gmail [dot] com 5 | * 6 | * fernmelder is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * fernmelder is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with fernmelder. If not, see . 18 | */ 19 | 20 | #ifndef __dns_h__ 21 | #define __dns_h__ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "net-headers.h" 37 | 38 | 39 | 40 | int host2qname(const std::string&, std::string&); 41 | 42 | 43 | struct dns_command { 44 | char op[8]; 45 | uint32_t nonce; 46 | } __attribute__((packed)); 47 | 48 | extern dns_command dns_timer_cmd; 49 | 50 | 51 | class DNS { 52 | 53 | int sock, family, type, secs; 54 | 55 | std::string err; 56 | std::map ns_map; 57 | std::map xid2name; 58 | 59 | public: 60 | DNS(int, int t = SOCK_DGRAM); 61 | 62 | ~DNS(); 63 | 64 | const char *why() { return err.c_str(); }; 65 | 66 | int build_error(const std::string &s) 67 | { 68 | err = "DNS::"; 69 | err += s; 70 | if (errno) { 71 | err += ": "; 72 | err += strerror(errno); 73 | } 74 | return -1; 75 | } 76 | 77 | int query(const std::string&, std::string&, uint16_t qtype = net_headers::dns_type::A); 78 | 79 | int parse_response(const std::string &, std::string &, std::multimap &); 80 | 81 | int add_ns(const std::string&, const std::string& port = "53"); 82 | 83 | int send(std::vector &); 84 | 85 | int recv(std::string &); 86 | 87 | int poll(int); 88 | 89 | int rebind(); 90 | 91 | void sleep(int s) 92 | { 93 | secs = s; 94 | } 95 | }; 96 | 97 | #endif 98 | 99 | -------------------------------------------------------------------------------- /fernmelder.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of fernmelder. 3 | * 4 | * (C) 2014 by Sebastian Krahmer, sebastian [dot] krahmer [at] gmail [dot] com 5 | * 6 | * fernmelder is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * fernmelder is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with fernmelder. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "net-headers.h" 27 | #include "dns.h" 28 | 29 | 30 | using namespace std; 31 | using namespace net_headers; 32 | 33 | 34 | void usage() 35 | { 36 | cout<<"\nfernmelder <-4|-6> <-N DNS server> [-N ...] [-Q] [-s shots] [-S usec delay]\n\n" 37 | <<"\t\t-4\tuse IPv4 UDP to DNS server (use before -N)\n" 38 | <<"\t\t-6\tuse IPv6 UDP to DNS server (use before -N)\n" 39 | <<"\t\t-Q\task for AAAA records (IPv6 mapping) rather than for A\n" 40 | <<"\t\t-N\tIP or hostname of recursive DNS server to use for resolving\n" 41 | <<"\t\t-A\tdo not print NXDOMAIn or CNAMEs\n" 42 | <<"\t\t-s\thow many requests in a row before receiving (low values OK)\n" 43 | <<"\t\t-S\tamount of usecs to sleep between send() calls to not flood server\n\n"; 44 | 45 | exit(1); 46 | } 47 | 48 | 49 | int main(int argc, char **argv) 50 | { 51 | DNS *dns = NULL; 52 | string req = "", res = "", name = "", dns_list = ""; 53 | vector v; 54 | multimap result; 55 | int r = 0, c = 0, shots = 4, secs = 1500; 56 | uint16_t qtype = dns_type::A; 57 | bool A_only = 0; 58 | fd_set rset; 59 | 60 | while ((c = getopt(argc, argv, "N:46s:S:QA")) != -1) { 61 | switch (c) { 62 | case '4': 63 | dns = new (nothrow) DNS(AF_INET); 64 | break; 65 | case '6': 66 | dns = new (nothrow) DNS(AF_INET6); 67 | break; 68 | case 'Q': 69 | qtype = dns_type::AAAA; 70 | break; 71 | case 'N': 72 | if (dns) { 73 | if (dns->add_ns(optarg) < 0) { 74 | cerr<why()< 0x100000) 88 | shots = 10; 89 | break; 90 | case 'S': 91 | secs = atoi(optarg); 92 | if (secs < 10 || secs > 0x100000) 93 | secs = 10; 94 | break; 95 | default: 96 | usage(); 97 | } 98 | } 99 | 100 | if (!dns) 101 | usage(); 102 | 103 | dns->sleep(secs); 104 | 105 | bool eof = 0; 106 | 107 | v.reserve(shots); 108 | 109 | cout<<"\n; <<>> fernmelder 0.3 <<>> -s "<>name)) { 132 | eof = 1; 133 | break; 134 | } 135 | 136 | if (dns->query(name, req, qtype) >= 0) 137 | v.push_back(req); 138 | } 139 | 140 | if (dns->send(v) < 0) 141 | cerr<why()<poll(2); 147 | if (!r) 148 | return 0; 149 | } 150 | 151 | r = dns->recv(res); 152 | 153 | if (r < 0) 154 | cerr<why(); 155 | 156 | if (r == 1) { 157 | name = ""; 158 | result.clear(); 159 | if (dns->parse_response(res, name, result) <= 0) 160 | continue; 161 | for (auto i = result.begin(); i != result.end(); ++i) { 162 | // only print resolved IP's, no CNAME or NXDOMAIN 163 | if (A_only) { 164 | if (i->first.find("A\t") == string::npos) 165 | continue; 166 | } 167 | cout<first<<"\t"<second<. 18 | */ 19 | 20 | /* some of the header definitions have been taken from various other 21 | * open-sourced include files 22 | */ 23 | 24 | #ifndef __net_headers__ 25 | #define __net_headers__ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | namespace net_headers { 34 | 35 | 36 | struct tap_header { 37 | uint16_t flags, proto; 38 | } __attribute__((packed)); 39 | 40 | 41 | struct icmphdr { 42 | uint8_t type; 43 | uint8_t code; 44 | uint16_t sum; 45 | 46 | union { 47 | struct { 48 | uint16_t id; 49 | uint16_t sequence; 50 | } echo; 51 | uint32_t gateway; 52 | struct { 53 | uint16_t unused; 54 | uint16_t mtu; 55 | } frag; 56 | } un; 57 | }; 58 | 59 | 60 | enum eth_types_t : uint16_t { 61 | ETH_P_IP = 0x0800 62 | }; 63 | 64 | 65 | enum icmp_type : uint8_t { 66 | ICMP_ECHO_REPLY = 0, 67 | ICMP_ECHO_REQUEST = 8, 68 | 69 | ICMP6_ECHO_REQUEST = 128, 70 | ICMP6_ECHO_REPLY = 129 71 | }; 72 | 73 | 74 | class udphdr { 75 | public: 76 | uint16_t source; 77 | uint16_t dest; 78 | uint16_t len; 79 | uint16_t check; 80 | 81 | udphdr() : source(0), dest(0), len(0), check(0) { } 82 | }; 83 | 84 | 85 | /* 86 | * The pseudo-header is used to calculate checksums over UDP 87 | * and TCP packets. 88 | */ 89 | struct pseudohdr { 90 | uint32_t saddr; 91 | uint32_t daddr; 92 | uint8_t zero; 93 | uint8_t proto; 94 | uint16_t len; 95 | }; 96 | 97 | 98 | struct pseudohdr6 { 99 | in6_addr saddr, daddr; 100 | uint32_t len; 101 | uint8_t zero[3]; 102 | uint8_t proto; 103 | }; 104 | 105 | 106 | enum tcp_flags_t : uint8_t { 107 | TH_FIN = 0x01, 108 | TH_SYN = 0x02, 109 | TH_RST = 0x04, 110 | TH_PUSH = 0x08, 111 | TH_ACK = 0x10, 112 | TH_URG = 0x20 113 | }; 114 | 115 | 116 | class tcphdr 117 | { 118 | public: 119 | uint16_t th_sport; 120 | uint16_t th_dport; 121 | uint32_t th_seq; 122 | uint32_t th_ack; 123 | #if __BYTE_ORDER == __LITTLE_ENDIAN 124 | uint8_t th_x2:4; // unused 125 | uint8_t th_off:4; 126 | #elif __BYTE_ORDER == __BIG_ENDIAN 127 | uint8_t th_off:4; 128 | uint8_t th_x2:4; 129 | #endif 130 | uint8_t th_flags; 131 | 132 | uint16_t th_win; 133 | uint16_t th_sum; 134 | uint16_t th_urg; 135 | }; 136 | 137 | 138 | enum tcp_opt_t : uint8_t { 139 | TCPOPT_EOL = 0, 140 | TCPOPT_NOP = 1, 141 | TCPOPT_MAXSEG = 2, 142 | TCPOLEN_MAXSEG = 4, 143 | TCPOPT_WINDOW = 3, 144 | TCPOLEN_WINDOW = 3, 145 | TCPOPT_SACK_PERMITTED = 4, // Experimental 146 | TCPOLEN_SACK_PERMITTED= 2, 147 | TCPOPT_SACK = 5, // Experimental 148 | TCPOPT_TIMESTAMP = 8, 149 | TCPOLEN_TIMESTAMP = 10, 150 | TCPOLEN_TSTAMP_APPA = (TCPOLEN_TIMESTAMP+2), // appendix A 151 | TCPOPT_QSR = 27, 152 | TCPOLEN_QSR = 8 153 | }; 154 | 155 | 156 | class iphdr 157 | { 158 | public: 159 | #if __BYTE_ORDER == __LITTLE_ENDIAN 160 | uint32_t ihl:4; 161 | uint32_t version:4; 162 | #elif __BYTE_ORDER == __BIG_ENDIAN 163 | uint32_t version:4; 164 | uint32_t ihl:4; 165 | #else 166 | # error "Please fix " 167 | #endif 168 | uint8_t tos; 169 | uint16_t tot_len; 170 | uint16_t id; 171 | uint16_t frag_off; 172 | uint8_t ttl; 173 | uint8_t protocol; 174 | uint16_t check; 175 | uint32_t saddr; 176 | uint32_t daddr; 177 | 178 | iphdr() : ihl(5), version(4), tos(0), tot_len(0), id(0), frag_off(0), 179 | ttl(64), protocol(IPPROTO_IP), check(0), saddr(0), daddr(0) { } 180 | }; 181 | 182 | 183 | enum ip_flags_t : uint16_t { 184 | IP_RF = 0x8000, 185 | IP_DF = 0x4000, 186 | IP_MF = 0x2000, 187 | IP_OFFMASK = 0x1fff 188 | }; 189 | 190 | 191 | struct ip6_hdr { 192 | #if __BYTE_ORDER == __LITTLE_ENDIAN 193 | uint8_t priority:4, 194 | version:4; 195 | #elif __BYTE_ORDER == __BIG_ENDIAN 196 | uint8_t version:4, 197 | priority:4; 198 | #else 199 | #error "Please fix " 200 | #endif 201 | uint8_t flow_lbl[3]; 202 | 203 | uint16_t payload_len; 204 | uint8_t nexthdr; 205 | uint8_t hop_limit; 206 | 207 | struct in6_addr saddr; 208 | struct in6_addr daddr; 209 | }; 210 | 211 | 212 | struct ip6_opt { 213 | uint8_t ip6o_type; 214 | uint8_t ip6o_len; 215 | }; 216 | 217 | 218 | struct icmp6_hdr { 219 | uint8_t icmp6_type; // type field 220 | uint8_t icmp6_code; // code field 221 | uint16_t icmp6_cksum; // checksum field 222 | union { 223 | uint32_t icmp6_un_data32[1]; // type-specific field 224 | uint16_t icmp6_un_data16[2]; // type-specific field 225 | uint8_t icmp6_un_data8[4]; // type-specific field 226 | } icmp6_dataun; 227 | }; 228 | 229 | 230 | 231 | class dnshdr { 232 | public: 233 | uint16_t id; 234 | 235 | #if __BYTE_ORDER == __BIG_ENDIAN 236 | /* fields in third byte */ 237 | uint16_t qr: 1; /* response flag */ 238 | uint16_t opcode: 4; /* purpose of message */ 239 | uint16_t aa: 1; /* authoritive answer */ 240 | uint16_t tc: 1; /* truncated message */ 241 | uint16_t rd: 1; /* recursion desired */ 242 | /* fields in fourth byte */ 243 | uint16_t ra: 1; /* recursion available */ 244 | uint16_t unused :1; /* unused bits (MBZ as of 4.9.3a3) */ 245 | uint16_t ad: 1; /* authentic data from named */ 246 | uint16_t cd: 1; /* checking disabled by resolver */ 247 | uint16_t rcode :4; /* response code */ 248 | #endif 249 | #if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN 250 | /* fields in third byte */ 251 | uint16_t rd :1; /* recursion desired */ 252 | uint16_t tc :1; /* truncated message */ 253 | uint16_t aa :1; /* authoritive answer */ 254 | uint16_t opcode :4; /* purpose of message */ 255 | uint16_t qr :1; /* response flag */ 256 | /* fields in fourth byte */ 257 | uint16_t rcode :4; /* response code */ 258 | uint16_t cd: 1; /* checking disabled by resolver */ 259 | uint16_t ad: 1; /* authentic data from named */ 260 | uint16_t unused :1; /* unused bits (MBZ as of 4.9.3a3) */ 261 | uint16_t ra :1; /* recursion available */ 262 | #endif 263 | /* 264 | union { 265 | u_int16_t flags; 266 | 267 | u_int16_t QR:1; 268 | u_int16_t opcode:4; 269 | u_int16_t AA:1; 270 | u_int16_t TC:1; 271 | u_int16_t RD:1; 272 | u_int16_t RA:1; 273 | u_int16_t zero:3; 274 | u_int16_t rcode:4; 275 | } u; 276 | */ 277 | uint16_t q_count; 278 | uint16_t a_count; 279 | uint16_t rra_count; 280 | uint16_t ad_count; 281 | 282 | dnshdr() : id (0), 283 | q_count(0), a_count(0), rra_count(0), ad_count(0) 284 | { 285 | qr = 0; opcode = 0; aa = 0; tc = 0; rd = 0; ra = 0; ad = 0; cd = 0; 286 | rcode = 0; unused = 0; 287 | } 288 | 289 | private: dnshdr(const dnshdr &) {}; 290 | }; 291 | 292 | 293 | enum dns_type : uint16_t { 294 | A = 1, 295 | NS = 2, 296 | CNAME = 5, 297 | SOA = 6, 298 | PTR = 12, 299 | HINFO = 13, 300 | MX = 15, 301 | TXT = 16, 302 | AAAA = 28, 303 | SRV = 33, 304 | DNAME = 39, 305 | OPT = 41, 306 | DNSKEY = 48, 307 | EUI64 = 109, 308 | }; 309 | 310 | 311 | // an IPv4 RR 312 | struct dns_rr { 313 | // name here 314 | uint16_t type, _class; 315 | uint32_t ttl; 316 | uint16_t len; 317 | // rdata 318 | } __attribute__((packed)); 319 | 320 | 321 | 322 | } // namespace 323 | 324 | #endif 325 | 326 | -------------------------------------------------------------------------------- /dns.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of fernmelder 3 | * 4 | * (C) 2014 by Sebastian Krahmer, sebastian [dot] krahmer [at] gmail [dot] com 5 | * 6 | * fernmelder is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * fernmelder is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with fernmelder. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "dns.h" 40 | 41 | 42 | using namespace std; 43 | using namespace net_headers; 44 | 45 | 46 | const uint8_t dns_max_label = 63; 47 | 48 | 49 | /* "\003foo\003bar\000" -> foo.bar 50 | */ 51 | int qname2host(const string &msg, string &result) 52 | { 53 | string::size_type i = 0; 54 | uint8_t len = 0; 55 | 56 | result = ""; 57 | string s = ""; 58 | try { 59 | s.reserve(msg.length()); 60 | } catch (...) { 61 | return -1; 62 | } 63 | 64 | while ((len = msg[i]) != 0) { 65 | if (len > dns_max_label) { 66 | result = s; 67 | return 0; 68 | } 69 | if (len + i + 1 > msg.size()) 70 | return -1; 71 | 72 | s += msg.substr(i + 1, len); 73 | s += "."; 74 | i += len + 1; 75 | } 76 | result = s; 77 | return i + 1; 78 | } 79 | 80 | 81 | /* "\003foo\003bar\02ab\02de\0", "\02ab\02de\0"-> foobar.ab.de 82 | * (to unsplit the automatically splitted large labels from host2qname()) 83 | * Un-splitting of domains stops if encoded_domain is seen 84 | */ 85 | int qname2host(const string &msg, string &result, const string &encoded_domain) 86 | { 87 | string::size_type i = 0; 88 | uint8_t len = 0; 89 | bool add_dot = 0; 90 | 91 | result = ""; 92 | string s = ""; 93 | 94 | try { 95 | s.reserve(msg.length()); 96 | } catch (...) { 97 | return -1; 98 | } 99 | 100 | while ((len = msg[i]) != 0) { 101 | if (len > dns_max_label) { 102 | result = s; 103 | return 0; 104 | } 105 | if (len + i + 1 > msg.size()) 106 | return -1; 107 | 108 | if (add_dot) 109 | s += "."; 110 | s += msg.substr(i + 1, len); 111 | i += len + 1; 112 | if (encoded_domain == msg.substr(i, encoded_domain.size())) 113 | add_dot = 1; 114 | } 115 | result = s; 116 | return i + 1; 117 | } 118 | 119 | 120 | /* "foo.bar" -> "\003foo\003bar\000" 121 | * automatically splits labels larger than 63 byte into 122 | * sub-domains 123 | */ 124 | int host2qname(const string &host, string &result) 125 | { 126 | string split_host = ""; 127 | string::size_type pos1 = 0, pos2 = 0; 128 | 129 | for (;pos1 < host.size();) { 130 | pos2 = host.find(".", pos1); 131 | if (pos2 == string::npos) { 132 | split_host += host.substr(pos1); 133 | break; 134 | } 135 | 136 | if (pos2 - pos1 > dns_max_label) { 137 | split_host += host.substr(pos1, dns_max_label); 138 | pos1 += dns_max_label; 139 | } else { 140 | split_host += host.substr(pos1, pos2 - pos1); 141 | pos1 = pos2 + 1; 142 | } 143 | 144 | split_host += "."; 145 | } 146 | 147 | try { 148 | result.clear(); 149 | result.reserve(split_host.length() + 2); 150 | result.resize(split_host.length() + 2); 151 | } catch (...) { 152 | return -1; 153 | } 154 | 155 | int i = 0, j = 0, k = 0, l = 0; 156 | uint8_t how_much = 0; 157 | 158 | while (i < (int)split_host.length()) { 159 | l = i; 160 | how_much = 0; 161 | while (split_host[i] != '.' && i != (int)split_host.length()) { 162 | ++how_much; 163 | ++i; 164 | } 165 | result[j] = how_much; 166 | ++j; 167 | i = l; 168 | for (k = 0; k < how_much; j++, i++, k++) 169 | result[j] = split_host[i]; 170 | ++i; 171 | } 172 | result[j] = '\0'; 173 | return j + 1; 174 | } 175 | 176 | 177 | int DNS::query(const string &host, string &result, uint16_t qtype) 178 | { 179 | static uint16_t seq = 0; 180 | err = ""; 181 | result = ""; 182 | string qname = ""; 183 | 184 | dnshdr dnsh; 185 | dnsh.id = ++seq; 186 | dnsh.rd = 1; 187 | dnsh.q_count = htons(1); 188 | 189 | size_t buflen = sizeof(dnsh) + 2*sizeof(uint16_t); 190 | if (host2qname(host, qname) < 0) 191 | return build_error("query: cannot encode hostname"); 192 | buflen += qname.length(); 193 | 194 | char *buf = new (nothrow) char[buflen]; 195 | if (!buf) 196 | return build_error("query: OOM"); 197 | 198 | memcpy(buf, &dnsh, sizeof(dnsh)); 199 | size_t idx = sizeof(dnsh); 200 | 201 | memcpy(buf + idx, qname.c_str(), qname.size()); 202 | idx += qname.size(); 203 | *(uint16_t *)&buf[idx] = htons(qtype); 204 | idx += sizeof(uint16_t); 205 | *(uint16_t *)&buf[idx] = htons(1); // INET 206 | idx += sizeof(uint16_t); 207 | 208 | result.assign(buf, buflen); 209 | 210 | xid2name[seq] = host; 211 | 212 | delete [] buf; 213 | return 0; 214 | } 215 | 216 | 217 | DNS::DNS(int af, int t) 218 | { 219 | family = af; 220 | type = t; 221 | sock = -1; 222 | err = ""; 223 | secs = 1000; 224 | } 225 | 226 | 227 | DNS::~DNS() 228 | { 229 | if (sock >= 0) 230 | close(sock); 231 | 232 | for (auto i : ns_map) { 233 | freeaddrinfo(i.first); 234 | } 235 | } 236 | 237 | 238 | int DNS::add_ns(const string &host, const string &port) 239 | { 240 | err = ""; 241 | 242 | // just one DNS server for TCP DNS 243 | if (type == SOCK_STREAM && ns_map.size() > 0) 244 | return 0; 245 | 246 | struct addrinfo *ai = NULL, hints; 247 | memset(&hints, 0, sizeof(hints)); 248 | hints.ai_family = family; 249 | hints.ai_socktype = type; 250 | 251 | int e; 252 | if ((e = getaddrinfo(host.c_str(), port.c_str(), &hints, &ai)) != 0) { 253 | err = "DNS::add_ns:getaddrinfo:"; 254 | err += gai_strerror(e); 255 | return -1; 256 | } 257 | 258 | ns_map[ai] = ai->ai_addrlen; 259 | 260 | // do not free, as we keep a pointer in the ns_map 261 | //freeaddrinfo(ai); 262 | return 0; 263 | } 264 | 265 | 266 | int DNS::parse_response(const string &msg, string &name, multimap &result) 267 | { 268 | name = ""; 269 | result.clear(); 270 | 271 | if (msg.size() < sizeof(dnshdr) + 4 + 16) 272 | return build_error("parse_response: response too short"); 273 | 274 | const dnshdr *hdr = (const dnshdr *)msg.c_str(); 275 | const char *qname = msg.c_str() + sizeof(dnshdr); 276 | 277 | if (ntohs(hdr->q_count) != 1) 278 | return build_error("parse_response: invalid packet (1)"); 279 | 280 | string fqdn = ""; 281 | int nl = 0; // length of DNS encoded name 282 | if ((nl = qname2host(string(qname, msg.size() - sizeof(dnshdr)), fqdn)) <= 0) 283 | return build_error("parse_response: invalid packet (2)"); 284 | 285 | name = fqdn; 286 | 287 | auto it = xid2name.find(hdr->id); 288 | if (it != xid2name.end()) { 289 | if (name.find(it->second) == string::npos) { 290 | return 0; 291 | } 292 | xid2name.erase(it); 293 | } else { 294 | return 0; 295 | } 296 | 297 | if (hdr->rcode != 0) { 298 | result.insert(pair("0\tIN", "NXDOMAIN")); 299 | return 1; 300 | } 301 | 302 | if (msg.size() < sizeof(dnshdr) + nl + 4 + sizeof(dns_rr)) 303 | return build_error("parse_response: invalid packet (3)"); 304 | 305 | const char *idx = reinterpret_cast(msg.c_str() + sizeof(dnshdr) + nl + 2*sizeof(uint16_t)); 306 | 307 | const dns_rr *rr = NULL; 308 | char ip_buf[128], ttl[32], exp_dn[MAXDNAME]; 309 | string s = ""; 310 | in_addr in; 311 | in6_addr in6; 312 | 313 | for (int i = 0; i < ntohs(hdr->a_count); ++i) { 314 | // compressed label? 315 | if ((uint8_t)idx[0] > dns_max_label) 316 | idx += 2; 317 | else 318 | idx += nl; 319 | 320 | if (idx + sizeof(dns_rr) > msg.c_str() + msg.size()) 321 | return build_error("parse_response: invalid packet (4)"); 322 | 323 | rr = reinterpret_cast(idx); 324 | idx += sizeof(dns_rr); 325 | 326 | if (idx + ntohs(rr->len) > msg.c_str() + msg.size()) 327 | return build_error("parse_response: invalid packet (5)"); 328 | 329 | memset(ttl, 0, sizeof(ttl)); 330 | snprintf(ttl, sizeof(ttl), "%d", ntohl(rr->ttl)); 331 | s = ttl; 332 | s += "\tIN\t"; 333 | 334 | if (rr->type == htons(dns_type::A)) { 335 | memset(ip_buf, 0, sizeof(ip_buf)); 336 | memcpy(&in.s_addr, idx, sizeof(in.s_addr)); 337 | inet_ntop(AF_INET, &in, ip_buf, sizeof(ip_buf)); 338 | s += "A\t"; 339 | result.insert(pair(s, ip_buf)); 340 | } else if (rr->type == htons(dns_type::CNAME)) { 341 | fqdn = ""; 342 | if ((nl = qname2host(string(idx, ntohs(rr->len)), fqdn)) < 0) 343 | return build_error("parse_response: invalid packet (6)"); 344 | // compressed 345 | else if (nl == 0) { 346 | memset(exp_dn, 0, sizeof(exp_dn)); 347 | ns_name_uncompress(reinterpret_cast(msg.c_str()), 348 | reinterpret_cast(msg.c_str() + msg.size() + 1), 349 | reinterpret_cast(idx), exp_dn, sizeof(exp_dn)); 350 | fqdn = string(exp_dn); 351 | fqdn += "."; 352 | } 353 | s += "CNAME\t"; 354 | result.insert(pair(s, fqdn)); 355 | } else if (rr->type == htons(dns_type::AAAA)) { 356 | memset(ip_buf, 0, sizeof(ip_buf)); 357 | memcpy(&in6, idx, sizeof(in6)); 358 | inet_ntop(AF_INET6, &in6, ip_buf, sizeof(ip_buf)); 359 | s += "AAAA\t"; 360 | result.insert(pair(s, ip_buf)); 361 | } 362 | 363 | idx += ntohs(rr->len); 364 | } 365 | 366 | if (result.size() == 0) 367 | return -1; 368 | 369 | return 1; 370 | } 371 | 372 | 373 | int DNS::rebind() 374 | { 375 | // no rebinds necessary for TCP DNS 376 | if (type == SOCK_STREAM) 377 | return 0; 378 | 379 | if (sock >= 0) 380 | close(sock); 381 | 382 | if ((sock = socket(family, type, 0)) < 0) 383 | return build_error("rebind::socket"); 384 | int fl = fcntl(sock, F_GETFL); 385 | if (fl >= 0) 386 | fcntl(sock, F_SETFL, fl|O_NONBLOCK); 387 | 388 | timeval tv; 389 | memset(&tv, 0, sizeof(tv)); 390 | gettimeofday(&tv, NULL); 391 | 392 | if (family == AF_INET) { 393 | sockaddr_in sin; 394 | memset(&sin, 0, sizeof(sin)); 395 | sin.sin_family = AF_INET; 396 | for (int i = 0;; ++i) { 397 | sin.sin_port = htons(tv.tv_usec + i % 0xffff); 398 | if (bind(sock, (sockaddr *)&sin, sizeof(sin)) == 0) 399 | break; 400 | } 401 | } else { 402 | sockaddr_in6 sin6; 403 | memset(&sin6, 0, sizeof(sin6)); 404 | sin6.sin6_family = AF_INET6; 405 | for (int i = 0;; ++i) { 406 | sin6.sin6_port = htons(tv.tv_usec + i % 0xffff); 407 | if (bind(sock, (sockaddr *)&sin6, sizeof(sin6)) == 0) 408 | break; 409 | } 410 | 411 | } 412 | return 0; 413 | } 414 | 415 | 416 | // poll implemented by means of select :-) 417 | int DNS::poll(int sec) 418 | { 419 | timeval tv; 420 | 421 | memset(&tv, 0, sizeof(tv)); 422 | tv.tv_sec = sec; 423 | fd_set rset; 424 | FD_ZERO(&rset); 425 | FD_SET(sock, &rset); 426 | 427 | if (select(sock + 1, &rset, NULL, NULL, &tv) == 1) 428 | return 1; 429 | 430 | return 0; 431 | } 432 | 433 | 434 | int DNS::send(vector &msgs) 435 | { 436 | if (sock < 0) { 437 | if ((sock = socket(family, type, 0)) < 0) 438 | return build_error("send::socket"); 439 | int fl = fcntl(sock, F_GETFL); 440 | if (fl >= 0) 441 | fcntl(sock, F_SETFL, fl|O_NONBLOCK); 442 | } 443 | 444 | if (msgs.size() == 0) 445 | return 0; 446 | 447 | int r = 0; 448 | string msg = ""; 449 | for (auto it = ns_map.begin(); msgs.size() > 0;) { 450 | msg = msgs.back(); 451 | 452 | r = ::sendto(sock, msg.c_str(), msg.length(), 0, it->first->ai_addr, 453 | it->second); 454 | usleep(secs); 455 | 456 | if (r < 0 && errno == EAGAIN) 457 | continue; 458 | else if (r < 0) 459 | return build_error("send::send"); 460 | 461 | msgs.pop_back(); 462 | ++it; 463 | if (it == ns_map.end()) 464 | it = ns_map.begin(); 465 | } 466 | return 0; 467 | } 468 | 469 | 470 | 471 | int DNS::recv(string &msg) 472 | { 473 | msg = ""; 474 | 475 | if (sock < 0) 476 | return build_error("recv: No open socket"); 477 | 478 | char buf[1024]; 479 | memset(buf, 0, sizeof(buf)); 480 | ssize_t r = ::recv(sock, buf, sizeof(buf), 0); 481 | if (r < 0 && errno == EAGAIN) 482 | return 0; 483 | else if (r < 0) 484 | return build_error("recv::recv"); 485 | 486 | msg = string(buf, r); 487 | return 1; 488 | } 489 | 490 | --------------------------------------------------------------------------------