• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

4.93
/pdns/resolver.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "utility.hh"
26
#include "resolver.hh"
27
#include <pthread.h>
28
#include <semaphore.h>
29
#include <iostream>
30
#include <cerrno>
31
#include "misc.hh"
32
#include <algorithm>
33
#include <sstream>
34
#include "dnsrecords.hh"
35
#include <cstring>
36
#include <string>
37
#include <vector>
38
#include <boost/algorithm/string.hpp>
39
#include "dns.hh"
40
#include "qtype.hh"
41

42
#include "pdnsexception.hh"
43
#include "arguments.hh"
44
#include "base64.hh"
45
#include "dnswriter.hh"
46
#include "dnsparser.hh"
47
#include "query-local-address.hh"
48

49
#include "dns_random.hh"
50
#include <poll.h>
51
#include "gss_context.hh"
52
#include "namespaces.hh"
53

54
using pdns::resolver::parseResult;
55

56
int makeQuerySocket(const ComboAddress& local, bool udpOrTCP, bool nonLocalBind)
57
{
7✔
58
  ComboAddress ourLocal(local);
7✔
59

60
  int sock=socket(ourLocal.sin4.sin_family, udpOrTCP ? SOCK_DGRAM : SOCK_STREAM, 0);
7!
61
  if(sock < 0) {
7!
62
    if(errno == EAFNOSUPPORT && local.sin4.sin_family == AF_INET6) {
×
63
        return -1;
×
64
    }
×
65
    unixDie("Creating local resolver socket for address '"+ourLocal.toString()+"'");
×
66
  }
×
67

68
  setCloseOnExec(sock);
7✔
69

70
  if(nonLocalBind)
7!
71
    Utility::setBindAny(local.sin4.sin_family, sock);
×
72

73
  if(udpOrTCP) {
7!
74
    // udp, try hard to bind an unpredictable port
75
    int tries=10;
×
76
    while(--tries) {
×
77
      ourLocal.sin4.sin_port = htons(10000+(dns_random(10000)));
×
78

79
      if (::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) >= 0)
×
80
        break;
×
81
    }
×
82
    // cerr<<"bound udp port "<<ourLocal.sin4.sin_port<<", "<<tries<<" tries left"<<endl;
83

84
    if(!tries) {
×
85
      closesocket(sock);
×
86
      throw PDNSException("Resolver binding to local UDP socket on '"+ourLocal.toString()+"': "+stringerror());
×
87
    }
×
88
  }
×
89
  else {
7✔
90
    // tcp, let the kernel figure out the port
91
    ourLocal.sin4.sin_port = 0;
7✔
92
    if(::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) < 0) {
7!
93
      closesocket(sock);
×
94
      throw PDNSException("Resolver binding to local TCP socket on '"+ourLocal.toString()+"': "+stringerror());
×
95
    }
×
96
  }
7✔
97
  return sock;
7✔
98
}
7✔
99

100
Resolver::Resolver()
101
{
×
102
  locals["default4"] = -1;
×
103
  locals["default6"] = -1;
×
104
  try {
×
105
    if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
×
106
      locals["default4"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
×
107
    }
×
108
    if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
×
109
      locals["default6"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
×
110
    }
×
111
  }
×
112
  catch(...) {
×
113
    if(locals["default4"]>=0)
×
114
      close(locals["default4"]);
×
115
    if(locals["default6"]>=0)
×
116
      close(locals["default6"]);
×
117
    throw;
×
118
  }
×
119
}
×
120

121
Resolver::~Resolver()
122
{
×
123
  for (auto& iter: locals) {
×
124
    if (iter.second >= 0)
×
125
      close(iter.second);
×
126
  }
×
127
}
×
128

129
uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& local,
130
                               const DNSName &domain, int type, int *localsock, bool dnssecOK,
131
                               const DNSName& tsigkeyname, const DNSName& tsigalgorithm,
132
                               const string& tsigsecret)
