• 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

10.95
/pdns/nameserver.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 <cstdio>
27
#include <cstring>
28
#include <cstdlib>
29
#include <cerrno>
30
#include <iostream>
31
#include <string>
32
#include <sys/types.h>
33
#include "responsestats.hh"
34

35
#include "auth-main.hh"
36
#include "dns.hh"
37
#include "dnsbackend.hh"
38
#include "dnspacket.hh"
39
#include "nameserver.hh"
40
#include "distributor.hh"
41
#include "logger.hh"
42
#include "arguments.hh"
43
#include "statbag.hh"
44
#include "proxy-protocol.hh"
45

46
#include "namespaces.hh"
47

48
extern StatBag S;
49

50
/** \mainpage 
51
    PowerDNS is a very versatile nameserver that can answer questions from different backends. To implement your
52
    own backend, see the documentation for the DNSBackend class.
53

54
    \section copyright Copyright and License
55
    PowerDNS is (C) 2001-2008 PowerDNS.COM BV. It is distributed according to the terms of the General Public License version 2.
56

57
    \section overview High level overview
58

59
    The Distributor contains a configurable number of PacketHandler instances, each in its own thread, for connection pooling. 
60
    PacketHandler instances are recycled of they let escape an PDNSException.
61

62
    The PacketHandler implements the RFC1034 algorithm and converts question packets into DNSBackend queries.
63

64
    A DNSBackend is an entity that returns DNSResourceRecord objects in return to explicit questions for domains with a specified QType
65

66
    PowerDNS uses the UeberBackend, which hosts DNSBackends. By default it has no DNSBackends within itself, those are loaded
67
    by setting --load=<list of backends>. This way DNSBackend implementations can be kept completely separate, but most aren't.
68

69
    If one or more DNSBackends are loaded, the UeberBackend fields the queries to all of them until one answers.
70

71
    \section TCP TCP Operations
72

73
    The TCP operation runs within a single thread called tcpreceiver(), that also queries the PacketHandler. 
74

75
    \section Cache Caching
76
 
77
    On its own, this setup is not suitable for high performance operations. A single DNS query can turn into many DNSBackend questions,
78
    each taking many milliseconds to complete. This is why the qthread() first checks the PacketCache to see if an answer is known to a packet
79
    asking this question. If so, the entire Distributor is shunted, and the answer is sent back *directly*, within a few microseconds.
80

81
    \section misc Miscellaneous
82
    Configuration details are available via the ArgvMap instance arg. Statistics are created by making calls to the StatBag object called S. 
83
    These statistics are made available via the UeberBackend on the same socket that is used for dynamic module commands.
84

85
    \section Main Main 
86
    The main() of PowerDNS can be found in auth-main.cc - start reading there for further insights into the operation of the nameserver
87
*/
88

89
vector<ComboAddress> g_localaddresses; // not static, our unit tests need to poke this
90

91
void UDPNameserver::bindAddresses()
92
{
×
93
  vector<string>locals;
×
94
  stringtok(locals,::arg()["local-address"]," ,");
×
95

96
  int one = 1;
×
97

98
  if(locals.empty())
×
99
    throw PDNSException("No local address specified");
×
100

101
  int s;
×
102
  // for(vector<string>::const_iterator i=locals.begin();i!=locals.end();++i) {
103
  for (const auto &local : locals) {
×
104
    ComboAddress locala(local, ::arg().asNum("local-port"));
×
105

106
    s = socket(locala.sin4.sin_family, SOCK_DGRAM, 0);
×
107

108
    if(s < 0) {
×
109
      if(errno == EAFNOSUPPORT) {
×
110
        g_log<<Logger::Error<<"Binding "<<locala.toStringWithPort()<<": Address Family is not supported - skipping bind" << endl;
×
111
        return;
×
112
      }
×
113
      throw PDNSException("Unable to acquire a UDP socket: "+stringerror());
×
114
    }
×
115

116
    setCloseOnExec(s);
×
117
    if(!setNonBlocking(s))
×
118
      throw PDNSException("Unable to set UDP socket " + locala.toStringWithPort() + " to non-blocking: "+stringerror());
×
119

120
    if(IsAnyAddress(locala)) {
×
121
      (void)setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one));
×
122
      if (locala.isIPv6()) {
×
123
        (void)setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));      // if this fails, we report an error in tcpreceiver too
×
124
#ifdef IPV6_RECVPKTINFO
×
125
        (void)setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
×
126
#endif
×
127
      }
×
128
    }
×
129

130
    if (!setSocketTimestamps(s))
×
131
      g_log<<Logger::Warning<<"Unable to enable timestamp reporting for socket "<<locala.toStringWithPort()<<endl;
