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

PowerDNS / pdns / 18409242756

10 Oct 2025 02:16PM UTC coverage: 19.38% (-44.8%) from 64.13%
18409242756

push

github

web-flow
Merge pull request #16245 from miodvallat/matriochka_exception

auth: yet another logic botch

3972 of 30808 branches covered (12.89%)

Branch coverage included in aggregate %.

11562 of 49346 relevant lines covered (23.43%)

3168.61 hits per line

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

0.0
/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
{
×
58
  ComboAddress ourLocal(local);
×
59

60
  int sock=socket(ourLocal.sin4.sin_family, udpOrTCP ? SOCK_DGRAM : SOCK_STREAM, 0);
×
61
  if(sock < 0) {
×
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);
×
69

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

73
  if(udpOrTCP) {
×
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 {
×
90
    // tcp, let the kernel figure out the port
91
    ourLocal.sin4.sin_port = 0;
×
92
    if(::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) < 0) {
×
93
      closesocket(sock);
×
94
      throw PDNSException("Resolver binding to local TCP socket on '"+ourLocal.toString()+"': "+stringerror());
×
95
    }
×
96
  }
×
97
  return sock;
×
98
}
×
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 == g_hmacmd5dnsname) {
×
148
      trc.d_algoName = g_hmacmd5dnsname_long;
×
149
    }
×
150
    else {
×
151
      trc.d_algoName = tsigalgorithm;
×
152
    }
×
153
    trc.d_time = time(nullptr);
×
154
    trc.d_fudge = 300;
×
155
    trc.d_origID=ntohs(randomid);
×
156
    trc.d_eRcode=0;
×
157
    addTSIG(pw, trc, tsigkeyname, tsigsecret, "", false);
×
158
  }
×
159

160
  int sock;
×
161

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

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

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

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

202
      if(mdp.d_header.rcode)
×
203
        return mdp.d_header.rcode;
×
204

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

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

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

226
      return 0;
×
227
    }
×
228

229
  } // namespace resolver
230
} // namespace pdns
231

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

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

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

248
  sock = -1;
×
249

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

258
  if (sock < 0) return false; // false alarm
×
259

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

366
  try {
×
367
    *serial = pdns::checked_stoi<uint32_t>(parts[2]);
×
368
  }
×
369
  catch(const std::out_of_range& oor) {
×
370
    throw ResolverException("Query to '" + ipport.toLogString() + "' for SOA of '" + domain.toLogString() + "' produced an unparseable serial");
×
371
  }
×
372
}
×
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

© 2026 Coveralls, Inc