• 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

87.83
/pdns/dnsdistdist/dnsdist-discovery.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 "config.h"
24
#include "dnsdist-discovery.hh"
25
#include "dnsdist-backend.hh"
26
#include "dnsdist.hh"
27
#include "dnsdist-random.hh"
28
#include "dnsparser.hh"
29
#include "dolog.hh"
30
#include "sstuff.hh"
31
#include "threadname.hh"
32

33
namespace dnsdist
34
{
35

36
const DNSName ServiceDiscovery::s_discoveryDomain{"_dns.resolver.arpa."};
37
const QType ServiceDiscovery::s_discoveryType{QType::SVCB};
38
const uint16_t ServiceDiscovery::s_defaultDoHSVCKey{7};
39

40
bool ServiceDiscovery::addUpgradeableServer(std::shared_ptr<DownstreamState>& server, uint32_t interval, std::string poolAfterUpgrade, uint16_t dohSVCKey, bool keepAfterUpgrade)
41
{
36✔
42
  s_upgradeableBackends.lock()->push_back(std::make_shared<UpgradeableBackend>(UpgradeableBackend{server, std::move(poolAfterUpgrade), 0, interval, dohSVCKey, keepAfterUpgrade}));
36✔
43
  return true;
36✔
44
}
36✔
45

46
struct DesignatedResolvers
47
{
48
  DNSName target;
49
  std::set<SvcParam> params;
50
  std::vector<ComboAddress> hints;
51
};
52

53
static bool parseSVCParams(const PacketBuffer& answer, std::map<uint16_t, DesignatedResolvers>& resolvers)
54
{
10✔
55
  std::map<DNSName, std::vector<ComboAddress>> hints;
10✔
56
  const dnsheader_aligned dh(answer.data());
10✔
57
  PacketReader pr(std::string_view(reinterpret_cast<const char*>(answer.data()), answer.size()));
10✔
58
  uint16_t qdcount = ntohs(dh->qdcount);
10✔
59
  uint16_t ancount = ntohs(dh->ancount);
10✔
60
  uint16_t nscount = ntohs(dh->nscount);
10✔
61
  uint16_t arcount = ntohs(dh->arcount);
10✔
62

63
  DNSName rrname;
10✔
64
  uint16_t rrtype;
10✔
65
  uint16_t rrclass;
10✔
66

67
  size_t idx = 0;
10✔
68
  /* consume qd */
69
  for (; idx < qdcount; idx++) {
20✔
70
    rrname = pr.getName();
10✔
71
    rrtype = pr.get16BitInt();
10✔
72
    rrclass = pr.get16BitInt();
10✔
73
    (void)rrtype;
10✔
74
    (void)rrclass;
10✔
75
  }
10✔
76

77
  /* parse AN */
78
  for (idx = 0; idx < ancount; idx++) {
20✔
79
    string blob;
10✔
80
    struct dnsrecordheader ah;
10✔
81
    rrname = pr.getName();
10✔
82
    pr.getDnsrecordheader(ah);
10✔
83

84
    if (ah.d_type == QType::SVCB) {
10✔
85
      auto prio = pr.get16BitInt();
9✔
86
      auto target = pr.getName();
9✔
87
      std::set<SvcParam> params;
9✔
88

89
      if (prio != 0) {
9!
90
        pr.xfrSvcParamKeyVals(params);
9✔
91
      }
9✔
92

93
      resolvers[prio] = {std::move(target), std::move(params), {}};
9✔
94
    }
9✔
95
    else {
1✔
96
      pr.xfrBlob(blob);
1✔
97
    }
1✔
98
  }
10✔
99

100
  /* parse NS */
101
  for (idx = 0; idx < nscount; idx++) {
11✔
102
    string blob;
1✔
103
    struct dnsrecordheader ah;
1✔
104
    rrname = pr.getName();
1✔
105
    pr.getDnsrecordheader(ah);
1✔
106

107
    pr.xfrBlob(blob);
1✔
108
  }
1✔
109

110
  /* parse additional for hints */
111
  for (idx = 0; idx < arcount; idx++) {
22✔
112
    string blob;
12✔
113
    struct dnsrecordheader ah;
12✔
114
    rrname = pr.getName();
12✔
115
    pr.getDnsrecordheader(ah);
12✔
116

117
    if (ah.d_type == QType::A) {
12✔
118
      ComboAddress addr;
1✔
119
      pr.xfrCAWithoutPort(4, addr);
1✔
120
      hints[rrname].push_back(addr);
1✔
121
    }
1✔
122
    else if (ah.d_type == QType::AAAA) {
11✔
123
      ComboAddress addr;
1✔
124
      pr.xfrCAWithoutPort(6, addr);
1✔
125
      hints[rrname].push_back(addr);
1✔
126
    }
1✔
127
    else {
10✔
128
      pr.xfrBlob(blob);
10✔
129
    }
10✔
130
  }
12✔
131

132
  for (auto& resolver : resolvers) {
10✔
133
    auto hint = hints.find(resolver.second.target);
9✔
134
    if (hint != hints.end()) {
9✔
135
      resolver.second.hints = hint->second;
1✔
136
    }
1✔
137
  }
9✔
138

139
  return !resolvers.empty();
10✔
140
}
10✔
141

142
static bool handleSVCResult(const PacketBuffer& answer, const ComboAddress& existingAddr, uint16_t dohSVCKey, ServiceDiscovery::DiscoveredResolverConfig& config)
143
{
10✔
144
  std::map<uint16_t, DesignatedResolvers> resolvers;
10✔
145
  if (!parseSVCParams(answer, resolvers)) {
10✔
146
    vinfolog("No configuration found in response for backend %s", existingAddr.toStringWithPort());
1!
147
    return false;
1✔
148
  }
1✔
149

150
  for (const auto& [priority, resolver] : resolvers) {
9✔
151
    (void)priority;
9✔
152
    /* do not compare the ports */
153
    std::set<ComboAddress, ComboAddress::addressOnlyLessThan> tentativeAddresses;
9✔
154
    ServiceDiscovery::DiscoveredResolverConfig tempConfig;
9✔
155
    tempConfig.d_addr.sin4.sin_family = 0;
9✔
156

157
    for (const auto& param : resolver.params) {
25✔
158
      if (param.getKey() == SvcParam::alpn) {
25✔
159
        auto alpns = param.getALPN();
9✔
160
        for (const auto& alpn : alpns) {
9✔
161
          if (alpn == "dot") {
9✔
162
            tempConfig.d_protocol = dnsdist::Protocol::DoT;
5✔
163
            if (tempConfig.d_port == 0) {
5!
164
              tempConfig.d_port = 853;
5✔
165
            }
5✔
166
          }
5✔
167
          else if (alpn == "h2") {
4✔
168
            tempConfig.d_protocol = dnsdist::Protocol::DoH;
3✔
169
            if (tempConfig.d_port == 0) {
3!
170
              tempConfig.d_port = 443;
3✔
171
            }
3✔
172
          }
3✔
173
        }
9✔
174
      }
9✔
175
      else if (param.getKey() == SvcParam::port) {
16✔
176
        tempConfig.d_port = param.getPort();
6✔
177
      }
6✔
178
      else if (param.getKey() == SvcParam::ipv4hint || param.getKey() == SvcParam::ipv6hint) {
10!
179
        if (tempConfig.d_addr.sin4.sin_family == 0) {
8!
180
          auto hints = param.getIPHints();
8✔
181
          for (const auto& hint : hints) {
8✔
182
            tentativeAddresses.insert(hint);
8✔
183
          }
8✔
184
        }
8✔
185
      }
8✔
186
      else if (dohSVCKey != 0 && param.getKey() == dohSVCKey) {
2!
187
        tempConfig.d_dohPath = param.getValue();
2✔
188
        auto expression = tempConfig.d_dohPath.find('{');
2✔
189
        if (expression != std::string::npos) {
2!
190
          /* nuke the {?dns} expression, if any, as we only support POST anyway */
191
          tempConfig.d_dohPath.resize(expression);
2✔
192
        }
2✔
193
      }
2✔
194
    }
25✔
195

196
    if (tempConfig.d_protocol == dnsdist::Protocol::DoH) {
9✔
197
#ifndef HAVE_DNS_OVER_HTTPS
198
      continue;
199
#endif
200
      if (tempConfig.d_dohPath.empty()) {
3✔
201
        vinfolog("Got a DoH upgrade offered for %s but no path, skipping", existingAddr.toStringWithPort());
1!
202
        continue;
1✔
203
      }
1✔
204
    }
3✔
205
    else if (tempConfig.d_protocol == dnsdist::Protocol::DoT) {
6✔
206
#ifndef HAVE_DNS_OVER_TLS
207
      continue;
208
#endif
209
    }
5✔
210
    else {
1✔
211
      continue;
1✔
212
    }
1✔
213

214
    /* we have a config that we can use! */
215
    for (const auto& hint : resolver.hints) {
7✔
216
      tentativeAddresses.insert(hint);
2✔
217
    }
2✔
218

219
    /* we prefer the address we already know, whenever possible */
220
    if (tentativeAddresses.count(existingAddr) != 0) {
7✔
221
      tempConfig.d_addr = existingAddr;
5✔
222
    }
5✔
223
    else {
2✔
224
      tempConfig.d_addr = *tentativeAddresses.begin();
2✔
225
    }
2✔
226

227
    tempConfig.d_subjectName = resolver.target.toStringNoDot();
7✔
228
    tempConfig.d_addr.sin4.sin_port = tempConfig.d_port;
7✔
229

230
    config = std::move(tempConfig);
7✔
231
    return true;
7✔
232
  }
9✔
233

234
  return false;
2✔
235
}
9✔
236

237
bool ServiceDiscovery::getDiscoveredConfig(const UpgradeableBackend& upgradeableBackend, ServiceDiscovery::DiscoveredResolverConfig& config)
238
{
18✔
239
  const auto verbose = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
18✔
240
  const auto& backend = upgradeableBackend.d_ds;
18✔
241
  const auto& addr = backend->d_config.remote;
18✔
242
  try {
18✔
243
    auto id = dnsdist::getRandomDNSID();
18✔
244
    PacketBuffer packet;
18✔
245
    GenericDNSPacketWriter pw(packet, s_discoveryDomain, s_discoveryType);
18✔
246
    pw.getHeader()->id = id;
18✔
247
    pw.getHeader()->rd = 1;
18✔
248
    pw.addOpt(4096, 0, 0);
18✔
249
    pw.commit();
18✔
250

251
    uint16_t querySize = static_cast<uint16_t>(packet.size());
18✔
252
    const uint8_t sizeBytes[] = {static_cast<uint8_t>(querySize / 256), static_cast<uint8_t>(querySize % 256)};
18✔
253
    packet.insert(packet.begin(), sizeBytes, sizeBytes + 2);
18✔
254

255
    Socket sock(addr.sin4.sin_family, SOCK_STREAM);
18✔
256
    sock.setNonBlocking();
18✔
257

258
#ifdef SO_BINDTODEVICE
18✔
259
    if (!backend->d_config.sourceItfName.empty()) {
18✔
260
      setsockopt(sock.getHandle(), SOL_SOCKET, SO_BINDTODEVICE, backend->d_config.sourceItfName.c_str(), backend->d_config.sourceItfName.length());
2✔
261
    }
2✔
262
#endif
18✔
263

264
    if (!IsAnyAddress(backend->d_config.sourceAddr)) {
18✔
265
      sock.setReuseAddr();
2✔
266
#ifdef IP_BIND_ADDRESS_NO_PORT
2✔
267
      if (backend->d_config.ipBindAddrNoPort) {
2!
268
        SSetsockopt(sock.getHandle(), SOL_IP, IP_BIND_ADDRESS_NO_PORT, 1);
2✔
269
      }
2✔
270
#endif
2✔
271
      sock.bind(backend->d_config.sourceAddr);
2✔
272
    }
2✔
273
    sock.connect(addr, backend->d_config.tcpConnectTimeout);
18✔
274

275
    sock.writenWithTimeout(reinterpret_cast<const char*>(packet.data()), packet.size(), backend->d_config.tcpSendTimeout);
18✔
276

277
    const struct timeval remainingTime = {.tv_sec = backend->d_config.tcpRecvTimeout, .tv_usec = 0};
18✔
278
    uint16_t responseSize = 0;
18✔
279
    auto got = readn2WithTimeout(sock.getHandle(), &responseSize, sizeof(responseSize), remainingTime);
18✔
280
    if (got != sizeof(responseSize)) {
18!
281
      if (verbose) {
×
282
        warnlog("Error while waiting for the ADD upgrade response size from backend %s: %d", addr.toStringWithPort(), got);
×
283
      }
×
284
      return false;
×
285
    }
×
286

287
    packet.resize(ntohs(responseSize));
18✔
288

289
    got = readn2WithTimeout(sock.getHandle(), packet.data(), packet.size(), remainingTime);
18✔
290
    if (got != packet.size()) {
18!
291
      if (verbose) {
×
292
        warnlog("Error while waiting for the ADD upgrade response from backend %s: %d", addr.toStringWithPort(), got);
×
293
      }
×
294
      return false;
×
295
    }
×
296

297
    if (packet.size() <= sizeof(struct dnsheader)) {
18✔
298
      if (verbose) {
1!
299
        warnlog("Too short answer of size %d received from the backend %s", packet.size(), addr.toStringWithPort());
1✔
300
      }
1✔
301
      return false;
1✔
302
    }
1✔
303

304
    struct dnsheader d;
17✔
305
    memcpy(&d, packet.data(), sizeof(d));
17✔
306
    if (d.id != id) {
17✔
307
      if (verbose) {
1!
308
        warnlog("Invalid ID (%d / %d) received from the backend %s", d.id, id, addr.toStringWithPort());
1✔
309
      }
1✔
310
      return false;
1✔
311
    }
1✔
312

313
    if (d.rcode != RCode::NoError) {
16✔
314
      if (verbose) {
1!
315
        warnlog("Response code '%s' received from the backend %s for '%s'", RCode::to_s(d.rcode), addr.toStringWithPort(), s_discoveryDomain);
1✔
316
      }
1✔
317

318
      return false;
1✔
319
    }
1✔
320

321
    if (ntohs(d.qdcount) != 1) {
15✔
322
      if (verbose) {
1!
323
        warnlog("Invalid answer (qdcount %d) received from the backend %s", ntohs(d.qdcount), addr.toStringWithPort());
1✔
324
      }
1✔
325
      return false;
1✔
326
    }
1✔
327

328
    uint16_t receivedType;
14✔
329
    uint16_t receivedClass;
14✔
330
    DNSName receivedName(reinterpret_cast<const char*>(packet.data()), packet.size(), sizeof(dnsheader), false, &receivedType, &receivedClass);
14✔
331

332
    if (receivedName != s_discoveryDomain || receivedType != s_discoveryType || receivedClass != QClass::IN) {
14!
333
      if (verbose) {
1!
334
        warnlog("Invalid answer, either the qname (%s / %s), qtype (%s / %s) or qclass (%s / %s) does not match, received from the backend %s", receivedName, s_discoveryDomain, QType(receivedType).toString(), s_discoveryType.toString(), QClass(receivedClass).toString(), QClass::IN.toString(), addr.toStringWithPort());
1✔
335
      }
1✔
336
      return false;
1✔
337
    }
1✔
338

339
    return handleSVCResult(packet, addr, upgradeableBackend.d_dohKey, config);
13✔
340
  }
14✔
341
  catch (const std::exception& e) {
18✔
342
    warnlog("Error while trying to discover backend upgrade for %s: %s", addr.toStringWithPort(), e.what());
3✔
343
  }
3✔
344
  catch (...) {
18✔
345
    warnlog("Error while trying to discover backend upgrade for %s", addr.toStringWithPort());
×
346
  }
×
347

348
  return false;
3✔
349
}
18✔
350

351
static bool checkBackendUsability(std::shared_ptr<DownstreamState>& ds)
352
{
7✔
353
  try {
7✔
354
    Socket sock(ds->d_config.remote.sin4.sin_family, SOCK_STREAM);
7✔
355
    sock.setNonBlocking();
7✔
356

357
    if (!IsAnyAddress(ds->d_config.sourceAddr)) {
7✔
358
      sock.setReuseAddr();
1✔
359
#ifdef IP_BIND_ADDRESS_NO_PORT
1✔
360
      if (ds->d_config.ipBindAddrNoPort) {
1!
361
        SSetsockopt(sock.getHandle(), SOL_IP, IP_BIND_ADDRESS_NO_PORT, 1);
1✔
362
      }
1✔
363
#endif
1✔
364

365
      if (!ds->d_config.sourceItfName.empty()) {
1!
366
#ifdef SO_BINDTODEVICE
1✔
367
        setsockopt(sock.getHandle(), SOL_SOCKET, SO_BINDTODEVICE, ds->d_config.sourceItfName.c_str(), ds->d_config.sourceItfName.length());
1✔
368
#endif
1✔
369
      }
1✔
370
      sock.bind(ds->d_config.sourceAddr);
1✔
371
    }
1✔
372

373
    auto handler = std::make_unique<TCPIOHandler>(ds->d_config.d_tlsSubjectName, ds->d_config.d_tlsSubjectIsAddr, sock.releaseHandle(), timeval{ds->d_config.checkTimeout, 0}, ds->d_tlsCtx);
7✔
374
    handler->connect(ds->d_config.tcpFastOpen, ds->d_config.remote, timeval{ds->d_config.checkTimeout, 0});
7✔
375
    return true;
7✔
376
  }
7✔
377
  catch (const std::exception& e) {
7✔
378
    vinfolog("Exception when trying to use a newly upgraded backend %s (subject %s): %s", ds->getNameWithAddr(), ds->d_config.d_tlsSubjectName, e.what());
4!
379
  }
4✔
380
  catch (...) {
7✔
381
    vinfolog("Exception when trying to use a newly upgraded backend %s (subject %s)", ds->getNameWithAddr(), ds->d_config.d_tlsSubjectName);
×
382
  }
×
383

384
  return false;
4✔
385
}
7✔
386

387
bool ServiceDiscovery::tryToUpgradeBackend(const UpgradeableBackend& backend)
388
{
18✔
389
  ServiceDiscovery::DiscoveredResolverConfig discoveredConfig;
18✔
390

391
  vinfolog("Trying to discover configuration for backend %s", backend.d_ds->getNameWithAddr());
18!
392
  if (!ServiceDiscovery::getDiscoveredConfig(backend, discoveredConfig)) {
18✔
393
    return false;
11✔
394
  }
11✔
395

396
  if (discoveredConfig.d_protocol != dnsdist::Protocol::DoT && discoveredConfig.d_protocol != dnsdist::Protocol::DoH) {
7!
397
    return false;
×
398
  }
×
399

400
  DownstreamState::Config config(backend.d_ds->d_config);
7✔
401
  config.remote = discoveredConfig.d_addr;
7✔
402
  config.remote.setPort(discoveredConfig.d_port);
7✔
403

404
  if (backend.keepAfterUpgrade && config.availability == DownstreamState::Availability::Up) {
7!
405
    /* it's OK to keep the forced state if we replace the initial
406
       backend, but if we are adding a new backend, it should not
407
       inherit that setting, especially since DoX backends are much
408
       more likely to fail (certificate errors, ...) */
409
    if (config.d_upgradeToLazyHealthChecks) {
1!
410
      config.availability = DownstreamState::Availability::Lazy;
×
411
    }
×
412
    else {
1✔
413
      config.availability = DownstreamState::Availability::Auto;
1✔
414
    }
1✔
415
  }
1✔
416

417
  ComboAddress::addressOnlyEqual comparator;
7✔
418
  config.d_dohPath = discoveredConfig.d_dohPath;
7✔
419
  if (!discoveredConfig.d_subjectName.empty() && comparator(config.remote, backend.d_ds->d_config.remote)) {
7!
420
    /* same address, we can used the supplied name for validation */
421
    config.d_tlsSubjectName = discoveredConfig.d_subjectName;
5✔
422
  }
5✔
423
  else {
2✔
424
    /* different name, and draft-ietf-add-ddr-04 states that:
425
       "In order to be considered a verified Designated Resolver, the TLS
426
       certificate presented by the Designated Resolver MUST contain the IP
427
       address of the designating Unencrypted Resolver in a subjectAltName
428
       extension."
429
    */
430
    config.d_tlsSubjectName = backend.d_ds->d_config.remote.toString();
2✔
431
    config.d_tlsSubjectIsAddr = true;
2✔
432
  }
2✔
433

434
  if (!backend.d_poolAfterUpgrade.empty()) {
7✔
435
    config.pools.clear();
1✔
436
    config.pools.insert(backend.d_poolAfterUpgrade);
1✔
437
  }
1✔
438

439
  try {
7✔
440
    /* create new backend, put it into the right pool(s) */
441
    auto tlsCtx = getTLSContext(config.d_tlsParams);
7✔
442
    auto newServer = std::make_shared<DownstreamState>(std::move(config), std::move(tlsCtx), true);
7✔
443

444
    /* check that we can connect to the backend (including certificate validation */
445
    if (!checkBackendUsability(newServer)) {
7✔
446
      vinfolog("Failed to use the automatically upgraded server %s, skipping for now", newServer->getNameWithAddr());
4!
447
      return false;
4✔
448
    }
4✔
449

450
    infolog("Added automatically upgraded server %s", newServer->getNameWithAddr());
3✔
451

452
    if (!newServer->d_config.pools.empty()) {
3✔
453
      for (const auto& poolName : newServer->d_config.pools) {
2✔
454
        addServerToPool(poolName, newServer);
2✔
455
      }
2✔
456
    }
2✔
457
    else {
1✔
458
      addServerToPool("", newServer);
1✔
459
    }
1✔
460

461
    newServer->start();
3✔
462

463
    /* remove the existing backend if needed */
464
    if (!backend.keepAfterUpgrade) {
3✔
465
      dnsdist::configuration::updateRuntimeConfiguration([&backend](dnsdist::configuration::RuntimeConfiguration& runtimeConfig) {
2✔
466
        auto& backends = runtimeConfig.d_backends;
2✔
467
        for (auto backendIt = backends.begin(); backendIt != backends.end(); ++backendIt) {
8!
468
          if (*backendIt == backend.d_ds) {
8✔
469
            backends.erase(backendIt);
2✔
470
            break;
2✔
471
          }
2✔
472
        }
8✔
473
      });
2✔
474

475
      for (const string& poolName : backend.d_ds->d_config.pools) {
2✔
476
        removeServerFromPool(poolName, backend.d_ds);
1✔
477
      }
1✔
478
      /* the server might also be in the default pool */
479
      removeServerFromPool("", backend.d_ds);
2✔
480
    }
2✔
481

482
    dnsdist::backend::registerNewBackend(newServer);
3✔
483

484
    if (!backend.keepAfterUpgrade) {
3✔
485
      backend.d_ds->stop();
2✔
486
    }
2✔
487

488
    return true;
3✔
489
  }
7✔
490
  catch (const std::exception& e) {
7✔
491
    warnlog("Error when trying to upgrade a discovered backend: %s", e.what());
×
492
  }
×
493

494
  return false;
×
495
}
7✔
496

497
void ServiceDiscovery::worker()
498
{
320✔
499
  setThreadName("dnsdist/discove");
320✔
500
  while (true) {
640✔
501
    time_t now = time(nullptr);
320✔
502

503
    auto upgradeables = *(s_upgradeableBackends.lock());
320✔
504
    std::set<std::shared_ptr<DownstreamState>> upgradedBackends;
320✔
505

506
    for (auto backendIt = upgradeables.begin(); backendIt != upgradeables.end();) {
338✔
507
      auto& backend = *backendIt;
18✔
508
      try {
18✔
509
        if (backend->d_nextCheck > now) {
18!
510
          ++backendIt;
×
511
          continue;
×
512
        }
×
513

514
        auto upgraded = tryToUpgradeBackend(*backend);
18✔
515
        if (upgraded) {
18✔
516
          upgradedBackends.insert(backend->d_ds);
3✔
517
          backendIt = upgradeables.erase(backendIt);
3✔
518
          continue;
3✔
519
        }
3✔
520
      }
18✔
521
      catch (const std::exception& e) {
18✔
522
        vinfolog("Exception in the Service Discovery thread: %s", e.what());
×
523
      }
×
524
      catch (...) {
18✔
525
        vinfolog("Exception in the Service Discovery thread");
×
526
      }
×
527

528
      backend->d_nextCheck = now + backend->d_interval;
15✔
529
      ++backendIt;
15✔
530
    }
15✔
531

532
    {
320✔
533
      auto backends = s_upgradeableBackends.lock();
320✔
534
      for (auto it = backends->begin(); it != backends->end();) {
338✔
535
        if (upgradedBackends.count((*it)->d_ds) != 0) {
18✔
536
          it = backends->erase(it);
3✔
537
        }
3✔
538
        else {
15✔
539
          ++it;
15✔
540
        }
15✔
541
      }
18✔
542
    }
320✔
543

544
    /* we could sleep until the next check but a new backend
545
       could be added in the meantime, so let's just check every
546
       minute if we have something to do */
547
    sleep(60);
320✔
548
  }
320✔
549
}
320✔
550

551
bool ServiceDiscovery::run()
552
{
320✔
553
  s_thread = std::thread(&ServiceDiscovery::worker);
320✔
554
  s_thread.detach();
320✔
555

556
  return true;
320✔
557
}
320✔
558

559
LockGuarded<std::vector<std::shared_ptr<ServiceDiscovery::UpgradeableBackend>>> ServiceDiscovery::s_upgradeableBackends;
560
std::thread ServiceDiscovery::s_thread;
561
}
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