×
132

133
    try {
×
134
      setSocketIgnorePMTU(s, locala.sin4.sin_family);
×
135
    }
×
136
    catch(const std::exception& e) {
×
137
      g_log<<Logger::Warning<<"Failed to set IP_MTU_DISCOVER on UDP server socket: "<<e.what()<<endl;
×
138
    }
×
139

140
    if (d_can_reuseport) {
×
141
      if (!setReusePort(s)) {
×
142
        d_can_reuseport = false;
×
143
      }
×
144
    }
×
145

146
    if( ::arg().mustDo("non-local-bind") )
×
147
      Utility::setBindAny(locala.sin4.sin_family, s);
×
148

149
    if( !d_additional_socket )
×
150
        g_localaddresses.push_back(locala);
×
151

152
    if(::bind(s, (sockaddr*)&locala, locala.getSocklen()) < 0) {
×
153
      int err = errno;
×
154
      close(s);
×
155
      if (err == EADDRNOTAVAIL && !::arg().mustDo("local-address-nonexist-fail")) {
×
156
        g_log<<Logger::Error<<"Address " << locala << " does not exist on this server - skipping UDP bind" << endl;
×
157
        continue;
×
158
      } else {
×
159
        g_log<<Logger::Error<<"Unable to bind UDP socket to '"+locala.toStringWithPort()+"': "<<stringerror(err)<<endl;
×
160
        throw PDNSException("Unable to bind to UDP socket");
×
161
      }
×
162
    }
×
163
    d_sockets.push_back(s);
×
164
    struct pollfd pfd;
×
165
    pfd.fd = s;
×
166
    pfd.events = POLLIN;
×
167
    pfd.revents = 0;
×
168
    d_rfds.push_back(pfd);
×
169
    g_log<<Logger::Error<<"UDP server bound to "<<locala.toStringWithPort()<<endl;
×
170
  }
×
171
}
×
172

173
bool AddressIsUs(const ComboAddress& remote)
174
{
8✔
175
  for(const ComboAddress& us :  g_localaddresses) {
8✔
176
    if(remote == us)
8✔
177
      return true;
1✔
178
    if(IsAnyAddress(us)) {
7✔
179
      int s = socket(remote.sin4.sin_family, SOCK_DGRAM, 0);
6✔
180
      if(s < 0) 
6!
181
        continue;
×
182

183
      if(connect(s, (struct sockaddr*)&remote, remote.getSocklen()) < 0) {
6!
184
        close(s);
×
185
        continue;
×
186
      }
×
187
    
188
      ComboAddress actualLocal;
6✔
189
      actualLocal.sin4.sin_family = remote.sin4.sin_family;
6✔
190
      socklen_t socklen = actualLocal.getSocklen();
6✔
191

192
      if(getsockname(s, (struct sockaddr*) &actualLocal, &socklen) < 0) {
6!
193
        close(s);
×
194
        continue;
×
195
      }
×
196
      close(s);
6✔
197
      actualLocal.sin4.sin_port = us.sin4.sin_port;
6✔
198
      if(actualLocal == remote)
6✔
199
        return true;
3✔
200
    }
6✔
201
  }
7✔
202
  return false;
4✔
203
}
8✔
204

205
UDPNameserver::UDPNameserver( bool additional_socket )
206
{
×
207
  d_can_reuseport = ::arg().mustDo("reuseport");
×
208
  // Are we the main socket (false) or a rebinding using SO_REUSEPORT ?
209
  d_additional_socket = additional_socket;
×
210

211
  if(::arg()["local-address"].empty())
×
212
    g_log<<Logger::Critical<<"PDNS is deaf and mute! Not listening on any interfaces"<<endl;    
×
213

214
  bindAddresses();
×
215
}
×
216

217
void UDPNameserver::send(DNSPacket& p)
218
{
×
219
  const string& buffer=p.getString();
×
220
  g_rs.submitResponse(p, true);
×
221

222
  struct msghdr msgh;
×
223
  struct iovec iov;
×
224
  cmsgbuf_aligned cbuf;
×
225

226
  fillMSGHdr(&msgh, &iov, &cbuf, 0, (char*)buffer.c_str(), buffer.length(), &p.d_remote);
×
227

228
  msgh.msg_control=nullptr;
×
229
  if(p.d_anyLocal) {
×
230
    addCMsgSrcAddr(&msgh, &cbuf, p.d_anyLocal.get_ptr(), 0);
×
231
  }
×
232
  DLOG(g_log<<Logger::Notice<<"Sending a packet to "<< p.getRemote() <<" ("<< buffer.length()<<" octets)"<<endl);
×
233
  if(buffer.length() > p.getMaxReplyLen()) {
×
234
    g_log<<Logger::Error<<"Weird, trying to send a message that needs truncation, "<< buffer.length()<<" > "<<p.getMaxReplyLen()<<". Question was for "<<p.qdomain<<"|"<<p.qtype.toString()<<endl;
×
235
  }
×
236
  if (sendOnNBSocket(p.getSocket(), &msgh) < 0) {
×
237
    int err = errno;
×
238
    g_log<<Logger::Error<<"Error sending reply with sendmsg (socket="<<p.getSocket()<<", dest="<<p.d_remote.toStringWithPort()<<"): "<<stringerror(err)<<endl;
×
239
  }
×
240
}
×
241