133
{
×
134
  uint16_t randomid;
×
135
  vector<uint8_t> packet;
×
136
  DNSPacketWriter pw(packet, domain, type);
×
137
  pw.getHeader()->id = randomid = dns_random_uint16();
×
138

139
  if(dnssecOK) {
×
140
    pw.addOpt(2800, 0, EDNSOpts::DNSSECOK);
×
141
    pw.commit();
×
142
  }
×
143

144
  if(!tsigkeyname.empty()) {
×
145
    // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
146
    TSIGRecordContent trc;
×
147
    if (tsigalgorithm == DNSName("hmac-md5"))
×
148
      trc.d_algoName = tsigalgorithm + DNSName("sig-alg.reg.int");
×
149
    else
×
150
      trc.d_algoName = tsigalgorithm;
×
151
    trc.d_time = time(nullptr);
×
152
    trc.d_fudge = 300;
×
153
    trc.d_origID=ntohs(randomid);
×
154
    trc.d_eRcode=0;
×
155
    addTSIG(pw, trc, tsigkeyname, tsigsecret, "", false);
×
156
  }
×
157

158
  int sock;
×
159

160
  // choose socket based on local
161
  if (local.sin4.sin_family == 0) {
×
162
    // up to us.
163
    if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
×
164
      string ipv = remote.sin4.sin_family == AF_INET ? "4" : "6";
×
165
      throw ResolverException("No IPv" + ipv + " socket available, is such an address configured in query-local-address?");
×
166
    }
×
167
    sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
×
168
  } else {
×
169
    std::string lstr = local.toString();
×
170
    std::map<std::string, int>::iterator lptr;
×
171

172
    // reuse an existing local socket or make a new one
173
    if ((lptr = locals.find(lstr)) != locals.end()) {
×
174
      sock = lptr->second;
×
175
    } else {
×
176
      // try to make socket
177
      sock = makeQuerySocket(local, true);
×
178
      if (sock < 0)
×
179
        throw ResolverException("Unable to create local socket on '"+lstr+"'' to '"+remote.toLogString()+"': "+stringerror());
×
180
      setNonBlocking( sock );
×
181
      locals[lstr] = sock;
×
182
    }
×
183
  }
×
184

185
  if (localsock != nullptr) {
×
186
    *localsock = sock;
×
187
  }
×
188
  if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
×
189
    throw ResolverException("Unable to ask query of '"+remote.toLogString()+"': "+stringerror());
×
190
  }
×
191
  return randomid;
×
192
}
×
193

194
namespace pdns {
195
  namespace resolver {
196
    int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t /* origQtype */, uint16_t id, Resolver::res_t* result)
197
    {
×
198
      result->clear();
×
199

200
      if(mdp.d_header.rcode)
×
201
        return mdp.d_header.rcode;
×
202

203
      if(origQname.countLabels()) {  // not AXFR
×
204
        if(mdp.d_header.id != id)
×
205
          throw ResolverException("Remote nameserver replied with wrong id");
×
206
        if(mdp.d_header.qdcount != 1)
×
207
          throw ResolverException("resolver: received answer with wrong number of questions ("+std::to_string(mdp.d_header.qdcount)+")");
×
208
        if(mdp.d_qname != origQname)
×
209
          throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
×
210
      }
×
211

212
      vector<DNSResourceRecord> ret;
×
213
      DNSResourceRecord rr;
×
214
      result->reserve(mdp.d_answers.size());
×
215

216
      for (const auto& i: mdp.d_answers) {
×
217
        rr.qname = i.d_name;
×
218
        rr.qtype = i.d_type;
×
219
        rr.ttl = i.d_ttl;
×
220
        rr.content = i.getContent()->getZoneRepresentation(true);
×
221
        result->push_back(rr);
×
222
      }
×
223

224
      return 0;
×
225
    }
×
226

227
  } // namespace resolver
228
} // namespace pdns
229

230
bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
231
{
×
232
  auto fds = std::make_unique<struct pollfd[]>(locals.size());
×
233
  size_t i = 0, k;
×
234
  int sock;
×
235

236
  for (const auto& iter: locals) {
×
237
    fds[i].fd = iter.second;
×
238
    fds[i].events = POLLIN;
×
239
    ++i;
×
240
  }
×
241

242
  if (poll(fds.get(), i, 250) < 1) { // wait for 0.25s
×
243
    return false;
×
244
  }
×
245

246
  sock = -1;
×
247

248
  // determine who
249
  for(k=0;k<i;k++) {
×
250
    if ((fds[k].revents & POLLIN) == POLLIN) {
×
251
      sock = fds[k].fd;
×
252
      break;
×
253
    }
×
254
  }
×
255

256
  if (sock < 0) return false; // false alarm
×
257

258
  int err;
×
259
  remote->sin6.sin6_family = AF_INET6; // make sure getSocklen() below returns a large enough value
×
260
  socklen_t addrlen=remote->getSocklen();
×
261
  char buf[3000];
×
262
  err = recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)(remote), &addrlen);
×
263
  if(err < 0) {
×
264
    if(errno == EAGAIN)
×
265
      return false;
×
266

267
    throw ResolverException("recvfrom error waiting for answer: "+stringerror());
×
268
  }
×
269

270
  MOADNSParser mdp(false, (char*)buf, err);
×
271
  *id=mdp.d_header.id;
×
272
  *domain = mdp.d_qname;
×
273

274
  if(domain->empty())
