• 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

56.1
/pdns/axfr-retriever.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

23
#include "axfr-retriever.hh"
24
#include "arguments.hh"
25
#include "dns_random.hh"
26
#include "utility.hh"
27
#include "resolver.hh"
28
#include "query-local-address.hh"
29

30
using pdns::resolver::parseResult;
31

32
AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
33
                             const DNSName& domain,
34
                             const TSIGTriplet& tt, 
35
                             const ComboAddress* laddr,
36
                             size_t maxReceivedBytes,
37
                             uint16_t timeout)
38
  : d_buf(65536), d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
39
{
7✔
40
  ComboAddress local;
7✔
41
  if (laddr != nullptr) {
7!
42
    local = ComboAddress(*laddr);
7✔
43
  } else {
7✔
44
    if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
×
45
      throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". Address family is not configured for outgoing queries");
×
46
    }
×
47
    local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
×
48
  }
×
49
  d_sock = -1;
7✔
50
  try {
7✔
51
    d_sock = makeQuerySocket(local, false); // make a TCP socket
7✔
52
    if (d_sock < 0)
7!
53
      throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
×
54

55
    d_remote = remote; // mostly for error reporting
7✔
56
    this->connect(timeout);
7✔
57
    d_soacount = 0;
7✔
58
  
59
    vector<uint8_t> packet;
7✔
60
    DNSPacketWriter pw(packet, domain, QType::AXFR);
7✔
61
    pw.getHeader()->id = dns_random_uint16();
7✔
62
  
63
    if(!tt.name.empty()) {
7!
64
      if (tt.algo == DNSName("hmac-md5"))
×
65
        d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
×
66
      else
×
67
        d_trc.d_algoName = tt.algo;
×
68
      d_trc.d_time = time(nullptr);
×
69
      d_trc.d_fudge = 300;
×
70
      d_trc.d_origID=ntohs(pw.getHeader()->id);
×
71
      d_trc.d_eRcode=0;
×
72
      addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
×
73
    }
×
74
  
75
    uint16_t replen=htons(packet.size());
7✔
76
    Utility::iovec iov[2];
7✔
77
    iov[0].iov_base=reinterpret_cast<char*>(&replen);
7✔
78
    iov[0].iov_len=2;
7✔
79
    iov[1].iov_base=packet.data();
7✔
80
    iov[1].iov_len=packet.size();
7✔
81
  
82
    int ret=Utility::writev(d_sock, iov, 2);
7✔
83
    if(ret < 0)
7!
84
      throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
×
85
    if(ret != (int)(2+packet.size())) {
7!
86
      throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
×
87
    }
×
88
  
89
    int res = waitForData(d_sock, timeout, 0);
7✔
90
    
91
    if(!res)
7!
92
      throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
×
93
    if(res<0)
7!
94
      throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
×
95
  }
7✔
96
  catch(...) {
7✔
97
    if(d_sock >= 0)
3!
98
      close(d_sock);
3✔
99
    d_sock = -1;
3✔
100
    throw;
3✔
101
  }
3✔
102
}
7✔
103

104
AXFRRetriever::~AXFRRetriever()
105
{
4✔
106
  close(d_sock);
4✔
107
}
4✔
108

109

110

111
int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
112
{
87✔
113
  if(d_soacount > 1)
87✔
114
    return false;
4✔
115

116
  // d_sock is connected and is about to spit out a packet
117
  int len=getLength(timeout);
83✔
118
  if(len<0)
83!
119
    throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
×
120

121
  if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
83!
122
    throw ResolverException("Reached the maximum number of received bytes during AXFR");
×
123

124
  timeoutReadn(len, timeout);
83✔
125

126
  d_receivedBytes += (uint16_t) len;
83✔
127

128
  MOADNSParser mdp(false, d_buf.data(), len);
83✔
129

130
  int err = mdp.d_header.rcode;
83✔
131

132
  if(err) {
83!
133
    throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
×
134
  }
×
135

136
  if(mdp.d_header.tc) {
83!
137
    throw ResolverException("AXFR chunk had TC bit set");
×
138
  }
×
139

140
  try {
83✔
141
    d_tsigVerifier.check(std::string(d_buf.data(), len), mdp);
83✔
142
  }
83✔
143
  catch(const std::runtime_error& re) {
83✔
144
    throw ResolverException(re.what());
×
145
  }
×
146

147
  if(!records) {
83!
148
    err = parseResult(mdp, DNSName(), 0, 0, &res);
×
149

150
    if (!err) {
×
151
      for(const auto& answer :  mdp.d_answers) {
×
152
        if (answer.d_type == QType::SOA) {
×
153
          d_soacount++;
×
154
        }
×
155
      }
×
156
    }
×
157
  }
×
158
  else {
83✔
159
    records->clear();
83✔
160
    records->reserve(mdp.d_answers.size());
83✔
161

162
    for(auto& r: mdp.d_answers) {
25,111✔
163
      if (r.d_type == QType::SOA) {
25,111✔
164
        d_soacount++;
8✔
165
      }
8✔
166

167
      records->push_back(std::move(r));
25,111✔
168
    }
25,111✔
169
  }
83✔
170

171
  return true;
83✔
172
}
83✔
173

174
void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
175
{
166✔
176
  const time_t start = time(nullptr);
166✔
177
  uint16_t bytesRead = 0;
166✔
178

179
  while (bytesRead < bytes) {
341✔
180
    // coverity[store_truncates_time_t]
181
    auto elapsed = time(nullptr) - start;
175✔
182
    if (elapsed > timeoutsec) {
175!
183
      throw ResolverException("Timeout while reading data from remote nameserver over TCP");
×
184
    }
×
185
    auto res = waitForData(d_sock, static_cast<int>(timeoutsec - elapsed));
175✔
186
    if (res < 0) {
175!
187
      throw ResolverException("Reading data from remote nameserver over TCP: " + stringerror());
×
188
    }
×
189
    if (res == 0) {
175!
190
      throw ResolverException("Timeout while reading data from remote nameserver over TCP");
×
191
    }
×
192

193
    auto received = recv(d_sock, &d_buf.at(bytesRead), bytes - bytesRead, 0);
175✔
194
    if (received < 0) {
175!
195
      throw ResolverException("Reading data from remote nameserver over TCP: " + stringerror());
×
196
    }
×
197
    if (received == 0) {
175!
198
      throw ResolverException("Remote nameserver closed TCP connection");
×
199
    }
×
200
    bytesRead += static_cast<uint16_t>(received);
175✔
201
  }
175✔
202
}
166✔
203

204
void AXFRRetriever::connect(uint16_t timeout)
205
{
7✔
206
  setNonBlocking( d_sock );
7✔
207

208
  int err;
7✔
209

210
  if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
7!
211
    try {
×
212
      closesocket(d_sock);
×
213
    }
×
214
    catch(const PDNSException& e) {
×
215
      d_sock=-1;
×
216
      throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
×
217
    }
×
218

219
    throw ResolverException("connect: "+stringerror());
×
220
  }
×
221

222
  if(!err)
7!
223
    goto done;
×
224

225
  err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
7✔
226
  
227
  if(!err) {
7!
228
    try {
×
229
      closesocket(d_sock); // timeout
×
230
    }
×
231
    catch(const PDNSException& e) {
×
232
      d_sock=-1;
×
233
      throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
×
234
    }
×
235

236
    d_sock=-1;
×
237
    errno=ETIMEDOUT;
×
238
    
239
    throw ResolverException("Timeout connecting to server");
×
240
  }
×
241
  else if(err < 0) {
7!
242
    throw ResolverException("Error connecting: "+stringerror());
×
243
  }
×
244
  else {
7✔
245
    Utility::socklen_t len=sizeof(err);
7✔
246
    if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
7!
247
      throw ResolverException("Error connecting: "+stringerror()); // Solaris
×
248

249
    if(err)
7✔
250
      throw ResolverException("Error connecting: "+string(strerror(err)));
3✔
251
  }
7✔
252
  
253
 done:
4✔
254
  setBlocking( d_sock );
4✔
255
  // d_sock now connected
256
}
4✔
257

258
int AXFRRetriever::getLength(uint16_t timeout)
259
{
83✔
260
  timeoutReadn(2, timeout);
83✔
261
  return (unsigned char)d_buf.at(0)*256+(unsigned char)d_buf.at(1);
83✔
262
}
83✔
263

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