242
bool UDPNameserver::receive(DNSPacket& packet, std::string& buffer)
243
{
×
244
  ComboAddress remote;
×
245
  extern StatBag S;
×
246
  ssize_t len=-1;
×
247
  Utility::sock_t sock=-1;
×
248

249
  struct msghdr msgh;
×
250
  struct iovec iov;
×
251
  cmsgbuf_aligned cbuf;
×
252

253
  remote.sin6.sin6_family=AF_INET6; // make sure it is big enough
×
254
  fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), &buffer.at(0), buffer.size(), &remote);
×
255
  
256
  int err;
×
257
  vector<struct pollfd> rfds= d_rfds;
×
258

259
  for(auto &pfd :  rfds) {
×
260
    pfd.events = POLLIN;
×
261
    pfd.revents = 0;
×
262
  }
×
263
    
264
  retry:;
×
265
  
266
  err = poll(&rfds[0], rfds.size(), -1);
×
267
  if(err < 0) {
×
268
    if(errno==EINTR)
×
269
      goto retry;
×
270
    unixDie("Unable to poll for new UDP events");
×
271
  }
×
272
    
273
  for(auto &pfd :  rfds) {
×
274
    if(pfd.revents & POLLIN) {
×
275
      sock=pfd.fd;        
×
276
      if((len=recvmsg(sock, &msgh, 0)) < 0 ) {
×
277
        if(errno != EAGAIN)
×
278
          g_log<<Logger::Error<<"recvfrom gave error, ignoring: "<<stringerror()<<endl;
×
279
        return false;
×
280
      }
×
281
      break;
×
282
    }
×
283
  }
×
284
  if(sock==-1)
×
285
    throw PDNSException("poll betrayed us! (should not happen)");
×
286
  
287
  DLOG(g_log<<"Received a packet " << len <<" bytes long from "<< remote.toString()<<endl);
×
288

289
  BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
×
290

291
  if(remote.sin4.sin_port == 0) // would generate error on responding. sin4 also works for ipv6
×
292
    return false;
×
293
  
294
  packet.setSocket(sock);
×
295
  packet.setRemote(&remote);
×
296

297
  ComboAddress dest;
×
298
  if(HarvestDestinationAddress(&msgh, &dest)) {
×
299
//    cerr<<"Setting d_anyLocal to '"<<dest.toString()<<"'"<<endl;
300
    packet.d_anyLocal = dest;
×
301
  }            
×
302

303
  struct timeval recvtv;
×
304
  if(HarvestTimestamp(&msgh, &recvtv)) {
×
305
    packet.d_dt.setTimeval(recvtv);
×
306
  }
×
307
  else
×
308
    packet.d_dt.set(); // timing
×
309

310
  if (g_proxyProtocolACL.match(remote)) {
×
311
    ComboAddress psource, pdestination;
×
312
    bool proxyProto, tcp;
×
313
    std::vector<ProxyProtocolValue> ppvalues;
×
314

315
    buffer.resize(len);
×
316
    ssize_t used = parseProxyHeader(buffer, proxyProto, psource, pdestination, tcp, ppvalues);
×
317
    if (used <= 0 || (size_t) used > g_proxyProtocolMaximumSize || (len - used) > DNSPacket::s_udpTruncationThreshold) {
×
318
      S.inc("corrupt-packets");
×
319
      S.ringAccount("remotes-corrupt", packet.d_remote);
×
320
      return false;
×
321
    }
×
322
    buffer.erase(0, used);
×
323
    packet.d_inner_remote = psource;
×
324
    packet.d_tcp = tcp;
×
325
  }
×
326
  else {
×
327
    packet.d_inner_remote.reset();
×
328
  }
×
329

330
  if(packet.parse(&buffer.at(0), (size_t) len)<0) {
×
331
    S.inc("corrupt-packets");
×
332
    S.ringAccount("remotes-corrupt", packet.getInnerRemote());
×
333

334
    return false; // unable to parse
×
335
  }
×
336
  
337
  return true;
×
338
}
×
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