×
275
    throw ResolverException("SOA query to '" + remote->toLogString() + "' produced response without domain name (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
×
276

277
  if(mdp.d_answers.empty())
×
278
    throw ResolverException("Query to '" + remote->toLogString() + "' for SOA of '" + domain->toLogString() + "' produced no results (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
×
279

280
  if(mdp.d_qtype != QType::SOA)
×
281
    throw ResolverException("Query to '" + remote->toLogString() + "' for SOA of '" + domain->toLogString() + "' returned wrong record type");
×
282

283
  if(mdp.d_header.rcode != 0)
×
284
    throw ResolverException("Query to '" + remote->toLogString() + "' for SOA of '" + domain->toLogString() + "' returned Rcode " + RCode::to_s(mdp.d_header.rcode));
×
285

286
  *theirInception = *theirExpire = 0;
×
287
  bool gotSOA=false;
×
288
  for(const MOADNSParser::answers_t::value_type& drc :  mdp.d_answers) {
×
289
    if(drc.d_type == QType::SOA && drc.d_name == *domain) {
×
290
      auto src = getRR<SOARecordContent>(drc);
×
291
      if (src) {
×
292
        *theirSerial = src->d_st.serial;
×
293
        gotSOA = true;
×
294
      }
×
295
    }
×
296
    if(drc.d_type == QType::RRSIG && drc.d_name == *domain) {
×
297
      auto rrc = getRR<RRSIGRecordContent>(drc);
×
298
      if(rrc && rrc->d_type == QType::SOA) {
×
299
        *theirInception= std::max(*theirInception, rrc->d_siginception);
×
300
        *theirExpire = std::max(*theirExpire, rrc->d_sigexpire);
×
301
      }
×
302
    }
×
303
  }
×
304
  if(!gotSOA)
×
305
    throw ResolverException("Query to '" + remote->toLogString() + "' for SOA of '" + domain->toLogString() + "' did not return a SOA");
×
306
  return true;
×
307
}
×
308

309
int Resolver::resolve(const ComboAddress& to, const DNSName &domain, int type, Resolver::res_t* res, const ComboAddress &local)
310
{
×
311
  try {
×
312
    int sock = -1;
×
313
    int id = sendResolve(to, local, domain, type, &sock);
×
314
    int err=waitForData(sock, 0, 3000000);
×
315

316
    if(!err) {
×
317
      throw ResolverException("Timeout waiting for answer");
×
318
    }
×
319
    if(err < 0)
×
320
      throw ResolverException("Error waiting for answer: "+stringerror());
×
321

322
    ComboAddress from;
×
323
    socklen_t addrlen = sizeof(from);
×
324
    char buffer[3000];
×
325
    int len;
×
326

327
    if((len=recvfrom(sock, buffer, sizeof(buffer), 0,(struct sockaddr*)(&from), &addrlen)) < 0)
×
328
      throw ResolverException("recvfrom error waiting for answer: "+stringerror());
×
329

330
    if (from != to) {
×
331
      throw ResolverException("Got answer from the wrong peer while resolving ('"+from.toLogString()+"' instead of '"+to.toLogString()+"', discarding");
×
332
    }
×
333

334
    MOADNSParser mdp(false, buffer, len);
×
335
    return parseResult(mdp, domain, type, id, res);
×
336
  }
×
337
  catch(ResolverException &re) {
×
338
    throw ResolverException(re.reason+" from "+to.toLogString());
×
339
  }
×
340
}
×
341

342
int Resolver::resolve(const ComboAddress& ipport, const DNSName &domain, int type, Resolver::res_t* res) {
×
343
  ComboAddress local;
×
344
  local.sin4.sin_family = 0;
×
345
  return resolve(ipport, domain, type, res, local);
×
346
}
×
347

348
void Resolver::getSoaSerial(const ComboAddress& ipport, const DNSName &domain, uint32_t *serial)
349
{
×
350
  vector<DNSResourceRecord> res;
×
351
  int ret = resolve(ipport, domain, QType::SOA, &res);
×
352

353
  if(ret || res.empty())
×
354
    throw ResolverException("Query to '" + ipport.toLogString() + "' for SOA of '" + domain.toLogString() + "' produced no answers");
×
355

356
  if(res[0].qtype.getCode() != QType::SOA)
×
357
    throw ResolverException("Query to '" + ipport.toLogString() + "' for SOA of '" + domain.toLogString() + "' produced a "+res[0].qtype.toString()+" record");
×
358

359
  vector<string>parts;
×
360
  stringtok(parts, res[0].content);
×
361
  if(parts.size()<3)
×
362
    throw ResolverException("Query to '" + ipport.toLogString() + "' for SOA of '" + domain.toLogString() + "' produced an unparseable response");
×
363

364
  try {
×
365
    *serial = pdns::checked_stoi<uint32_t>(parts[2]);
×
366
  }
×
367
  catch(const std::out_of_range& oor) {
×
368
    throw ResolverException("Query to '" + ipport.toLogString() + "' for SOA of '" + domain.toLogString() + "' produced an unparseable serial");
×
369
  }
×
370
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc