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