2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 38 Network Measurement Database */
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.
20 #include "CachePeer.h"
21 #include "CachePeers.h"
27 #include "HttpReply.h"
28 #include "icmp/net_db.h"
30 #include "ip/Address.h"
32 #include "MemObject.h"
33 #include "mgr/Registration.h"
34 #include "mime_header.h"
35 #include "neighbors.h"
36 #include "PeerSelectState.h"
37 #include "sbuf/SBuf.h"
38 #include "SquidConfig.h"
40 #include "StoreClient.h"
49 #include "icmp/IcmpSquid.h"
51 #include "StoreClient.h"
59 class netdbExchangeState
61 CBDATA_CLASS(netdbExchangeState
);
64 netdbExchangeState(CachePeer
*aPeer
, const HttpRequestPointer
&theReq
) :
69 // TODO: check if we actually need to do this. should be implicit
70 r
->http_ver
= Http::ProtocolVersion();
73 ~netdbExchangeState() {
74 debugs(38, 3, e
->url());
75 storeUnregister(sc
, e
, this);
76 e
->unlock("netdbExchangeDone");
79 CbcPointer
<CachePeer
> p
;
80 StoreEntry
*e
= nullptr;
81 store_client
*sc
= nullptr;
84 /// for receiving a NetDB reply body from Store and interpreting it
85 Store::ParsingBuffer parsingBuffer
;
87 netdb_conn_state_t connstate
= STATE_HEADER
;
90 CBDATA_CLASS_INIT(netdbExchangeState
);
92 static hash_table
*addr_table
= nullptr;
93 static hash_table
*host_table
= nullptr;
95 static Ip::Address
networkFromInaddr(const Ip::Address
&a
);
96 static void netdbRelease(netdbEntry
* n
);
98 static void netdbHashInsert(netdbEntry
* n
, Ip::Address
&addr
);
99 static void netdbHashDelete(const char *key
);
100 static void netdbHostInsert(netdbEntry
* n
, const char *hostname
);
101 static void netdbHostDelete(const net_db_name
* x
);
102 static void netdbPurgeLRU(void);
103 static netdbEntry
*netdbLookupHost(const char *key
);
104 static net_db_peer
*netdbPeerByName(const netdbEntry
* n
, const char *);
105 static net_db_peer
*netdbPeerAdd(netdbEntry
* n
, CachePeer
* e
);
106 static const char *netdbPeerName(const char *name
);
107 static IPH netdbSendPing
;
108 static STCB netdbExchangeHandleReply
;
110 /* We have to keep a local list of CachePeer names. The Peers structure
111 * gets freed during a reconfigure. We want this database to
112 * remain persisitent, so _net_db_peer->peername points into this
114 static wordlist
*peer_names
= nullptr;
117 netdbHashInsert(netdbEntry
* n
, Ip::Address
&addr
)
119 networkFromInaddr(addr
).toStr(n
->network
, MAX_IPSTRLEN
);
121 assert(hash_lookup(addr_table
, n
->network
) == nullptr);
122 hash_join(addr_table
, n
);
126 netdbHashDelete(const char *key
)
128 hash_link
*hptr
= (hash_link
*)hash_lookup(addr_table
, key
);
130 if (hptr
== nullptr) {
131 debug_trap("netdbHashDelete: key not found");
135 hash_remove_link(addr_table
, hptr
);
138 net_db_name::net_db_name(const char *hostname
, netdbEntry
*e
) :
139 next(e
? e
->hosts
: nullptr),
142 key
= xstrdup(hostname
);
150 netdbHostInsert(netdbEntry
* n
, const char *hostname
)
152 net_db_name
*x
= new net_db_name(hostname
, n
);
153 assert(hash_lookup(host_table
, hostname
) == nullptr);
154 hash_join(host_table
, x
);
158 netdbHostDelete(const net_db_name
* x
)
160 assert(x
!= nullptr);
161 assert(x
->net_db_entry
!= nullptr);
163 netdbEntry
*n
= x
->net_db_entry
;
166 for (auto **X
= &n
->hosts
; *X
; X
= &(*X
)->next
) {
173 hash_remove_link(host_table
, (hash_link
*) x
);
178 netdbLookupHost(const char *key
)
180 net_db_name
*x
= (net_db_name
*) hash_lookup(host_table
, key
);
181 return x
? x
->net_db_entry
: nullptr;
185 netdbRelease(netdbEntry
* n
)
190 for (x
= n
->hosts
; x
; x
= next
) {
199 n
->n_peers_alloc
= 0;
201 if (n
->link_count
== 0) {
202 netdbHashDelete(n
->network
);
208 netdbLRU(const void *A
, const void *B
)
210 const netdbEntry
*const *n1
= (const netdbEntry
*const *)A
;
211 const netdbEntry
*const *n2
= (const netdbEntry
*const *)B
;
213 if ((*n1
)->last_use_time
> (*n2
)->last_use_time
)
216 if ((*n1
)->last_use_time
< (*n2
)->last_use_time
)
229 list
= (netdbEntry
**)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry
*));
230 hash_first(addr_table
);
232 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
233 assert(list_count
< netdbEntry::UseCount());
234 *(list
+ list_count
) = n
;
240 sizeof(netdbEntry
*),
243 for (k
= 0; k
< list_count
; ++k
) {
244 if (netdbEntry::UseCount() < Config
.Netdb
.low
)
247 netdbRelease(*(list
+ k
));
254 netdbLookupAddr(const Ip::Address
&addr
)
257 char *key
= new char[MAX_IPSTRLEN
];
258 networkFromInaddr(addr
).toStr(key
,MAX_IPSTRLEN
);
259 n
= (netdbEntry
*) hash_lookup(addr_table
, key
);
265 netdbAdd(Ip::Address
&addr
)
269 if (netdbEntry::UseCount() > Config
.Netdb
.high
)
272 if ((n
= netdbLookupAddr(addr
)) == nullptr) {
274 netdbHashInsert(n
, addr
);
281 netdbSendPing(const ipcache_addrs
*ia
, const Dns::LookupDetails
&, void *data
)
284 char *hostname
= nullptr;
285 static_cast<generic_cbdata
*>(data
)->unwrap(&hostname
);
296 addr
= ia
->current();
298 if ((n
= netdbLookupHost(hostname
)) == nullptr) {
300 netdbHostInsert(n
, hostname
);
301 } else if ((na
= netdbLookupAddr(addr
)) != n
) {
303 *hostname moved from 'network n' to 'network na'!
309 debugs(38, 3, "netdbSendPing: " << hostname
<< " moved from " << n
->network
<< " to " << na
->network
);
311 x
= (net_db_name
*) hash_lookup(host_table
, hostname
);
314 debugs(38, DBG_IMPORTANT
, "ERROR: Squid BUG: net_db_name list bug: " << hostname
<< " not found");
319 /* remove net_db_name from 'network n' linked list */
320 for (X
= &n
->hosts
; *X
; X
= &(*X
)->next
) {
328 /* point to 'network na' from host entry */
329 x
->net_db_entry
= na
;
330 /* link net_db_name to 'network na' */
337 if (n
->next_ping_time
<= squid_curtime
) {
338 debugs(38, 3, "netdbSendPing: pinging " << hostname
);
339 icmpEngine
.DomainPing(addr
, hostname
);
341 n
->next_ping_time
= squid_curtime
+ Config
.Netdb
.period
;
342 n
->last_use_time
= squid_curtime
;
349 networkFromInaddr(const Ip::Address
&in
)
355 /* in IPv6 the 'network' should be the routing section. */
357 out
.applyMask(64, AF_INET6
);
358 debugs(14, 5, "networkFromInaddr : Masked IPv6 Address to " << in
<< "/64 routing part.");
362 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << out
<< "/24.");
364 /* use /24 for everything under IPv4 */
365 out
.applyMask(24, AF_INET
);
366 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << in
<< "/24.");
372 sortByRtt(const void *A
, const void *B
)
374 const netdbEntry
*const *n1
= (const netdbEntry
*const *)A
;
375 const netdbEntry
*const *n2
= (const netdbEntry
*const *)B
;
377 if ((*n1
)->rtt
> (*n2
)->rtt
)
379 else if ((*n1
)->rtt
< (*n2
)->rtt
)
386 netdbPeerByName(const netdbEntry
* n
, const char *peername
)
389 net_db_peer
*p
= n
->peers
;
391 for (i
= 0; i
< n
->n_peers
; ++i
, ++p
) {
392 if (!strcmp(p
->peername
, peername
))
400 netdbPeerAdd(netdbEntry
* n
, CachePeer
* e
)
407 if (n
->n_peers
== n
->n_peers_alloc
) {
409 osize
= n
->n_peers_alloc
;
411 if (n
->n_peers_alloc
== 0)
412 n
->n_peers_alloc
= 2;
414 n
->n_peers_alloc
<<= 1;
416 debugs(38, 3, "netdbPeerAdd: Growing peer list for '" << n
->network
<< "' to " << n
->n_peers_alloc
);
418 n
->peers
= (net_db_peer
*)xcalloc(n
->n_peers_alloc
, sizeof(net_db_peer
));
420 for (i
= 0; i
< osize
; ++i
)
421 *(n
->peers
+ i
) = *(o
+ i
);
428 p
= n
->peers
+ n
->n_peers
;
429 p
->peername
= netdbPeerName(e
->host
);
435 sortPeerByRtt(const void *A
, const void *B
)
437 const net_db_peer
*p1
= (net_db_peer
*)A
;
438 const net_db_peer
*p2
= (net_db_peer
*)B
;
440 if (p1
->rtt
> p2
->rtt
)
442 else if (p1
->rtt
< p2
->rtt
)
449 netdbSaveState(void *)
451 if (strcmp(Config
.netdbFilename
, "none") == 0)
458 struct timeval start
= current_time
;
461 * This was nicer when we were using stdio, but thanks to
462 * Solaris bugs, its a bad idea. fopen can fail if more than
466 * unlink() is here because there is currently no way to make
467 * logfileOpen() use O_TRUNC.
469 unlink(Config
.netdbFilename
);
470 lf
= logfileOpen(Config
.netdbFilename
, 4096, 0);
474 debugs(50, DBG_IMPORTANT
, MYNAME
<< Config
.netdbFilename
<< ": " << xstrerr(xerrno
));
478 hash_first(addr_table
);
480 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
481 if (n
->pings_recv
== 0)
484 logfilePrintf(lf
, "%s %d %d %10.5f %10.5f %d %d",
490 (int) n
->next_ping_time
,
491 (int) n
->last_use_time
);
493 for (x
= n
->hosts
; x
; x
= x
->next
)
494 logfilePrintf(lf
, " %s", hashKeyStr(x
));
496 logfilePrintf(lf
, "\n");
506 debugs(38, DBG_IMPORTANT
, "NETDB state saved; " <<
507 count
<< " entries, " <<
508 tvSubMsec(start
, current_time
) << " msec" );
509 eventAddIsh("netdbSaveState", netdbSaveState
, nullptr, 3600.0, 1);
513 netdbReloadState(void)
515 if (strcmp(Config
.netdbFilename
, "none") == 0)
529 struct timeval start
= current_time
;
531 * This was nicer when we were using stdio, but thanks to
532 * Solaris bugs, its a bad idea. fopen can fail if more than
535 fd
= file_open(Config
.netdbFilename
, O_RDONLY
| O_BINARY
);
540 if (fstat(fd
, &sb
) < 0) {
546 char *buf
= (char *)xcalloc(1, sb
.st_size
+ 1);
548 l
= FD_READ_METHOD(fd
, buf
, sb
.st_size
);
556 while ((s
= strchr(t
, '\n'))) {
561 q
= strtok(t
, w_space
);
570 if (netdbLookupAddr(addr
) != nullptr) /* no dups! */
573 if ((q
= strtok(nullptr, w_space
)) == nullptr)
576 N
.pings_sent
= atoi(q
);
578 if ((q
= strtok(nullptr, w_space
)) == nullptr)
581 N
.pings_recv
= atoi(q
);
583 if (N
.pings_recv
== 0)
586 /* give this measurement low weight */
591 if ((q
= strtok(nullptr, w_space
)) == nullptr)
596 if ((q
= strtok(nullptr, w_space
)) == nullptr)
601 if ((q
= strtok(nullptr, w_space
)) == nullptr)
604 N
.next_ping_time
= (time_t) atoi(q
);
606 if ((q
= strtok(nullptr, w_space
)) == nullptr)
609 N
.last_use_time
= (time_t) atoi(q
);
613 memcpy(n
, &N
, sizeof(netdbEntry
));
615 netdbHashInsert(n
, addr
);
617 while ((q
= strtok(nullptr, w_space
)) != nullptr) {
618 if (netdbLookupHost(q
) != nullptr) /* no dups! */
621 netdbHostInsert(n
, q
);
629 debugs(38, DBG_IMPORTANT
, "NETDB state reloaded; " <<
630 count
<< " entries, " <<
631 tvSubMsec(start
, current_time
) << " msec" );
635 netdbPeerName(const char *name
)
639 for (w
= peer_names
; w
; w
= w
->next
) {
640 if (!strcmp(w
->key
, name
))
644 return wordlistAdd(&peer_names
, name
);
648 netdbExchangeHandleReply(void *data
, StoreIOBuffer receivedData
)
652 netdbExchangeState
*ex
= (netdbExchangeState
*)data
;
654 struct in_addr line_addr
;
660 size_t rec_sz
= 0; // received record size (TODO: make const)
661 rec_sz
+= 1 + sizeof(struct in_addr
);
662 rec_sz
+= 1 + sizeof(int);
663 rec_sz
+= 1 + sizeof(int);
664 // to make progress without growing buffer space, we must parse at least one record per call
665 Assure(rec_sz
<= ex
->parsingBuffer
.capacity());
666 debugs(38, 3, "netdbExchangeHandleReply: " << receivedData
.length
<< " read bytes");
668 if (!ex
->p
.valid()) {
669 debugs(38, 3, "netdbExchangeHandleReply: Peer became invalid");
674 debugs(38, 3, "for " << *ex
->p
);
676 if (receivedData
.flags
.error
) {
681 if (ex
->connstate
== STATE_HEADER
) {
682 const auto scode
= ex
->e
->mem().baseReply().sline
.status();
683 assert(scode
!= Http::scNone
);
684 debugs(38, 3, "reply status " << scode
);
685 if (scode
!= Http::scOkay
) {
689 ex
->connstate
= STATE_BODY
;
692 assert(ex
->connstate
== STATE_BODY
);
694 ex
->parsingBuffer
.appended(receivedData
.data
, receivedData
.length
);
695 auto p
= ex
->parsingBuffer
.c_str(); // current parsing position
696 auto size
= ex
->parsingBuffer
.contentSize(); // bytes we still need to parse
698 /* If we get here, we have some body to parse .. */
699 debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size
);
701 while (size
>= rec_sz
) {
702 debugs(38, 5, "netdbExchangeHandleReply: in parsing loop, size = " << size
);
706 size_t o
; // current record parsing offset
707 for (o
= 0; o
< rec_sz
;) {
708 switch ((int) *(p
+ o
)) {
710 case NETDB_EX_NETWORK
:
712 // XXX: NetDB can still only send IPv4
713 memcpy(&line_addr
, p
+ o
, sizeof(struct in_addr
));
715 o
+= sizeof(struct in_addr
);
720 memcpy(&j
, p
+ o
, sizeof(int));
722 rtt
= (double) ntohl(j
) / 1000.0;
727 memcpy(&j
, p
+ o
, sizeof(int));
729 hops
= (double) ntohl(j
) / 1000.0;
733 debugs(38, DBG_IMPORTANT
, "ERROR: netdbExchangeHandleReply: corrupt data, aborting");
739 if (!addr
.isAnyAddr() && rtt
> 0)
740 netdbExchangeUpdatePeer(addr
, ex
->p
.get(), rtt
, hops
);
751 const auto parsedSize
= ex
->parsingBuffer
.contentSize() - size
;
752 ex
->parsingBuffer
.consume(parsedSize
);
754 debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size
<< " bytes");
756 debugs(38, 3, "netdbExchangeHandleReply: used " << nused
<<
757 " entries, (x " << rec_sz
<< " bytes) == " << nused
* rec_sz
<<
760 if (EBIT_TEST(ex
->e
->flags
, ENTRY_ABORTED
)) {
761 debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED");
766 if (ex
->sc
->atEof()) {
767 if (const auto leftoverBytes
= ex
->parsingBuffer
.contentSize())
768 debugs(38, 2, "discarding a partially received record due to Store EOF: " << leftoverBytes
);
773 // TODO: To protect us from a broken peer sending an "infinite" stream of
774 // new addresses, limit the cumulative number of received bytes or records?
776 const auto remainingSpace
= ex
->parsingBuffer
.space().positionAt(receivedData
.offset
+ receivedData
.length
);
777 // rec_sz is at most buffer capacity, and we consume all fully loaded records
778 Assure(remainingSpace
.length
);
779 storeClientCopy(ex
->sc
, ex
->e
, remainingSpace
, netdbExchangeHandleReply
, ex
);
782 #endif /* USE_ICMP */
784 /* PUBLIC FUNCTIONS */
790 Mgr::RegisterAction("netdb", "Network Measurement Database", netdbDump
, 0, 1);
795 int n
= hashPrime(Config
.Netdb
.high
/ 4);
797 addr_table
= hash_create((HASHCMP
*) strcmp
, n
, hash_string
);
799 n
= hashPrime(3 * Config
.Netdb
.high
/ 4);
801 host_table
= hash_create((HASHCMP
*) strcmp
, n
, hash_string
);
803 eventAddIsh("netdbSaveState", netdbSaveState
, nullptr, 3600.0, 1);
811 netdbPingSite(const char *hostname
)
816 if ((n
= netdbLookupHost(hostname
)) != nullptr)
817 if (n
->next_ping_time
> squid_curtime
)
820 ipcache_nbgethostbyname(hostname
, netdbSendPing
,
821 new generic_cbdata(xstrdup(hostname
)));
828 netdbHandlePingReply(const Ip::Address
&from
, int hops
, int rtt
)
833 debugs(38, 3, "netdbHandlePingReply: from " << from
);
835 if ((n
= netdbLookupAddr(from
)) == nullptr)
846 n
->hops
= ((n
->hops
* (N
- 1)) + hops
) / N
;
848 n
->rtt
= ((n
->rtt
* (N
- 1)) + rtt
) / N
;
850 debugs(38, 3, "netdbHandlePingReply: " << n
->network
<< "; rtt="<<
851 std::setw(5)<< std::setprecision(2) << n
->rtt
<< " hops="<<
852 std::setw(4) << n
->hops
);
861 netdbDump(StoreEntry
* sentry
)
871 storeAppendPrintf(sentry
, "Network DB Statistics:\n");
872 storeAppendPrintf(sentry
, "%-46.46s %9s %7s %5s %s\n", /* Max between 16 (IPv4) or 46 (IPv6) */
878 list
= (netdbEntry
**)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry
*));
880 hash_first(addr_table
);
882 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
887 if (i
!= netdbEntry::UseCount())
888 debugs(38, DBG_CRITICAL
, "WARNING: netdb_addrs count off, found " << i
<<
889 ", expected " << netdbEntry::UseCount());
893 sizeof(netdbEntry
*),
896 for (k
= 0; k
< i
; ++k
) {
898 storeAppendPrintf(sentry
, "%-46.46s %4d/%4d %7.1f %5.1f", /* Max between 16 (IPv4) or 46 (IPv6) */
905 for (x
= n
->hosts
; x
; x
= x
->next
)
906 storeAppendPrintf(sentry
, " %s", hashKeyStr(x
));
908 storeAppendPrintf(sentry
, "\n");
912 for (j
= 0; j
< n
->n_peers
; ++j
, ++p
) {
913 storeAppendPrintf(sentry
, " %-22.22s %7.1f %5.1f\n",
923 storeAppendPrintf(sentry
,"NETDB support not compiled into this Squid cache.\n");
928 netdbHostHops(const char *host
)
931 netdbEntry
*n
= netdbLookupHost(host
);
934 n
->last_use_time
= squid_curtime
;
935 return (int) (n
->hops
+ 0.5);
944 netdbHostRtt(const char *host
)
947 netdbEntry
*n
= netdbLookupHost(host
);
950 n
->last_use_time
= squid_curtime
;
951 return (int) (n
->rtt
+ 0.5);
961 netdbHostData(const char *host
, int *samp
, int *rtt
, int *hops
)
964 netdbEntry
*n
= netdbLookupHost(host
);
969 *samp
= n
->pings_recv
;
971 *rtt
= (int) (n
->rtt
+ 0.5);
973 *hops
= (int) (n
->hops
+ 0.5);
975 n
->last_use_time
= squid_curtime
;
986 netdbUpdatePeer(const AnyP::Uri
&url
, CachePeer
*e
, int irtt
, int ihops
)
990 double rtt
= (double) irtt
;
991 double hops
= (double) ihops
;
993 debugs(38, 3, url
.host() << ", " << ihops
<< " hops, " << irtt
<< " rtt");
994 n
= netdbLookupHost(url
.host());
997 debugs(38, 3, "host " << url
.host() << " not found");
1001 if ((p
= netdbPeerByName(n
, e
->host
)) == nullptr)
1002 p
= netdbPeerAdd(n
, e
);
1008 p
->expires
= squid_curtime
+ 3600;
1013 qsort((char *) n
->peers
,
1015 sizeof(net_db_peer
),
1027 netdbExchangeUpdatePeer(Ip::Address
&addr
, CachePeer
* e
, double rtt
, double hops
)
1032 debugs(38, 5, "netdbExchangeUpdatePeer: '" << addr
<< "', "<<
1033 std::setfill('0')<< std::setprecision(2) << hops
<< " hops, " <<
1036 if ( !addr
.isIPv4() ) {
1037 debugs(38, 5, "netdbExchangeUpdatePeer: Aborting peer update for '" << addr
<< "', NetDB cannot handle IPv6.");
1041 n
= netdbLookupAddr(addr
);
1046 assert(nullptr != n
);
1048 if ((p
= netdbPeerByName(n
, e
->host
)) == nullptr)
1049 p
= netdbPeerAdd(n
, e
);
1055 p
->expires
= squid_curtime
+ 3600; /* XXX ? */
1060 qsort((char *) n
->peers
,
1062 sizeof(net_db_peer
),
1074 netdbDeleteAddrNetwork(Ip::Address
&addr
)
1077 netdbEntry
*n
= netdbLookupAddr(addr
);
1082 debugs(38, 3, "netdbDeleteAddrNetwork: " << n
->network
);
1091 netdbBinaryExchange(StoreEntry
* s
)
1093 HttpReply
*reply
= new HttpReply
;
1104 struct in_addr line_addr
;
1106 reply
->setHeaders(Http::scOkay
, "OK", nullptr, -1, squid_curtime
, -2);
1107 s
->replaceHttpReply(reply
);
1109 rec_sz
+= 1 + sizeof(struct in_addr
);
1110 rec_sz
+= 1 + sizeof(int);
1111 rec_sz
+= 1 + sizeof(int);
1112 buf
= (char *)memAllocate(MEM_4K_BUF
);
1114 hash_first(addr_table
);
1116 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
1120 if (n
->rtt
> 60000) /* RTT > 1 MIN probably bogus */
1123 if (! (addr
= n
->network
) )
1126 // XXX: NetDB cannot yet handle IPv6 addresses
1127 if ( !addr
.isIPv4() )
1130 buf
[i
] = (char) NETDB_EX_NETWORK
;
1133 addr
.getInAddr(line_addr
);
1134 memcpy(&buf
[i
], &line_addr
, sizeof(struct in_addr
));
1136 i
+= sizeof(struct in_addr
);
1138 buf
[i
] = (char) NETDB_EX_RTT
;
1141 j
= htonl((int) (n
->rtt
* 1000));
1143 memcpy(&buf
[i
], &j
, sizeof(int));
1147 buf
[i
] = (char) NETDB_EX_HOPS
;
1150 j
= htonl((int) (n
->hops
* 1000));
1152 memcpy(&buf
[i
], &j
, sizeof(int));
1156 if (i
+ rec_sz
> 4096) {
1169 memFree(buf
, MEM_4K_BUF
);
1172 reply
->setHeaders(Http::scBadRequest
, "Bad Request", nullptr, -1, squid_curtime
, -2);
1173 s
->replaceHttpReply(reply
);
1174 storeAppendPrintf(s
, "NETDB support not compiled into this Squid cache.\n");
1181 netdbExchangeStart(void *data
)
1184 CachePeer
*p
= (CachePeer
*)data
;
1185 static const SBuf
netDB("netdb");
1186 char *uri
= internalRemoteUri(p
->secure
.encryptTransport
, p
->host
, p
->http_port
, "/squid-internal-dynamic/", netDB
);
1187 debugs(38, 3, "Requesting '" << uri
<< "'");
1188 const auto mx
= MasterXaction::MakePortless
<XactionInitiator::initIcmp
>();
1189 HttpRequestPointer
req(HttpRequest::FromUrlXXX(uri
, mx
));
1192 debugs(38, DBG_IMPORTANT
, "ERROR: " << MYNAME
<< ": Bad URI " << uri
);
1196 netdbExchangeState
*ex
= new netdbExchangeState(p
, req
);
1197 ex
->e
= storeCreateEntry(uri
, uri
, RequestFlags(), Http::METHOD_GET
);
1198 assert(nullptr != ex
->e
);
1200 ex
->sc
= storeClientListAdd(ex
->e
, ex
);
1201 storeClientCopy(ex
->sc
, ex
->e
, ex
->parsingBuffer
.makeInitialSpace(), netdbExchangeHandleReply
, ex
);
1203 ex
->r
->flags
.loopDetected
= true; /* cheat! -- force direct */
1205 // XXX: send as Proxy-Authenticate instead
1207 ex
->r
->url
.userInfo(SBuf(p
->login
));
1209 FwdState::fwdStart(Comm::ConnectionPointer(), ex
->e
, ex
->r
.getRaw());
1216 /// a netdbClosestParent() helper to find the first usable parent CachePeer
1217 /// responsible for the given hostname
1219 findUsableParentAtHostname(PeerSelector
*ps
, const char * const hostname
, const HttpRequest
&request
)
1221 for (const auto &peer
: CurrentCachePeers()) {
1222 const auto p
= peer
.get();
1223 // Both fields should be lowercase, but no code ensures that invariant.
1224 // TODO: net_db_peer should point to CachePeer instead of its hostname!
1225 if (strcasecmp(p
->host
, hostname
) != 0)
1228 if (neighborType(p
, request
.url
) != PEER_PARENT
)
1231 if (!peerHTTPOkay(p
, ps
))
1242 netdbClosestParent(PeerSelector
*ps
)
1246 HttpRequest
*request
= ps
->request
;
1249 const ipcache_addrs
*ia
;
1252 n
= netdbLookupHost(request
->url
.host());
1256 ia
= ipcache_gethostbyname(request
->url
.host(), 0);
1259 n
= netdbLookupAddr(ia
->current());
1265 if (0 == n
->n_peers
)
1268 n
->last_use_time
= squid_curtime
;
1271 * Find the parent with the least RTT to the origin server.
1272 * Make sure we don't return a parent who is farther away than
1273 * we are. Note, the n->peers list is pre-sorted by RTT.
1275 for (i
= 0; i
< n
->n_peers
; ++i
) {
1279 if (n
->rtt
< h
->rtt
)
1282 if (const auto p
= findUsableParentAtHostname(ps
, h
->peername
, *request
))