]> git.ipfire.org Git - thirdparty/pdns.git/blame_incremental - pdns/lwres.cc
More licensing everywhere
[thirdparty/pdns.git] / pdns / lwres.cc
... / ...
CommitLineData
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 */
57int 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