]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | PowerDNS Versatile Database Driven Nameserver | |
3 | Copyright (C) 2002 - 2015 PowerDNS.COM BV | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License version 2 as | |
7 | published by the Free Software Foundation | |
8 | ||
9 | Additionally, the license of this program contains a special | |
10 | exception which allows to distribute the program in binary form when | |
11 | it is linked against OpenSSL. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | */ | |
22 | ||
23 | ||
24 | #ifdef HAVE_CONFIG_H | |
25 | #include "config.h" | |
26 | #endif | |
27 | #include "utility.hh" | |
28 | #include "lwres.hh" | |
29 | #include <iostream> | |
30 | #include "dnsrecords.hh" | |
31 | #include <errno.h> | |
32 | #include "misc.hh" | |
33 | #include <algorithm> | |
34 | #include <sstream> | |
35 | #include <cstring> | |
36 | #include <string> | |
37 | #include <vector> | |
38 | #include "dns.hh" | |
39 | #include "qtype.hh" | |
40 | #include "pdnsexception.hh" | |
41 | #include "arguments.hh" | |
42 | #include "sstuff.hh" | |
43 | #include "syncres.hh" | |
44 | #include "dnswriter.hh" | |
45 | #include "dnsparser.hh" | |
46 | #include "logger.hh" | |
47 | #include "dns_random.hh" | |
48 | #include <boost/scoped_array.hpp> | |
49 | #include <boost/algorithm/string.hpp> | |
50 | #include "validate-recursor.hh" | |
51 | #include "ednssubnet.hh" | |
52 | ||
53 | //! returns -2 for OS limits error, -1 for permanent error that has to do with remote **transport**, 0 for timeout, 1 for success | |
54 | /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors | |
55 | Never throws! | |
56 | */ | |
57 | int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult *lwr) | |
58 | { | |
59 | size_t len; | |
60 | size_t bufsize=g_outgoingEDNSBufsize; | |
61 | scoped_array<unsigned char> buf(new unsigned char[bufsize]); | |
62 | vector<uint8_t> vpacket; | |
63 | // string mapped0x20=dns0x20(domain); | |
64 | DNSPacketWriter pw(vpacket, domain, type); | |
65 | ||
66 | pw.getHeader()->rd=sendRDQuery; | |
67 | pw.getHeader()->id=dns_random(0xffff); | |
68 | /* RFC 6840 section 5.9: | |
69 | * This document further specifies that validating resolvers SHOULD set | |
70 | * the CD bit on every upstream query. This is regardless of whether | |
71 | * the CD bit was set on the incoming query [...] | |
72 | * | |
73 | * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or | |
74 | * set in the forward-zone-file), so we use this as an indicator for it being | |
75 | * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we | |
76 | * only set +CD on forwarded query in any mode other than dnssec=off. | |
77 | */ | |
78 | pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off); | |
79 | ||
80 | string ping; | |
81 | bool weWantEDNSSubnet=false; | |
82 | if(EDNS0Level) { | |
83 | DNSPacketWriter::optvect_t opts; | |
84 | if(srcmask) { | |
85 | EDNSSubnetOpts eo; | |
86 | eo.source = *srcmask; | |
87 | // cout<<"Adding request mask: "<<eo.source.toString()<<endl; | |
88 | opts.push_back(make_pair(8, makeEDNSSubnetOptsString(eo))); | |
89 | srcmask=boost::optional<Netmask>(); // this is also our return value | |
90 | weWantEDNSSubnet=true; | |
91 | } | |
92 | ||
93 | pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts); | |
94 | pw.commit(); | |
95 | } | |
96 | lwr->d_rcode = 0; | |
97 | lwr->d_haveEDNS = false; | |
98 | int ret; | |
99 | ||
100 | DTime dt; | |
101 | dt.set(); | |
102 | *now=dt.getTimeval(); | |
103 | errno=0; | |
104 | if(!doTCP) { | |
105 | int queryfd; | |
106 | if(ip.sin4.sin_family==AF_INET6) | |
107 | g_stats.ipv6queries++; | |
108 | ||
109 | if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, pw.getHeader()->id, | |
110 | domain, type, &queryfd)) < 0) { | |
111 | return ret; // passes back the -2 EMFILE | |
112 | } | |
113 | ||
114 | // sleep until we see an answer to this, interface to mtasker | |
115 | ||
116 | ret=arecvfrom(reinterpret_cast<char *>(buf.get()), bufsize-1,0, ip, &len, pw.getHeader()->id, | |
117 | domain, type, queryfd, now); | |
118 | } | |
119 | else { | |
120 | try { | |
121 | Socket s(ip.sin4.sin_family, SOCK_STREAM); | |
122 | ||
123 | s.setNonBlocking(); | |
124 | ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0); | |
125 | ||
126 | s.bind(local); | |
127 | ||
128 | ComboAddress remote = ip; | |
129 | remote.sin4.sin_port = htons(53); | |
130 | s.connect(remote); | |
131 | ||
132 | uint16_t tlen=htons(vpacket.size()); | |
133 | char *lenP=(char*)&tlen; | |
134 | const char *msgP=(const char*)&*vpacket.begin(); | |
135 | string packet=string(lenP, lenP+2)+string(msgP, msgP+vpacket.size()); | |
136 | ||
137 | ret=asendtcp(packet, &s); | |
138 | if(!(ret>0)) | |
139 | return ret; | |
140 | ||
141 | packet.clear(); | |
142 | ret=arecvtcp(packet, 2, &s, false); | |
143 | if(!(ret > 0)) | |
144 | return ret; | |
145 | ||
146 | memcpy(&tlen, packet.c_str(), sizeof(tlen)); | |
147 | len=ntohs(tlen); // switch to the 'len' shared with the rest of the function | |
148 | ||
149 | ret=arecvtcp(packet, len, &s, false); | |
150 | if(!(ret > 0)) | |
151 | return ret; | |
152 | ||
153 | if(len > bufsize) { | |
154 | bufsize=len; | |
155 | scoped_array<unsigned char> narray(new unsigned char[bufsize]); | |
156 | buf.swap(narray); | |
157 | } | |
158 | memcpy(buf.get(), packet.c_str(), len); | |
159 | ||
160 | ret=1; | |
161 | } | |
162 | catch(NetworkError& ne) { | |
163 | ret = -2; // OS limits error | |
164 | } | |
165 | } | |
166 | ||
167 | ||
168 | lwr->d_usec=dt.udiff(); | |
169 | *now=dt.getTimeval(); | |
170 | ||
171 | if(ret <= 0) // includes 'timeout' | |
172 | return ret; | |
173 | ||
174 | lwr->d_records.clear(); | |
175 | try { | |
176 | lwr->d_tcbit=0; | |
177 | MOADNSParser mdp((const char*)buf.get(), len); | |
178 | lwr->d_aabit=mdp.d_header.aa; | |
179 | lwr->d_tcbit=mdp.d_header.tc; | |
180 | lwr->d_rcode=mdp.d_header.rcode; | |
181 | ||
182 | if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) { | |
183 | return 1; // this is "success", the error is set in lwr->d_rcode | |
184 | } | |
185 | ||
186 | if(domain != mdp.d_qname) { | |
187 | if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too | |
188 | L<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl; | |
189 | } | |
190 | // unexpected count has already been done @ pdns_recursor.cc | |
191 | goto out; | |
192 | } | |
193 | ||
194 | for(const auto& a : mdp.d_answers) | |
195 | lwr->d_records.push_back(a.first); | |
196 | ||
197 | EDNSOpts edo; | |
198 | if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) { | |
199 | lwr->d_haveEDNS = true; | |
200 | ||
201 | if(weWantEDNSSubnet) { | |
202 | for(const auto& opt : edo.d_options) { | |
203 | if(opt.first==8) { | |
204 | EDNSSubnetOpts reso; | |
205 | if(getEDNSSubnetOptsFromString(opt.second, &reso)) { | |
206 | // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl; | |
207 | if(reso.scope.getBits()) | |
208 | srcmask = reso.scope; | |
209 | } | |
210 | } | |
211 | } | |
212 | } | |
213 | } | |
214 | ||
215 | return 1; | |
216 | } | |
217 | catch(std::exception &mde) { | |
218 | if(::arg().mustDo("log-common-errors")) | |
219 | L<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl; | |
220 | lwr->d_rcode = RCode::FormErr; | |
221 | g_stats.serverParseError++; | |
222 | return 1; // success - oddly enough | |
223 | } | |
224 | catch(...) { | |
225 | L<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl; | |
226 | } | |
227 | ||
228 | g_stats.serverParseError++; | |
229 | ||
230 | out: | |
231 | if(!lwr->d_rcode) | |
232 | lwr->d_rcode=RCode::ServFail; | |
233 | ||
234 | return -1; | |
235 | } | |
236 |