]> git.ipfire.org Git - thirdparty/squid.git/blame - src/icmp/net_db.cc
Maintenance: replace most NULL with nullptr (#1402)
[thirdparty/squid.git] / src / icmp / net_db.cc
CommitLineData
516350ca 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
516350ca 7 */
8
bbc27441
AJ
9/* DEBUG: section 38 Network Measurement Database */
10
add2192d 11/*
12 * XXX XXX XXX
13 *
14 * This code may be slightly broken now. If you're getting consistent
15 * (sometimes working) corrupt data exchanges, please contact adrian
16 * (adrian@squid-cache.org) to sort them out.
17 */
18
582c2af2 19#include "squid.h"
a011edee 20#include "CachePeer.h"
aa839030 21#include "cbdata.h"
a553a5a3 22#include "event.h"
528b2c61 23#include "fde.h"
b3f7fd88 24#include "fs_io.h"
eb13c21e 25#include "FwdState.h"
5bed43d6 26#include "HttpReply.h"
5bed43d6 27#include "icmp/net_db.h"
fc54b8d2 28#include "internal.h"
5bed43d6
FC
29#include "ip/Address.h"
30#include "log/File.h"
5bed43d6
FC
31#include "MemObject.h"
32#include "mgr/Registration.h"
b6149797 33#include "mime_header.h"
5bed43d6 34#include "neighbors.h"
cb365059 35#include "PeerSelectState.h"
122a6e3c 36#include "sbuf/SBuf.h"
2a0ab044 37#include "SquidConfig.h"
5bed43d6 38#include "Store.h"
e87137f1 39#include "StoreClient.h"
5bed43d6 40#include "tools.h"
5bed43d6 41#include "wordlist.h"
fc54b8d2 42
582c2af2
FC
43#if HAVE_SYS_STAT_H
44#include <sys/stat.h>
45#endif
46
a97cfa48 47#if USE_ICMP
9b5c4a9a 48#include "icmp/IcmpSquid.h"
714e68b7 49#include "ipcache.h"
4b725156 50#include "StoreClient.h"
51
add2192d 52typedef enum {
fa80a8ef 53 STATE_NONE,
54 STATE_HEADER,
55 STATE_BODY
add2192d 56} netdb_conn_state_t;
8b833697 57
fa91d030
AJ
58class netdbExchangeState
59{
60 CBDATA_CLASS(netdbExchangeState);
61
62public:
891a70f1 63 netdbExchangeState(CachePeer *aPeer, const HttpRequestPointer &theReq) :
64 p(aPeer),
65 r(theReq)
fa91d030 66 {
891a70f1 67 assert(r);
fa91d030
AJ
68 // TODO: check if we actually need to do this. should be implicit
69 r->http_ver = Http::ProtocolVersion();
70 }
71
72 ~netdbExchangeState() {
73 debugs(38, 3, e->url());
74 storeUnregister(sc, e, this);
75 e->unlock("netdbExchangeDone");
fa91d030
AJ
76 }
77
891a70f1 78 CbcPointer<CachePeer> p;
79 StoreEntry *e = nullptr;
80 store_client *sc = nullptr;
81 HttpRequestPointer r;
122a6e3c
AR
82
83 /// for receiving a NetDB reply body from Store and interpreting it
84 Store::ParsingBuffer parsingBuffer;
85
891a70f1 86 netdb_conn_state_t connstate = STATE_HEADER;
fa91d030
AJ
87};
88
89CBDATA_CLASS_INIT(netdbExchangeState);
9ad1cbca 90
aee3523a
AR
91static hash_table *addr_table = nullptr;
92static hash_table *host_table = nullptr;
87801fcb 93
b7ac5457 94Ip::Address networkFromInaddr(const Ip::Address &a);
f5b8bbc4 95static void netdbRelease(netdbEntry * n);
62e76326 96
b7ac5457 97static void netdbHashInsert(netdbEntry * n, Ip::Address &addr);
f5b8bbc4 98static void netdbHashDelete(const char *key);
587d8445 99static void netdbHostInsert(netdbEntry * n, const char *hostname);
100static void netdbHostDelete(const net_db_name * x);
f5b8bbc4 101static void netdbPurgeLRU(void);
23d92c64 102static netdbEntry *netdbLookupHost(const char *key);
f5b8bbc4 103static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *);
a3c6762c 104static net_db_peer *netdbPeerAdd(netdbEntry * n, CachePeer * e);
858783c9 105static const char *netdbPeerName(const char *name);
b69f7771 106static IPH netdbSendPing;
9ad1cbca 107static STCB netdbExchangeHandleReply;
429fdbec 108
a3c6762c 109/* We have to keep a local list of CachePeer names. The Peers structure
429fdbec 110 * gets freed during a reconfigure. We want this database to
111 * remain persisitent, so _net_db_peer->peername points into this
112 * linked list */
aee3523a 113static wordlist *peer_names = nullptr;
67508012 114
87801fcb 115static void
b7ac5457 116netdbHashInsert(netdbEntry * n, Ip::Address &addr)
87801fcb 117{
4dd643d5 118 networkFromInaddr(addr).toStr(n->network, MAX_IPSTRLEN);
4ec218d5 119 n->key = n->network;
aee3523a 120 assert(hash_lookup(addr_table, n->network) == nullptr);
4ec218d5 121 hash_join(addr_table, n);
87801fcb 122}
123
124static void
0ee4272b 125netdbHashDelete(const char *key)
87801fcb 126{
e6ccf245 127 hash_link *hptr = (hash_link *)hash_lookup(addr_table, key);
62e76326 128
aee3523a 129 if (hptr == nullptr) {
62e76326 130 debug_trap("netdbHashDelete: key not found");
131 return;
87801fcb 132 }
62e76326 133
e0b1c6aa 134 hash_remove_link(addr_table, hptr);
67508012 135}
136
3c670b50
AJ
137net_db_name::net_db_name(const char *hostname, netdbEntry *e) :
138 next(e ? e->hosts : nullptr),
139 net_db_entry(e)
140{
4ec218d5 141 key = xstrdup(hostname);
3c670b50
AJ
142 if (e) {
143 e->hosts = this;
144 ++ e->link_count;
145 }
146}
147
67508012 148static void
587d8445 149netdbHostInsert(netdbEntry * n, const char *hostname)
67508012 150{
3c670b50 151 net_db_name *x = new net_db_name(hostname, n);
aee3523a 152 assert(hash_lookup(host_table, hostname) == nullptr);
4ec218d5 153 hash_join(host_table, x);
67508012 154}
155
156static void
587d8445 157netdbHostDelete(const net_db_name * x)
67508012 158{
aee3523a
AR
159 assert(x != nullptr);
160 assert(x->net_db_entry != nullptr);
3c670b50
AJ
161
162 netdbEntry *n = x->net_db_entry;
7c64cac7 163 -- n->link_count;
62e76326 164
3c670b50 165 for (auto **X = &n->hosts; *X; X = &(*X)->next) {
62e76326 166 if (*X == x) {
167 *X = x->next;
168 break;
169 }
587d8445 170 }
62e76326 171
587d8445 172 hash_remove_link(host_table, (hash_link *) x);
3c670b50 173 delete x;
87801fcb 174}
175
176static netdbEntry *
0ee4272b 177netdbLookupHost(const char *key)
87801fcb 178{
587d8445 179 net_db_name *x = (net_db_name *) hash_lookup(host_table, key);
aee3523a 180 return x ? x->net_db_entry : nullptr;
87801fcb 181}
182
c907000b 183static void
184netdbRelease(netdbEntry * n)
185{
429fdbec 186 net_db_name *x;
587d8445 187 net_db_name *next;
62e76326 188
c907000b 189 for (x = n->hosts; x; x = next) {
62e76326 190 next = x->next;
191 netdbHostDelete(x);
c907000b 192 }
62e76326 193
aee3523a 194 n->hosts = nullptr;
429fdbec 195 safe_free(n->peers);
aee3523a 196 n->peers = nullptr;
429fdbec 197 n->n_peers = 0;
198 n->n_peers_alloc = 0;
62e76326 199
c907000b 200 if (n->link_count == 0) {
62e76326 201 netdbHashDelete(n->network);
1a7cfe02 202 delete n;
c907000b 203 }
204}
205
206static int
79d39a72 207netdbLRU(const void *A, const void *B)
c907000b 208{
e6ccf245 209 const netdbEntry *const *n1 = (const netdbEntry *const *)A;
210 const netdbEntry *const *n2 = (const netdbEntry *const *)B;
62e76326 211
c907000b 212 if ((*n1)->last_use_time > (*n2)->last_use_time)
62e76326 213 return (1);
214
c907000b 215 if ((*n1)->last_use_time < (*n2)->last_use_time)
62e76326 216 return (-1);
217
c907000b 218 return (0);
219}
220
c907000b 221static void
222netdbPurgeLRU(void)
223{
224 netdbEntry *n;
225 netdbEntry **list;
226 int k = 0;
227 int list_count = 0;
1a7cfe02 228 list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *));
0f6bebac 229 hash_first(addr_table);
62e76326 230
0f6bebac 231 while ((n = (netdbEntry *) hash_next(addr_table))) {
1a7cfe02 232 assert(list_count < netdbEntry::UseCount());
62e76326 233 *(list + list_count) = n;
7c64cac7 234 ++list_count;
c907000b 235 }
62e76326 236
c907000b 237 qsort((char *) list,
62e76326 238 list_count,
239 sizeof(netdbEntry *),
240 netdbLRU);
241
7c64cac7 242 for (k = 0; k < list_count; ++k) {
1a7cfe02 243 if (netdbEntry::UseCount() < Config.Netdb.low)
62e76326 244 break;
245
246 netdbRelease(*(list + k));
c907000b 247 }
62e76326 248
c907000b 249 xfree(list);
250}
251
67508012 252static netdbEntry *
b7ac5457 253netdbLookupAddr(const Ip::Address &addr)
87801fcb 254{
587d8445 255 netdbEntry *n;
cc192b50 256 char *key = new char[MAX_IPSTRLEN];
4dd643d5 257 networkFromInaddr(addr).toStr(key,MAX_IPSTRLEN);
587d8445 258 n = (netdbEntry *) hash_lookup(addr_table, key);
4a7799f9 259 delete[] key;
587d8445 260 return n;
87801fcb 261}
262
67508012 263static netdbEntry *
b7ac5457 264netdbAdd(Ip::Address &addr)
87801fcb 265{
266 netdbEntry *n;
62e76326 267
1a7cfe02 268 if (netdbEntry::UseCount() > Config.Netdb.high)
62e76326 269 netdbPurgeLRU();
270
aee3523a 271 if ((n = netdbLookupAddr(addr)) == nullptr) {
1a7cfe02 272 n = new netdbEntry;
62e76326 273 netdbHashInsert(n, addr);
87801fcb 274 }
62e76326 275
67508012 276 return n;
87801fcb 277}
278
279static void
4a3b98d7 280netdbSendPing(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
87801fcb 281{
b7ac5457 282 Ip::Address addr;
aee3523a 283 char *hostname = nullptr;
aa839030 284 static_cast<generic_cbdata *>(data)->unwrap(&hostname);
87801fcb 285 netdbEntry *n;
b19361fd 286 netdbEntry *na;
587d8445 287 net_db_name *x;
288 net_db_name **X;
62e76326 289
aee3523a 290 if (ia == nullptr) {
62e76326 291 xfree(hostname);
292 return;
cb190ed7 293 }
62e76326 294
fd9c47d1 295 addr = ia->current();
62e76326 296
aee3523a 297 if ((n = netdbLookupHost(hostname)) == nullptr) {
62e76326 298 n = netdbAdd(addr);
299 netdbHostInsert(n, hostname);
b19361fd 300 } else if ((na = netdbLookupAddr(addr)) != n) {
62e76326 301 /*
302 *hostname moved from 'network n' to 'network na'!
303 */
304
aee3523a 305 if (na == nullptr)
62e76326 306 na = netdbAdd(addr);
307
bf8fe701 308 debugs(38, 3, "netdbSendPing: " << hostname << " moved from " << n->network << " to " << na->network);
62e76326 309
310 x = (net_db_name *) hash_lookup(host_table, hostname);
311
aee3523a 312 if (x == nullptr) {
d816f28d 313 debugs(38, DBG_IMPORTANT, "ERROR: Squid BUG: net_db_name list bug: " << hostname << " not found");
62e76326 314 xfree(hostname);
315 return;
316 }
317
318 /* remove net_db_name from 'network n' linked list */
319 for (X = &n->hosts; *X; X = &(*X)->next) {
320 if (*X == x) {
321 *X = x->next;
322 break;
323 }
324 }
325
7c64cac7 326 -- n->link_count;
62e76326 327 /* point to 'network na' from host entry */
328 x->net_db_entry = na;
329 /* link net_db_name to 'network na' */
330 x->next = na->hosts;
331 na->hosts = x;
7c64cac7 332 ++ na->link_count;
62e76326 333 n = na;
b19361fd 334 }
62e76326 335
674ac814 336 if (n->next_ping_time <= squid_curtime) {
bf8fe701 337 debugs(38, 3, "netdbSendPing: pinging " << hostname);
cc192b50 338 icmpEngine.DomainPing(addr, hostname);
7c64cac7 339 ++ n->pings_sent;
62e76326 340 n->next_ping_time = squid_curtime + Config.Netdb.period;
341 n->last_use_time = squid_curtime;
674ac814 342 }
62e76326 343
28c60158 344 xfree(hostname);
67508012 345}
346
b7ac5457
AJ
347Ip::Address
348networkFromInaddr(const Ip::Address &in)
67508012 349{
b7ac5457 350 Ip::Address out;
cc192b50 351
352 out = in;
cc192b50 353
354 /* in IPv6 the 'network' should be the routing section. */
4dd643d5
AJ
355 if ( in.isIPv6() ) {
356 out.applyMask(64, AF_INET6);
cc192b50 357 debugs(14, 5, "networkFromInaddr : Masked IPv6 Address to " << in << "/64 routing part.");
358 return out;
359 }
62e76326 360
429fdbec 361#if USE_CLASSFUL
cc192b50 362 struct in_addr b;
363
4dd643d5 364 in.getInAddr(b);
62e76326 365
67508012 366 if (IN_CLASSC(b.s_addr))
62e76326 367 b.s_addr &= IN_CLASSC_NET;
67508012 368 else if (IN_CLASSB(b.s_addr))
62e76326 369 b.s_addr &= IN_CLASSB_NET;
67508012 370 else if (IN_CLASSA(b.s_addr))
62e76326 371 b.s_addr &= IN_CLASSA_NET;
372
cc192b50 373 out = b;
62e76326 374
429fdbec 375#endif
62e76326 376
cc192b50 377 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << out << "/24.");
62e76326 378
cc192b50 379 /* use /24 for everything under IPv4 */
4dd643d5 380 out.applyMask(24, AF_INET);
cc192b50 381 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << in << "/24.");
382
383 return out;
67508012 384}
385
386static int
79d39a72 387sortByRtt(const void *A, const void *B)
67508012 388{
e6ccf245 389 const netdbEntry *const *n1 = (const netdbEntry *const *)A;
390 const netdbEntry *const *n2 = (const netdbEntry *const *)B;
62e76326 391
429fdbec 392 if ((*n1)->rtt > (*n2)->rtt)
62e76326 393 return 1;
429fdbec 394 else if ((*n1)->rtt < (*n2)->rtt)
62e76326 395 return -1;
67508012 396 else
62e76326 397 return 0;
67508012 398}
399
429fdbec 400static net_db_peer *
401netdbPeerByName(const netdbEntry * n, const char *peername)
402{
403 int i;
404 net_db_peer *p = n->peers;
62e76326 405
7c64cac7 406 for (i = 0; i < n->n_peers; ++i, ++p) {
62e76326 407 if (!strcmp(p->peername, peername))
408 return p;
429fdbec 409 }
62e76326 410
aee3523a 411 return nullptr;
429fdbec 412}
413
414static net_db_peer *
a3c6762c 415netdbPeerAdd(netdbEntry * n, CachePeer * e)
429fdbec 416{
417 net_db_peer *p;
418 net_db_peer *o;
419 int osize;
420 int i;
62e76326 421
429fdbec 422 if (n->n_peers == n->n_peers_alloc) {
62e76326 423 o = n->peers;
424 osize = n->n_peers_alloc;
425
426 if (n->n_peers_alloc == 0)
427 n->n_peers_alloc = 2;
428 else
429 n->n_peers_alloc <<= 1;
430
bf8fe701 431 debugs(38, 3, "netdbPeerAdd: Growing peer list for '" << n->network << "' to " << n->n_peers_alloc);
62e76326 432
433 n->peers = (net_db_peer *)xcalloc(n->n_peers_alloc, sizeof(net_db_peer));
434
7c64cac7 435 for (i = 0; i < osize; ++i)
62e76326 436 *(n->peers + i) = *(o + i);
437
438 if (osize) {
439 safe_free(o);
440 }
429fdbec 441 }
62e76326 442
429fdbec 443 p = n->peers + n->n_peers;
444 p->peername = netdbPeerName(e->host);
7c64cac7 445 ++ n->n_peers;
429fdbec 446 return p;
447}
448
449static int
79d39a72 450sortPeerByRtt(const void *A, const void *B)
429fdbec 451{
e6ccf245 452 const net_db_peer *p1 = (net_db_peer *)A;
453 const net_db_peer *p2 = (net_db_peer *)B;
62e76326 454
429fdbec 455 if (p1->rtt > p2->rtt)
62e76326 456 return 1;
429fdbec 457 else if (p1->rtt < p2->rtt)
62e76326 458 return -1;
429fdbec 459 else
62e76326 460 return 0;
429fdbec 461}
462
463static void
8b082ed9 464netdbSaveState(void *)
429fdbec 465{
b7ed6dbb 466 if (strcmp(Config.netdbFilename, "none") == 0)
26ac0430 467 return;
2b753521 468
08e8e020 469 Logfile *lf;
429fdbec 470 netdbEntry *n;
429fdbec 471 net_db_name *x;
62e76326 472
429fdbec 473 struct timeval start = current_time;
474 int count = 0;
111b16cf 475 /*
476 * This was nicer when we were using stdio, but thanks to
477 * Solaris bugs, its a bad idea. fopen can fail if more than
478 * 256 FDs are open.
479 */
e3732f4f 480 /*
481 * unlink() is here because there is currently no way to make
482 * logfileOpen() use O_TRUNC.
483 */
2b753521 484 unlink(Config.netdbFilename);
485 lf = logfileOpen(Config.netdbFilename, 4096, 0);
62e76326 486
16cd62c7 487 if (!lf) {
b69e9ffa
AJ
488 int xerrno = errno;
489 debugs(50, DBG_IMPORTANT, MYNAME << Config.netdbFilename << ": " << xstrerr(xerrno));
62e76326 490 return;
429fdbec 491 }
62e76326 492
0f6bebac 493 hash_first(addr_table);
62e76326 494
0f6bebac 495 while ((n = (netdbEntry *) hash_next(addr_table))) {
62e76326 496 if (n->pings_recv == 0)
497 continue;
498
499 logfilePrintf(lf, "%s %d %d %10.5f %10.5f %d %d",
500 n->network,
501 n->pings_sent,
502 n->pings_recv,
503 n->hops,
504 n->rtt,
505 (int) n->next_ping_time,
506 (int) n->last_use_time);
507
508 for (x = n->hosts; x; x = x->next)
4ec218d5 509 logfilePrintf(lf, " %s", hashKeyStr(x));
62e76326 510
511 logfilePrintf(lf, "\n");
512
7c64cac7 513 ++count;
62e76326 514
111b16cf 515#undef RBUF_SZ
62e76326 516
429fdbec 517 }
62e76326 518
08e8e020 519 logfileClose(lf);
587d8445 520 getCurrentTime();
e0236918 521 debugs(38, DBG_IMPORTANT, "NETDB state saved; " <<
26ac0430
AJ
522 count << " entries, " <<
523 tvSubMsec(start, current_time) << " msec" );
aee3523a 524 eventAddIsh("netdbSaveState", netdbSaveState, nullptr, 3600.0, 1);
429fdbec 525}
526
527static void
528netdbReloadState(void)
529{
b7ed6dbb 530 if (strcmp(Config.netdbFilename, "none") == 0)
26ac0430 531 return;
2b753521 532
111b16cf 533 char *s;
534 int fd;
535 int l;
62e76326 536
111b16cf 537 struct stat sb;
429fdbec 538 netdbEntry *n;
539 netdbEntry N;
62e76326 540
b7ac5457 541 Ip::Address addr;
429fdbec 542 int count = 0;
62e76326 543
429fdbec 544 struct timeval start = current_time;
111b16cf 545 /*
546 * This was nicer when we were using stdio, but thanks to
547 * Solaris bugs, its a bad idea. fopen can fail if more than
548 * 256 FDs are open.
549 */
2b753521 550 fd = file_open(Config.netdbFilename, O_RDONLY | O_BINARY);
62e76326 551
111b16cf 552 if (fd < 0)
62e76326 553 return;
554
053ea9f4 555 if (fstat(fd, &sb) < 0) {
62e76326 556 file_close(fd);
557 return;
053ea9f4 558 }
62e76326 559
e0bbe966 560 char *t;
561 char *buf = (char *)xcalloc(1, sb.st_size + 1);
562 t = buf;
d0ef8ea8 563 l = FD_READ_METHOD(fd, buf, sb.st_size);
111b16cf 564 file_close(fd);
62e76326 565
e0bbe966 566 if (l <= 0) {
62e76326 567 safe_free (buf);
568 return;
e0bbe966 569 };
62e76326 570
111b16cf 571 while ((s = strchr(t, '\n'))) {
62e76326 572 char *q;
573 assert(s - buf < l);
574 *s = '\0';
b56b37cf 575 N = netdbEntry();
62e76326 576 q = strtok(t, w_space);
577 t = s + 1;
578
aee3523a 579 if (nullptr == q)
62e76326 580 continue;
581
cc192b50 582 if (! (addr = q) )
62e76326 583 continue;
584
aee3523a 585 if (netdbLookupAddr(addr) != nullptr) /* no dups! */
62e76326 586 continue;
587
aee3523a 588 if ((q = strtok(nullptr, w_space)) == nullptr)
62e76326 589 continue;
590
591 N.pings_sent = atoi(q);
592
aee3523a 593 if ((q = strtok(nullptr, w_space)) == nullptr)
62e76326 594 continue;
595
596 N.pings_recv = atoi(q);
597
598 if (N.pings_recv == 0)
599 continue;
600
601 /* give this measurement low weight */
602 N.pings_sent = 1;
603
604 N.pings_recv = 1;
605
aee3523a 606 if ((q = strtok(nullptr, w_space)) == nullptr)
62e76326 607 continue;
608
609 N.hops = atof(q);
610
aee3523a 611 if ((q = strtok(nullptr, w_space)) == nullptr)
62e76326 612 continue;
613
614 N.rtt = atof(q);
615
aee3523a 616 if ((q = strtok(nullptr, w_space)) == nullptr)
62e76326 617 continue;
618
619 N.next_ping_time = (time_t) atoi(q);
620
aee3523a 621 if ((q = strtok(nullptr, w_space)) == nullptr)
62e76326 622 continue;
623
624 N.last_use_time = (time_t) atoi(q);
625
1a7cfe02 626 n = new netdbEntry;
62e76326 627
41d00cd3 628 memcpy(n, &N, sizeof(netdbEntry));
62e76326 629
630 netdbHashInsert(n, addr);
631
aee3523a
AR
632 while ((q = strtok(nullptr, w_space)) != nullptr) {
633 if (netdbLookupHost(q) != nullptr) /* no dups! */
62e76326 634 continue;
635
636 netdbHostInsert(n, q);
637 }
638
7c64cac7 639 ++count;
429fdbec 640 }
62e76326 641
111b16cf 642 xfree(buf);
587d8445 643 getCurrentTime();
e0236918 644 debugs(38, DBG_IMPORTANT, "NETDB state reloaded; " <<
26ac0430
AJ
645 count << " entries, " <<
646 tvSubMsec(start, current_time) << " msec" );
429fdbec 647}
648
858783c9 649static const char *
429fdbec 650netdbPeerName(const char *name)
651{
858783c9 652 const wordlist *w;
62e76326 653
429fdbec 654 for (w = peer_names; w; w = w->next) {
62e76326 655 if (!strcmp(w->key, name))
656 return w->key;
429fdbec 657 }
62e76326 658
858783c9 659 return wordlistAdd(&peer_names, name);
429fdbec 660}
661
45c54bdc 662static void
2324cda2 663netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData)
45c54bdc 664{
b7ac5457 665 Ip::Address addr;
cc192b50 666
e6ccf245 667 netdbExchangeState *ex = (netdbExchangeState *)data;
62e76326 668
cc192b50 669 struct in_addr line_addr;
45c54bdc 670 double rtt;
671 double hops;
45c54bdc 672 int j;
70305391 673 int nused = 0;
add2192d 674
122a6e3c 675 size_t rec_sz = 0; // received record size (TODO: make const)
cc192b50 676 rec_sz += 1 + sizeof(struct in_addr);
45c54bdc 677 rec_sz += 1 + sizeof(int);
678 rec_sz += 1 + sizeof(int);
122a6e3c
AR
679 // to make progress without growing buffer space, we must parse at least one record per call
680 Assure(rec_sz <= ex->parsingBuffer.capacity());
2324cda2 681 debugs(38, 3, "netdbExchangeHandleReply: " << receivedData.length << " read bytes");
62e76326 682
891a70f1 683 if (!ex->p.valid()) {
bf8fe701 684 debugs(38, 3, "netdbExchangeHandleReply: Peer became invalid");
fa91d030 685 delete ex;
62e76326 686 return;
45c54bdc 687 }
62e76326 688
a555a85b 689 debugs(38, 3, "for " << *ex->p);
528b2c61 690
122a6e3c 691 if (receivedData.flags.error) {
fa91d030 692 delete ex;
62e76326 693 return;
528b2c61 694 }
62e76326 695
add2192d 696 if (ex->connstate == STATE_HEADER) {
122a6e3c
AR
697 const auto scode = ex->e->mem().baseReply().sline.status();
698 assert(scode != Http::scNone);
699 debugs(38, 3, "reply status " << scode);
700 if (scode != Http::scOkay) {
701 delete ex;
62e76326 702 return;
703 }
122a6e3c 704 ex->connstate = STATE_BODY;
45c54bdc 705 }
62e76326 706
add2192d 707 assert(ex->connstate == STATE_BODY);
708
122a6e3c
AR
709 ex->parsingBuffer.appended(receivedData.data, receivedData.length);
710 auto p = ex->parsingBuffer.c_str(); // current parsing position
711 auto size = ex->parsingBuffer.contentSize(); // bytes we still need to parse
712
add2192d 713 /* If we get here, we have some body to parse .. */
bf8fe701 714 debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size);
62e76326 715
45c54bdc 716 while (size >= rec_sz) {
bf8fe701 717 debugs(38, 5, "netdbExchangeHandleReply: in parsing loop, size = " << size);
4dd643d5 718 addr.setAnyAddr();
62e76326 719 hops = rtt = 0.0;
720
122a6e3c 721 size_t o; // current record parsing offset
62e76326 722 for (o = 0; o < rec_sz;) {
723 switch ((int) *(p + o)) {
724
725 case NETDB_EX_NETWORK:
7c64cac7 726 ++o;
9837567d 727 // XXX: NetDB can still only send IPv4
41d00cd3 728 memcpy(&line_addr, p + o, sizeof(struct in_addr));
cc192b50 729 addr = line_addr;
730 o += sizeof(struct in_addr);
62e76326 731 break;
732
733 case NETDB_EX_RTT:
7c64cac7 734 ++o;
41d00cd3 735 memcpy(&j, p + o, sizeof(int));
62e76326 736 o += sizeof(int);
737 rtt = (double) ntohl(j) / 1000.0;
738 break;
739
740 case NETDB_EX_HOPS:
7c64cac7 741 ++o;
41d00cd3 742 memcpy(&j, p + o, sizeof(int));
62e76326 743 o += sizeof(int);
744 hops = (double) ntohl(j) / 1000.0;
745 break;
746
747 default:
d816f28d 748 debugs(38, DBG_IMPORTANT, "ERROR: netdbExchangeHandleReply: corrupt data, aborting");
fa91d030 749 delete ex;
62e76326 750 return;
751 }
752 }
753
4dd643d5 754 if (!addr.isAnyAddr() && rtt > 0)
891a70f1 755 netdbExchangeUpdatePeer(addr, ex->p.get(), rtt, hops);
62e76326 756
757 assert(o == rec_sz);
758
62e76326 759 size -= rec_sz;
760
761 p += rec_sz;
762
7c64cac7 763 ++nused;
45c54bdc 764 }
add2192d 765
122a6e3c
AR
766 const auto parsedSize = ex->parsingBuffer.contentSize() - size;
767 ex->parsingBuffer.consume(parsedSize);
add2192d 768
bf8fe701 769 debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size << " bytes");
add2192d 770
bf8fe701 771 debugs(38, 3, "netdbExchangeHandleReply: used " << nused <<
772 " entries, (x " << rec_sz << " bytes) == " << nused * rec_sz <<
773 " bytes total");
62e76326 774
b7fe0ab0 775 if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) {
bf8fe701 776 debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED");
fa91d030 777 delete ex;
122a6e3c 778 return;
45c54bdc 779 }
122a6e3c
AR
780
781 if (ex->sc->atEof()) {
782 if (const auto leftoverBytes = ex->parsingBuffer.contentSize())
783 debugs(38, 2, "discarding a partially received record due to Store EOF: " << leftoverBytes);
784 delete ex;
785 return;
786 }
787
788 // TODO: To protect us from a broken peer sending an "infinite" stream of
789 // new addresses, limit the cumulative number of received bytes or records?
790
791 const auto remainingSpace = ex->parsingBuffer.space().positionAt(receivedData.offset + receivedData.length);
792 // rec_sz is at most buffer capacity, and we consume all fully loaded records
793 Assure(remainingSpace.length);
794 storeClientCopy(ex->sc, ex->e, remainingSpace, netdbExchangeHandleReply, ex);
45c54bdc 795}
796
f69d9265
AJ
797#endif /* USE_ICMP */
798
e97f40f4 799/* PUBLIC FUNCTIONS */
800
67508012 801void
e97f40f4 802netdbInit(void)
803{
804#if USE_ICMP
1a7cfe02 805 Mgr::RegisterAction("netdb", "Network Measurement Database", netdbDump, 0, 1);
d120ed12 806
19054954 807 if (addr_table)
62e76326 808 return;
809
1a7cfe02 810 int n = hashPrime(Config.Netdb.high / 4);
62e76326 811
30abd221 812 addr_table = hash_create((HASHCMP *) strcmp, n, hash_string);
62e76326 813
aa9e2cab 814 n = hashPrime(3 * Config.Netdb.high / 4);
62e76326 815
30abd221 816 host_table = hash_create((HASHCMP *) strcmp, n, hash_string);
62e76326 817
aee3523a 818 eventAddIsh("netdbSaveState", netdbSaveState, nullptr, 3600.0, 1);
62e76326 819
429fdbec 820 netdbReloadState();
62e76326 821
62ee09ca 822#endif
823}
824
e97f40f4 825void
0ee4272b 826netdbPingSite(const char *hostname)
67508012 827{
e97f40f4 828#if USE_ICMP
67508012 829 netdbEntry *n;
62e76326 830
aee3523a 831 if ((n = netdbLookupHost(hostname)) != nullptr)
62e76326 832 if (n->next_ping_time > squid_curtime)
833 return;
834
aa839030 835 ipcache_nbgethostbyname(hostname, netdbSendPing,
26ac0430 836 new generic_cbdata(xstrdup(hostname)));
8b082ed9
FC
837#else
838 (void)hostname;
e97f40f4 839#endif
67508012 840}
841
e97f40f4 842void
b7ac5457 843netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
4d311579 844{
e97f40f4 845#if USE_ICMP
846 netdbEntry *n;
847 int N;
cc192b50 848 debugs(38, 3, "netdbHandlePingReply: from " << from);
62e76326 849
aee3523a 850 if ((n = netdbLookupAddr(from)) == nullptr)
62e76326 851 return;
852
429fdbec 853 N = ++n->pings_recv;
62e76326 854
429fdbec 855 if (N > 5)
62e76326 856 N = 5;
857
e6ccf245 858 if (rtt < 1)
62e76326 859 rtt = 1;
860
e97f40f4 861 n->hops = ((n->hops * (N - 1)) + hops) / N;
62e76326 862
e97f40f4 863 n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
62e76326 864
bf8fe701 865 debugs(38, 3, "netdbHandlePingReply: " << n->network << "; rtt="<<
866 std::setw(5)<< std::setprecision(2) << n->rtt << " hops="<<
867 std::setw(4) << n->hops);
8b082ed9
FC
868#else
869 (void)from;
870 (void)hops;
871 (void)rtt;
e97f40f4 872#endif
4d311579 873}
e5f6c5c2 874
e97f40f4 875void
876netdbDump(StoreEntry * sentry)
877{
878#if USE_ICMP
879 netdbEntry *n;
880 netdbEntry **list;
429fdbec 881 net_db_name *x;
e97f40f4 882 int k;
883 int i;
429fdbec 884 int j;
885 net_db_peer *p;
15576b6a 886 storeAppendPrintf(sentry, "Network DB Statistics:\n");
cc192b50 887 storeAppendPrintf(sentry, "%-46.46s %9s %7s %5s %s\n", /* Max between 16 (IPv4) or 46 (IPv6) */
62e76326 888 "Network",
889 "recv/sent",
890 "RTT",
891 "Hops",
892 "Hostnames");
1a7cfe02 893 list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *));
0ee4272b 894 i = 0;
0f6bebac 895 hash_first(addr_table);
62e76326 896
a38ec4b1
FC
897 while ((n = (netdbEntry *) hash_next(addr_table))) {
898 *(list + i) = n;
899 ++i;
900 }
62e76326 901
1a7cfe02 902 if (i != netdbEntry::UseCount())
fa84c01d 903 debugs(38, DBG_CRITICAL, "WARNING: netdb_addrs count off, found " << i <<
1a7cfe02 904 ", expected " << netdbEntry::UseCount());
62e76326 905
0ee4272b 906 qsort((char *) list,
62e76326 907 i,
908 sizeof(netdbEntry *),
909 sortByRtt);
910
7c64cac7 911 for (k = 0; k < i; ++k) {
62e76326 912 n = *(list + k);
cc192b50 913 storeAppendPrintf(sentry, "%-46.46s %4d/%4d %7.1f %5.1f", /* Max between 16 (IPv4) or 46 (IPv6) */
62e76326 914 n->network,
915 n->pings_recv,
916 n->pings_sent,
917 n->rtt,
918 n->hops);
919
920 for (x = n->hosts; x; x = x->next)
4ec218d5 921 storeAppendPrintf(sentry, " %s", hashKeyStr(x));
62e76326 922
923 storeAppendPrintf(sentry, "\n");
924
925 p = n->peers;
926
7c64cac7 927 for (j = 0; j < n->n_peers; ++j, ++p) {
62e76326 928 storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n",
929 p->peername,
930 p->rtt,
931 p->hops);
932 }
0ee4272b 933 }
62e76326 934
0ee4272b 935 xfree(list);
c8391077 936#else
62e76326 937
9b5c4a9a 938 storeAppendPrintf(sentry,"NETDB support not compiled into this Squid cache.\n");
e97f40f4 939#endif
940}
48f44632 941
942int
943netdbHostHops(const char *host)
944{
945#if USE_ICMP
946 netdbEntry *n = netdbLookupHost(host);
62e76326 947
429fdbec 948 if (n) {
62e76326 949 n->last_use_time = squid_curtime;
950 return (int) (n->hops + 0.5);
429fdbec 951 }
8b082ed9
FC
952#else
953 (void)host;
48f44632 954#endif
429fdbec 955 return 0;
48f44632 956}
957
958int
959netdbHostRtt(const char *host)
960{
961#if USE_ICMP
962 netdbEntry *n = netdbLookupHost(host);
62e76326 963
429fdbec 964 if (n) {
62e76326 965 n->last_use_time = squid_curtime;
966 return (int) (n->rtt + 0.5);
429fdbec 967 }
62e76326 968
8b082ed9
FC
969#else
970 (void)host;
429fdbec 971#endif
972 return 0;
973}
974
1d6ae62d 975void
976netdbHostData(const char *host, int *samp, int *rtt, int *hops)
977{
978#if USE_ICMP
5942e8d4 979 netdbEntry *n = netdbLookupHost(host);
62e76326 980
aee3523a 981 if (n == nullptr)
62e76326 982 return;
983
5942e8d4 984 *samp = n->pings_recv;
62e76326 985
5942e8d4 986 *rtt = (int) (n->rtt + 0.5);
62e76326 987
5942e8d4 988 *hops = (int) (n->hops + 0.5);
62e76326 989
4ed6ef62 990 n->last_use_time = squid_curtime;
62e76326 991
8b082ed9
FC
992#else
993 (void)hops;
994 (void)rtt;
995 (void)samp;
996 (void)host;
1d6ae62d 997#endif
998}
999
429fdbec 1000void
c8ab5ec6 1001netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
429fdbec 1002{
1003#if USE_ICMP
1004 netdbEntry *n;
1005 double rtt = (double) irtt;
1006 double hops = (double) ihops;
1007 net_db_peer *p;
5c51bffb
AJ
1008 debugs(38, 3, url.host() << ", " << ihops << " hops, " << irtt << " rtt");
1009 n = netdbLookupHost(url.host());
62e76326 1010
aee3523a 1011 if (n == nullptr) {
5c51bffb 1012 debugs(38, 3, "host " << url.host() << " not found");
62e76326 1013 return;
429fdbec 1014 }
62e76326 1015
aee3523a 1016 if ((p = netdbPeerByName(n, e->host)) == nullptr)
62e76326 1017 p = netdbPeerAdd(n, e);
1018
429fdbec 1019 p->rtt = rtt;
62e76326 1020
429fdbec 1021 p->hops = hops;
62e76326 1022
429fdbec 1023 p->expires = squid_curtime + 3600;
62e76326 1024
429fdbec 1025 if (n->n_peers < 2)
62e76326 1026 return;
1027
429fdbec 1028 qsort((char *) n->peers,
62e76326 1029 n->n_peers,
1030 sizeof(net_db_peer),
1031 sortPeerByRtt);
1032
8b082ed9
FC
1033#else
1034 (void)ihops;
1035 (void)irtt;
1036 (void)e;
1037 (void)url;
48f44632 1038#endif
48f44632 1039}
ce75f381 1040
27efd484 1041void
a3c6762c 1042netdbExchangeUpdatePeer(Ip::Address &addr, CachePeer * e, double rtt, double hops)
27efd484 1043{
1044#if USE_ICMP
1045 netdbEntry *n;
1046 net_db_peer *p;
cc192b50 1047 debugs(38, 5, "netdbExchangeUpdatePeer: '" << addr << "', "<<
bf8fe701 1048 std::setfill('0')<< std::setprecision(2) << hops << " hops, " <<
1049 rtt << " rtt");
1050
4dd643d5 1051 if ( !addr.isIPv4() ) {
cc192b50 1052 debugs(38, 5, "netdbExchangeUpdatePeer: Aborting peer update for '" << addr << "', NetDB cannot handle IPv6.");
1053 return;
1054 }
1055
27efd484 1056 n = netdbLookupAddr(addr);
62e76326 1057
aee3523a 1058 if (n == nullptr)
62e76326 1059 n = netdbAdd(addr);
1060
aee3523a 1061 assert(nullptr != n);
62e76326 1062
aee3523a 1063 if ((p = netdbPeerByName(n, e->host)) == nullptr)
62e76326 1064 p = netdbPeerAdd(n, e);
1065
27efd484 1066 p->rtt = rtt;
62e76326 1067
27efd484 1068 p->hops = hops;
62e76326 1069
f53969cc 1070 p->expires = squid_curtime + 3600; /* XXX ? */
62e76326 1071
27efd484 1072 if (n->n_peers < 2)
62e76326 1073 return;
1074
27efd484 1075 qsort((char *) n->peers,
62e76326 1076 n->n_peers,
1077 sizeof(net_db_peer),
1078 sortPeerByRtt);
1079
8b082ed9
FC
1080#else
1081 (void)hops;
1082 (void)rtt;
1083 (void)e;
1084 (void)addr;
27efd484 1085#endif
1086}
1087
587d8445 1088void
b7ac5457 1089netdbDeleteAddrNetwork(Ip::Address &addr)
587d8445 1090{
1091#if USE_ICMP
1092 netdbEntry *n = netdbLookupAddr(addr);
62e76326 1093
aee3523a 1094 if (n == nullptr)
62e76326 1095 return;
1096
bf8fe701 1097 debugs(38, 3, "netdbDeleteAddrNetwork: " << n->network);
62e76326 1098
587d8445 1099 netdbRelease(n);
8b082ed9
FC
1100#else
1101 (void)addr;
587d8445 1102#endif
1103}
de2a0782 1104
1105void
1106netdbBinaryExchange(StoreEntry * s)
1107{
06a5ae20 1108 HttpReply *reply = new HttpReply;
9ad1cbca 1109#if USE_ICMP
62e76326 1110
b7ac5457 1111 Ip::Address addr;
cc192b50 1112
de2a0782 1113 netdbEntry *n;
de2a0782 1114 int i;
1115 int j;
1116 int rec_sz;
1117 char *buf;
62e76326 1118
cc192b50 1119 struct in_addr line_addr;
3900307b 1120 s->buffer();
aee3523a 1121 reply->setHeaders(Http::scOkay, "OK", nullptr, -1, squid_curtime, -2);
db237875 1122 s->replaceHttpReply(reply);
de2a0782 1123 rec_sz = 0;
cc192b50 1124 rec_sz += 1 + sizeof(struct in_addr);
de2a0782 1125 rec_sz += 1 + sizeof(int);
1126 rec_sz += 1 + sizeof(int);
e6ccf245 1127 buf = (char *)memAllocate(MEM_4K_BUF);
de2a0782 1128 i = 0;
0f6bebac 1129 hash_first(addr_table);
62e76326 1130
0f6bebac 1131 while ((n = (netdbEntry *) hash_next(addr_table))) {
62e76326 1132 if (0.0 == n->rtt)
1133 continue;
1134
f53969cc 1135 if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */
62e76326 1136 continue;
1137
cc192b50 1138 if (! (addr = n->network) )
1139 continue;
1140
9837567d 1141 // XXX: NetDB cannot yet handle IPv6 addresses
4dd643d5 1142 if ( !addr.isIPv4() )
62e76326 1143 continue;
1144
a38ec4b1
FC
1145 buf[i] = (char) NETDB_EX_NETWORK;
1146 ++i;
62e76326 1147
4dd643d5 1148 addr.getInAddr(line_addr);
41d00cd3 1149 memcpy(&buf[i], &line_addr, sizeof(struct in_addr));
62e76326 1150
cc192b50 1151 i += sizeof(struct in_addr);
62e76326 1152
a38ec4b1
FC
1153 buf[i] = (char) NETDB_EX_RTT;
1154 ++i;
62e76326 1155
1156 j = htonl((int) (n->rtt * 1000));
1157
41d00cd3 1158 memcpy(&buf[i], &j, sizeof(int));
62e76326 1159
1160 i += sizeof(int);
1161
a38ec4b1
FC
1162 buf[i] = (char) NETDB_EX_HOPS;
1163 ++i;
62e76326 1164
1165 j = htonl((int) (n->hops * 1000));
1166
41d00cd3 1167 memcpy(&buf[i], &j, sizeof(int));
62e76326 1168
1169 i += sizeof(int);
1170
1171 if (i + rec_sz > 4096) {
3900307b 1172 s->append(buf, i);
62e76326 1173 i = 0;
1174 }
de2a0782 1175 }
62e76326 1176
84075e8f 1177 if (i > 0) {
3900307b 1178 s->append(buf, i);
62e76326 1179 i = 0;
84075e8f 1180 }
62e76326 1181
de2a0782 1182 assert(0 == i);
3900307b 1183 s->flush();
db1cd23c 1184 memFree(buf, MEM_4K_BUF);
de2a0782 1185#else
62e76326 1186
a1b1756c 1187 reply->setHeaders(Http::scBadRequest, "Bad Request", nullptr, -1, squid_curtime, -2);
db237875 1188 s->replaceHttpReply(reply);
de2a0782 1189 storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
1190#endif
62e76326 1191
528b2c61 1192 s->complete();
9ad1cbca 1193}
1194
1195void
1196netdbExchangeStart(void *data)
1197{
45c54bdc 1198#if USE_ICMP
a3c6762c 1199 CachePeer *p = (CachePeer *)data;
51b5dcf5 1200 static const SBuf netDB("netdb");
c002f0e7 1201 char *uri = internalRemoteUri(p->secure.encryptTransport, p->host, p->http_port, "/squid-internal-dynamic/", netDB);
ad049d4d 1202 debugs(38, 3, "Requesting '" << uri << "'");
ad05b958 1203 const auto mx = MasterXaction::MakePortless<XactionInitiator::initIcmp>();
6c880a16 1204 HttpRequestPointer req(HttpRequest::FromUrlXXX(uri, mx));
62e76326 1205
891a70f1 1206 if (!req) {
d816f28d 1207 debugs(38, DBG_IMPORTANT, "ERROR: " << MYNAME << ": Bad URI " << uri);
62e76326 1208 return;
a74c5601 1209 }
62e76326 1210
fa91d030 1211 netdbExchangeState *ex = new netdbExchangeState(p, req);
c2a7cefd 1212 ex->e = storeCreateEntry(uri, uri, RequestFlags(), Http::METHOD_GET);
aee3523a 1213 assert(nullptr != ex->e);
fa91d030 1214
fa91d030 1215 ex->sc = storeClientListAdd(ex->e, ex);
122a6e3c 1216 storeClientCopy(ex->sc, ex->e, ex->parsingBuffer.makeInitialSpace(), netdbExchangeHandleReply, ex);
fa91d030 1217
f53969cc 1218 ex->r->flags.loopDetected = true; /* cheat! -- force direct */
62e76326 1219
92d6986d 1220 // XXX: send as Proxy-Authenticate instead
9bc73deb 1221 if (p->login)
92d6986d 1222 ex->r->url.userInfo(SBuf(p->login));
62e76326 1223
891a70f1 1224 FwdState::fwdStart(Comm::ConnectionPointer(), ex->e, ex->r.getRaw());
8b082ed9
FC
1225#else
1226 (void)data;
45c54bdc 1227#endif
de2a0782 1228}
69c95dd3 1229
a555a85b
AR
1230#if USE_ICMP
1231/// a netdbClosestParent() helper to find the first usable parent CachePeer
1232/// responsible for the given hostname
1233static CachePeer *
1234findUsableParentAtHostname(PeerSelector *ps, const char * const hostname, const HttpRequest &request)
1235{
1236 for (auto p = Config.peers; p; p = p->next) {
1237 // Both fields should be lowercase, but no code ensures that invariant.
1238 // TODO: net_db_peer should point to CachePeer instead of its hostname!
1239 if (strcasecmp(p->host, hostname) != 0)
1240 continue;
1241
1242 if (neighborType(p, request.url) != PEER_PARENT)
1243 continue;
1244
1245 if (!peerHTTPOkay(p, ps))
1246 continue;
1247
1248 return p;
1249 }
1250
1251 return nullptr;
1252}
1253#endif
1254
a3c6762c 1255CachePeer *
cb365059 1256netdbClosestParent(PeerSelector *ps)
69c95dd3 1257{
69c95dd3 1258#if USE_ICMP
cb365059
EB
1259 assert(ps);
1260 HttpRequest *request = ps->request;
1261
69c95dd3 1262 netdbEntry *n;
1263 const ipcache_addrs *ia;
1264 net_db_peer *h;
1265 int i;
5c51bffb 1266 n = netdbLookupHost(request->url.host());
62e76326 1267
aee3523a 1268 if (nullptr == n) {
62e76326 1269 /* try IP addr */
5c51bffb 1270 ia = ipcache_gethostbyname(request->url.host(), 0);
62e76326 1271
aee3523a 1272 if (nullptr != ia)
fd9c47d1 1273 n = netdbLookupAddr(ia->current());
69c95dd3 1274 }
62e76326 1275
aee3523a
AR
1276 if (nullptr == n)
1277 return nullptr;
62e76326 1278
69c95dd3 1279 if (0 == n->n_peers)
aee3523a 1280 return nullptr;
62e76326 1281
4ed6ef62 1282 n->last_use_time = squid_curtime;
62e76326 1283
1284 /*
69c95dd3 1285 * Find the parent with the least RTT to the origin server.
1286 * Make sure we don't return a parent who is farther away than
1287 * we are. Note, the n->peers list is pre-sorted by RTT.
1288 */
7c64cac7 1289 for (i = 0; i < n->n_peers; ++i) {
62e76326 1290 h = &n->peers[i];
1291
1292 if (n->rtt > 0)
1293 if (n->rtt < h->rtt)
1294 break;
1295
a555a85b
AR
1296 if (const auto p = findUsableParentAtHostname(ps, h->peername, *request))
1297 return p;
69c95dd3 1298 }
62e76326 1299
8b082ed9
FC
1300#else
1301 (void)ps;
69c95dd3 1302#endif
aee3523a 1303 return nullptr;
69c95dd3 1304}
f53969cc 1305