2 * Copyright (C) 1996-2017 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"
26 #include "HttpReply.h"
27 #include "icmp/net_db.h"
29 #include "ip/Address.h"
31 #include "MemObject.h"
32 #include "mgr/Registration.h"
33 #include "mime_header.h"
34 #include "neighbors.h"
35 #include "SquidConfig.h"
36 #include "SquidTime.h"
38 #include "StoreClient.h"
47 #include "icmp/IcmpSquid.h"
49 #include "StoreClient.h"
51 #define NETDB_REQBUF_SZ 4096
59 class netdbExchangeState
61 CBDATA_CLASS(netdbExchangeState
);
64 netdbExchangeState(CachePeer
*aPeer
, const HttpRequestPointer
&theReq
) :
70 // TODO: check if we actually need to do this. should be implicit
71 r
->http_ver
= Http::ProtocolVersion();
74 ~netdbExchangeState() {
75 debugs(38, 3, e
->url());
76 storeUnregister(sc
, e
, this);
77 e
->unlock("netdbExchangeDone");
80 CbcPointer
<CachePeer
> p
;
81 StoreEntry
*e
= nullptr;
82 store_client
*sc
= nullptr;
85 size_t buf_sz
= NETDB_REQBUF_SZ
;
86 char buf
[NETDB_REQBUF_SZ
];
88 netdb_conn_state_t connstate
= STATE_HEADER
;
91 CBDATA_CLASS_INIT(netdbExchangeState
);
93 static hash_table
*addr_table
= NULL
;
94 static hash_table
*host_table
= NULL
;
96 Ip::Address
networkFromInaddr(const Ip::Address
&a
);
97 static void netdbRelease(netdbEntry
* n
);
99 static void netdbHashInsert(netdbEntry
* n
, Ip::Address
&addr
);
100 static void netdbHashDelete(const char *key
);
101 static void netdbHostInsert(netdbEntry
* n
, const char *hostname
);
102 static void netdbHostDelete(const net_db_name
* x
);
103 static void netdbPurgeLRU(void);
104 static netdbEntry
*netdbLookupHost(const char *key
);
105 static net_db_peer
*netdbPeerByName(const netdbEntry
* n
, const char *);
106 static net_db_peer
*netdbPeerAdd(netdbEntry
* n
, CachePeer
* e
);
107 static const char *netdbPeerName(const char *name
);
108 static IPH netdbSendPing
;
109 static FREE netdbFreeNameEntry
;
110 static FREE netdbFreeNetdbEntry
;
111 static STCB netdbExchangeHandleReply
;
113 /* We have to keep a local list of CachePeer names. The Peers structure
114 * gets freed during a reconfigure. We want this database to
115 * remain persisitent, so _net_db_peer->peername points into this
117 static wordlist
*peer_names
= NULL
;
120 netdbHashInsert(netdbEntry
* n
, Ip::Address
&addr
)
122 networkFromInaddr(addr
).toStr(n
->network
, MAX_IPSTRLEN
);
123 n
->hash
.key
= n
->network
;
124 assert(hash_lookup(addr_table
, n
->network
) == NULL
);
125 hash_join(addr_table
, &n
->hash
);
129 netdbHashDelete(const char *key
)
131 hash_link
*hptr
= (hash_link
*)hash_lookup(addr_table
, key
);
134 debug_trap("netdbHashDelete: key not found");
138 hash_remove_link(addr_table
, hptr
);
141 net_db_name::net_db_name(const char *hostname
, netdbEntry
*e
) :
142 next(e
? e
->hosts
: nullptr),
145 hash
.key
= xstrdup(hostname
);
153 netdbHostInsert(netdbEntry
* n
, const char *hostname
)
155 net_db_name
*x
= new net_db_name(hostname
, n
);
156 assert(hash_lookup(host_table
, hostname
) == NULL
);
157 hash_join(host_table
, &x
->hash
);
161 netdbHostDelete(const net_db_name
* x
)
164 assert(x
->net_db_entry
!= NULL
);
166 netdbEntry
*n
= x
->net_db_entry
;
169 for (auto **X
= &n
->hosts
; *X
; X
= &(*X
)->next
) {
176 hash_remove_link(host_table
, (hash_link
*) x
);
181 netdbLookupHost(const char *key
)
183 net_db_name
*x
= (net_db_name
*) hash_lookup(host_table
, key
);
184 return x
? x
->net_db_entry
: NULL
;
188 netdbRelease(netdbEntry
* n
)
193 for (x
= n
->hosts
; x
; x
= next
) {
202 n
->n_peers_alloc
= 0;
204 if (n
->link_count
== 0) {
205 netdbHashDelete(n
->network
);
211 netdbLRU(const void *A
, const void *B
)
213 const netdbEntry
*const *n1
= (const netdbEntry
*const *)A
;
214 const netdbEntry
*const *n2
= (const netdbEntry
*const *)B
;
216 if ((*n1
)->last_use_time
> (*n2
)->last_use_time
)
219 if ((*n1
)->last_use_time
< (*n2
)->last_use_time
)
233 list
= (netdbEntry
**)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry
*));
234 hash_first(addr_table
);
236 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
237 assert(list_count
< netdbEntry::UseCount());
238 *(list
+ list_count
) = n
;
244 sizeof(netdbEntry
*),
247 for (k
= 0; k
< list_count
; ++k
) {
248 if (netdbEntry::UseCount() < Config
.Netdb
.low
)
251 netdbRelease(*(list
+ k
));
260 netdbLookupAddr(const Ip::Address
&addr
)
263 char *key
= new char[MAX_IPSTRLEN
];
264 networkFromInaddr(addr
).toStr(key
,MAX_IPSTRLEN
);
265 n
= (netdbEntry
*) hash_lookup(addr_table
, key
);
271 netdbAdd(Ip::Address
&addr
)
275 if (netdbEntry::UseCount() > Config
.Netdb
.high
)
278 if ((n
= netdbLookupAddr(addr
)) == NULL
) {
280 netdbHashInsert(n
, addr
);
287 netdbSendPing(const ipcache_addrs
*ia
, const Dns::LookupDetails
&, void *data
)
290 char *hostname
= NULL
;
291 static_cast<generic_cbdata
*>(data
)->unwrap(&hostname
);
302 addr
= ia
->in_addrs
[ia
->cur
];
304 if ((n
= netdbLookupHost(hostname
)) == NULL
) {
306 netdbHostInsert(n
, hostname
);
307 } else if ((na
= netdbLookupAddr(addr
)) != n
) {
309 *hostname moved from 'network n' to 'network na'!
315 debugs(38, 3, "netdbSendPing: " << hostname
<< " moved from " << n
->network
<< " to " << na
->network
);
317 x
= (net_db_name
*) hash_lookup(host_table
, hostname
);
320 debugs(38, DBG_IMPORTANT
, "netdbSendPing: net_db_name list bug: " << hostname
<< " not found");
325 /* remove net_db_name from 'network n' linked list */
326 for (X
= &n
->hosts
; *X
; X
= &(*X
)->next
) {
334 /* point to 'network na' from host entry */
335 x
->net_db_entry
= na
;
336 /* link net_db_name to 'network na' */
343 if (n
->next_ping_time
<= squid_curtime
) {
344 debugs(38, 3, "netdbSendPing: pinging " << hostname
);
345 icmpEngine
.DomainPing(addr
, hostname
);
347 n
->next_ping_time
= squid_curtime
+ Config
.Netdb
.period
;
348 n
->last_use_time
= squid_curtime
;
355 networkFromInaddr(const Ip::Address
&in
)
361 /* in IPv6 the 'network' should be the routing section. */
363 out
.applyMask(64, AF_INET6
);
364 debugs(14, 5, "networkFromInaddr : Masked IPv6 Address to " << in
<< "/64 routing part.");
373 if (IN_CLASSC(b
.s_addr
))
374 b
.s_addr
&= IN_CLASSC_NET
;
375 else if (IN_CLASSB(b
.s_addr
))
376 b
.s_addr
&= IN_CLASSB_NET
;
377 else if (IN_CLASSA(b
.s_addr
))
378 b
.s_addr
&= IN_CLASSA_NET
;
384 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << out
<< "/24.");
386 /* use /24 for everything under IPv4 */
387 out
.applyMask(24, AF_INET
);
388 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << in
<< "/24.");
394 sortByRtt(const void *A
, const void *B
)
396 const netdbEntry
*const *n1
= (const netdbEntry
*const *)A
;
397 const netdbEntry
*const *n2
= (const netdbEntry
*const *)B
;
399 if ((*n1
)->rtt
> (*n2
)->rtt
)
401 else if ((*n1
)->rtt
< (*n2
)->rtt
)
408 netdbPeerByName(const netdbEntry
* n
, const char *peername
)
411 net_db_peer
*p
= n
->peers
;
413 for (i
= 0; i
< n
->n_peers
; ++i
, ++p
) {
414 if (!strcmp(p
->peername
, peername
))
422 netdbPeerAdd(netdbEntry
* n
, CachePeer
* e
)
429 if (n
->n_peers
== n
->n_peers_alloc
) {
431 osize
= n
->n_peers_alloc
;
433 if (n
->n_peers_alloc
== 0)
434 n
->n_peers_alloc
= 2;
436 n
->n_peers_alloc
<<= 1;
438 debugs(38, 3, "netdbPeerAdd: Growing peer list for '" << n
->network
<< "' to " << n
->n_peers_alloc
);
440 n
->peers
= (net_db_peer
*)xcalloc(n
->n_peers_alloc
, sizeof(net_db_peer
));
442 for (i
= 0; i
< osize
; ++i
)
443 *(n
->peers
+ i
) = *(o
+ i
);
450 p
= n
->peers
+ n
->n_peers
;
451 p
->peername
= netdbPeerName(e
->host
);
457 sortPeerByRtt(const void *A
, const void *B
)
459 const net_db_peer
*p1
= (net_db_peer
*)A
;
460 const net_db_peer
*p2
= (net_db_peer
*)B
;
462 if (p1
->rtt
> p2
->rtt
)
464 else if (p1
->rtt
< p2
->rtt
)
471 netdbSaveState(void *foo
)
473 if (strcmp(Config
.netdbFilename
, "none") == 0)
480 struct timeval start
= current_time
;
483 * This was nicer when we were using stdio, but thanks to
484 * Solaris bugs, its a bad idea. fopen can fail if more than
488 * unlink() is here because there is currently no way to make
489 * logfileOpen() use O_TRUNC.
491 unlink(Config
.netdbFilename
);
492 lf
= logfileOpen(Config
.netdbFilename
, 4096, 0);
496 debugs(50, DBG_IMPORTANT
, MYNAME
<< Config
.netdbFilename
<< ": " << xstrerr(xerrno
));
500 hash_first(addr_table
);
502 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
503 if (n
->pings_recv
== 0)
506 logfilePrintf(lf
, "%s %d %d %10.5f %10.5f %d %d",
512 (int) n
->next_ping_time
,
513 (int) n
->last_use_time
);
515 for (x
= n
->hosts
; x
; x
= x
->next
)
516 logfilePrintf(lf
, " %s", hashKeyStr(&x
->hash
));
518 logfilePrintf(lf
, "\n");
528 debugs(38, DBG_IMPORTANT
, "NETDB state saved; " <<
529 count
<< " entries, " <<
530 tvSubMsec(start
, current_time
) << " msec" );
531 eventAddIsh("netdbSaveState", netdbSaveState
, NULL
, 3600.0, 1);
535 netdbReloadState(void)
537 if (strcmp(Config
.netdbFilename
, "none") == 0)
551 struct timeval start
= current_time
;
553 * This was nicer when we were using stdio, but thanks to
554 * Solaris bugs, its a bad idea. fopen can fail if more than
557 fd
= file_open(Config
.netdbFilename
, O_RDONLY
| O_BINARY
);
562 if (fstat(fd
, &sb
) < 0) {
568 char *buf
= (char *)xcalloc(1, sb
.st_size
+ 1);
570 l
= FD_READ_METHOD(fd
, buf
, sb
.st_size
);
578 while ((s
= strchr(t
, '\n'))) {
582 memset(&N
, '\0', sizeof(netdbEntry
));
583 q
= strtok(t
, w_space
);
592 if (netdbLookupAddr(addr
) != NULL
) /* no dups! */
595 if ((q
= strtok(NULL
, w_space
)) == NULL
)
598 N
.pings_sent
= atoi(q
);
600 if ((q
= strtok(NULL
, w_space
)) == NULL
)
603 N
.pings_recv
= atoi(q
);
605 if (N
.pings_recv
== 0)
608 /* give this measurement low weight */
613 if ((q
= strtok(NULL
, w_space
)) == NULL
)
618 if ((q
= strtok(NULL
, w_space
)) == NULL
)
623 if ((q
= strtok(NULL
, w_space
)) == NULL
)
626 N
.next_ping_time
= (time_t) atoi(q
);
628 if ((q
= strtok(NULL
, w_space
)) == NULL
)
631 N
.last_use_time
= (time_t) atoi(q
);
635 memcpy(n
, &N
, sizeof(netdbEntry
));
637 netdbHashInsert(n
, addr
);
639 while ((q
= strtok(NULL
, w_space
)) != NULL
) {
640 if (netdbLookupHost(q
) != NULL
) /* no dups! */
643 netdbHostInsert(n
, q
);
651 debugs(38, DBG_IMPORTANT
, "NETDB state reloaded; " <<
652 count
<< " entries, " <<
653 tvSubMsec(start
, current_time
) << " msec" );
657 netdbPeerName(const char *name
)
661 for (w
= peer_names
; w
; w
= w
->next
) {
662 if (!strcmp(w
->key
, name
))
666 return wordlistAdd(&peer_names
, name
);
670 netdbFreeNetdbEntry(void *data
)
672 netdbEntry
*n
= (netdbEntry
*)data
;
678 netdbFreeNameEntry(void *data
)
680 net_db_name
*x
= (net_db_name
*)data
;
685 netdbExchangeHandleReply(void *data
, StoreIOBuffer receivedData
)
689 netdbExchangeState
*ex
= (netdbExchangeState
*)data
;
693 struct in_addr line_addr
;
698 HttpReply
const *rep
;
702 int oldbufofs
= ex
->buf_ofs
;
705 rec_sz
+= 1 + sizeof(struct in_addr
);
706 rec_sz
+= 1 + sizeof(int);
707 rec_sz
+= 1 + sizeof(int);
708 debugs(38, 3, "netdbExchangeHandleReply: " << receivedData
.length
<< " read bytes");
710 if (!ex
->p
.valid()) {
711 debugs(38, 3, "netdbExchangeHandleReply: Peer became invalid");
716 debugs(38, 3, "netdbExchangeHandleReply: for '" << ex
->p
->host
<< ":" << ex
->p
->http_port
<< "'");
718 if (receivedData
.length
== 0 && !receivedData
.flags
.error
) {
719 debugs(38, 3, "netdbExchangeHandleReply: Done");
726 /* Get the size of the buffer now */
727 size
= ex
->buf_ofs
+ receivedData
.length
;
728 debugs(38, 3, "netdbExchangeHandleReply: " << size
<< " bytes buf");
730 /* Check if we're still doing headers */
732 if (ex
->connstate
== STATE_HEADER
) {
734 ex
->buf_ofs
+= receivedData
.length
;
736 /* skip reply headers */
738 if ((hdr_sz
= headersEnd(p
, ex
->buf_ofs
))) {
739 debugs(38, 5, "netdbExchangeHandleReply: hdr_sz = " << hdr_sz
);
740 rep
= ex
->e
->getReply();
741 assert(rep
->sline
.status() != Http::scNone
);
742 debugs(38, 3, "netdbExchangeHandleReply: reply status " << rep
->sline
.status());
744 if (rep
->sline
.status() != Http::scOkay
) {
749 assert((size_t)ex
->buf_ofs
>= hdr_sz
);
752 * Now, point p to the part of the buffer where the data
753 * starts, and update the size accordingly
755 assert(ex
->used
== 0);
757 size
= ex
->buf_ofs
- hdr_sz
;
760 /* Finally, set the conn state mode to STATE_BODY */
761 ex
->connstate
= STATE_BODY
;
763 StoreIOBuffer tempBuffer
;
764 tempBuffer
.offset
= ex
->buf_ofs
;
765 tempBuffer
.length
= ex
->buf_sz
- ex
->buf_ofs
;
766 tempBuffer
.data
= ex
->buf
+ ex
->buf_ofs
;
767 /* Have more headers .. */
768 storeClientCopy(ex
->sc
, ex
->e
, tempBuffer
,
769 netdbExchangeHandleReply
, ex
);
774 assert(ex
->connstate
== STATE_BODY
);
776 /* If we get here, we have some body to parse .. */
777 debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size
);
779 while (size
>= rec_sz
) {
780 debugs(38, 5, "netdbExchangeHandleReply: in parsing loop, size = " << size
);
784 for (o
= 0; o
< rec_sz
;) {
785 switch ((int) *(p
+ o
)) {
787 case NETDB_EX_NETWORK
:
789 /* FIXME INET6 : NetDB can still ony send IPv4 */
790 memcpy(&line_addr
, p
+ o
, sizeof(struct in_addr
));
792 o
+= sizeof(struct in_addr
);
797 memcpy(&j
, p
+ o
, sizeof(int));
799 rtt
= (double) ntohl(j
) / 1000.0;
804 memcpy(&j
, p
+ o
, sizeof(int));
806 hops
= (double) ntohl(j
) / 1000.0;
810 debugs(38, DBG_IMPORTANT
, "netdbExchangeHandleReply: corrupt data, aborting");
816 if (!addr
.isAnyAddr() && rtt
> 0)
817 netdbExchangeUpdatePeer(addr
, ex
->p
.get(), rtt
, hops
);
831 * Copy anything that is left over to the beginning of the buffer,
832 * and adjust buf_ofs accordingly
836 * Evilly, size refers to the buf size left now,
837 * ex->buf_ofs is the original buffer size, so just copy that
840 memmove(ex
->buf
, ex
->buf
+ (ex
->buf_ofs
- size
), size
);
845 * And don't re-copy the remaining data ..
850 * Now the tricky bit - size _included_ the leftover bit from the _last_
851 * storeClientCopy. We don't want to include that, or our offset will be wrong.
852 * So, don't count the size of the leftover buffer we began with.
853 * This can _disappear_ when we're not tracking offsets ..
855 ex
->used
-= oldbufofs
;
857 debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size
<< " bytes");
859 debugs(38, 3, "netdbExchangeHandleReply: used " << nused
<<
860 " entries, (x " << rec_sz
<< " bytes) == " << nused
* rec_sz
<<
863 debugs(38, 3, "netdbExchangeHandleReply: used " << ex
->used
);
865 if (EBIT_TEST(ex
->e
->flags
, ENTRY_ABORTED
)) {
866 debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED");
868 } else if (ex
->e
->store_status
== STORE_PENDING
) {
869 StoreIOBuffer tempBuffer
;
870 tempBuffer
.offset
= ex
->used
;
871 tempBuffer
.length
= ex
->buf_sz
- ex
->buf_ofs
;
872 tempBuffer
.data
= ex
->buf
+ ex
->buf_ofs
;
873 debugs(38, 3, "netdbExchangeHandleReply: EOF not received");
874 storeClientCopy(ex
->sc
, ex
->e
, tempBuffer
,
875 netdbExchangeHandleReply
, ex
);
879 #endif /* USE_ICMP */
881 /* PUBLIC FUNCTIONS */
887 Mgr::RegisterAction("netdb", "Network Measurement Database", netdbDump
, 0, 1);
892 int n
= hashPrime(Config
.Netdb
.high
/ 4);
894 addr_table
= hash_create((HASHCMP
*) strcmp
, n
, hash_string
);
896 n
= hashPrime(3 * Config
.Netdb
.high
/ 4);
898 host_table
= hash_create((HASHCMP
*) strcmp
, n
, hash_string
);
900 eventAddIsh("netdbSaveState", netdbSaveState
, NULL
, 3600.0, 1);
908 netdbPingSite(const char *hostname
)
913 if ((n
= netdbLookupHost(hostname
)) != NULL
)
914 if (n
->next_ping_time
> squid_curtime
)
917 ipcache_nbgethostbyname(hostname
, netdbSendPing
,
918 new generic_cbdata(xstrdup(hostname
)));
924 netdbHandlePingReply(const Ip::Address
&from
, int hops
, int rtt
)
929 debugs(38, 3, "netdbHandlePingReply: from " << from
);
931 if ((n
= netdbLookupAddr(from
)) == NULL
)
942 n
->hops
= ((n
->hops
* (N
- 1)) + hops
) / N
;
944 n
->rtt
= ((n
->rtt
* (N
- 1)) + rtt
) / N
;
946 debugs(38, 3, "netdbHandlePingReply: " << n
->network
<< "; rtt="<<
947 std::setw(5)<< std::setprecision(2) << n
->rtt
<< " hops="<<
948 std::setw(4) << n
->hops
);
954 netdbFreeMemory(void)
957 hashFreeItems(addr_table
, netdbFreeNetdbEntry
);
958 hashFreeMemory(addr_table
);
960 hashFreeItems(host_table
, netdbFreeNameEntry
);
961 hashFreeMemory(host_table
);
963 wordlistDestroy(&peer_names
);
969 netdbDump(StoreEntry
* sentry
)
979 storeAppendPrintf(sentry
, "Network DB Statistics:\n");
980 storeAppendPrintf(sentry
, "%-46.46s %9s %7s %5s %s\n", /* Max between 16 (IPv4) or 46 (IPv6) */
986 list
= (netdbEntry
**)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry
*));
988 hash_first(addr_table
);
990 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
995 if (i
!= netdbEntry::UseCount())
996 debugs(38, DBG_CRITICAL
, "WARNING: netdb_addrs count off, found " << i
<<
997 ", expected " << netdbEntry::UseCount());
1001 sizeof(netdbEntry
*),
1004 for (k
= 0; k
< i
; ++k
) {
1006 storeAppendPrintf(sentry
, "%-46.46s %4d/%4d %7.1f %5.1f", /* Max between 16 (IPv4) or 46 (IPv6) */
1013 for (x
= n
->hosts
; x
; x
= x
->next
)
1014 storeAppendPrintf(sentry
, " %s", hashKeyStr(&x
->hash
));
1016 storeAppendPrintf(sentry
, "\n");
1020 for (j
= 0; j
< n
->n_peers
; ++j
, ++p
) {
1021 storeAppendPrintf(sentry
, " %-22.22s %7.1f %5.1f\n",
1031 storeAppendPrintf(sentry
,"NETDB support not compiled into this Squid cache.\n");
1036 netdbHostHops(const char *host
)
1039 netdbEntry
*n
= netdbLookupHost(host
);
1042 n
->last_use_time
= squid_curtime
;
1043 return (int) (n
->hops
+ 0.5);
1051 netdbHostRtt(const char *host
)
1054 netdbEntry
*n
= netdbLookupHost(host
);
1057 n
->last_use_time
= squid_curtime
;
1058 return (int) (n
->rtt
+ 0.5);
1066 netdbHostData(const char *host
, int *samp
, int *rtt
, int *hops
)
1069 netdbEntry
*n
= netdbLookupHost(host
);
1074 *samp
= n
->pings_recv
;
1076 *rtt
= (int) (n
->rtt
+ 0.5);
1078 *hops
= (int) (n
->hops
+ 0.5);
1080 n
->last_use_time
= squid_curtime
;
1086 netdbUpdatePeer(const URL
&url
, CachePeer
* e
, int irtt
, int ihops
)
1090 double rtt
= (double) irtt
;
1091 double hops
= (double) ihops
;
1093 debugs(38, 3, url
.host() << ", " << ihops
<< " hops, " << irtt
<< " rtt");
1094 n
= netdbLookupHost(url
.host());
1097 debugs(38, 3, "host " << url
.host() << " not found");
1101 if ((p
= netdbPeerByName(n
, e
->host
)) == NULL
)
1102 p
= netdbPeerAdd(n
, e
);
1108 p
->expires
= squid_curtime
+ 3600;
1113 qsort((char *) n
->peers
,
1115 sizeof(net_db_peer
),
1122 netdbExchangeUpdatePeer(Ip::Address
&addr
, CachePeer
* e
, double rtt
, double hops
)
1127 debugs(38, 5, "netdbExchangeUpdatePeer: '" << addr
<< "', "<<
1128 std::setfill('0')<< std::setprecision(2) << hops
<< " hops, " <<
1131 if ( !addr
.isIPv4() ) {
1132 debugs(38, 5, "netdbExchangeUpdatePeer: Aborting peer update for '" << addr
<< "', NetDB cannot handle IPv6.");
1136 n
= netdbLookupAddr(addr
);
1143 if ((p
= netdbPeerByName(n
, e
->host
)) == NULL
)
1144 p
= netdbPeerAdd(n
, e
);
1150 p
->expires
= squid_curtime
+ 3600; /* XXX ? */
1155 qsort((char *) n
->peers
,
1157 sizeof(net_db_peer
),
1164 netdbDeleteAddrNetwork(Ip::Address
&addr
)
1167 netdbEntry
*n
= netdbLookupAddr(addr
);
1172 debugs(38, 3, "netdbDeleteAddrNetwork: " << n
->network
);
1179 netdbBinaryExchange(StoreEntry
* s
)
1181 HttpReply
*reply
= new HttpReply
;
1192 struct in_addr line_addr
;
1194 reply
->setHeaders(Http::scOkay
, "OK", NULL
, -1, squid_curtime
, -2);
1195 s
->replaceHttpReply(reply
);
1197 rec_sz
+= 1 + sizeof(struct in_addr
);
1198 rec_sz
+= 1 + sizeof(int);
1199 rec_sz
+= 1 + sizeof(int);
1200 buf
= (char *)memAllocate(MEM_4K_BUF
);
1202 hash_first(addr_table
);
1204 while ((n
= (netdbEntry
*) hash_next(addr_table
))) {
1208 if (n
->rtt
> 60000) /* RTT > 1 MIN probably bogus */
1211 if (! (addr
= n
->network
) )
1214 /* FIXME INET6 : NetDB cannot yet handle IPv6 addresses. Ensure only IPv4 get sent. */
1215 if ( !addr
.isIPv4() )
1218 buf
[i
] = (char) NETDB_EX_NETWORK
;
1221 addr
.getInAddr(line_addr
);
1222 memcpy(&buf
[i
], &line_addr
, sizeof(struct in_addr
));
1224 i
+= sizeof(struct in_addr
);
1226 buf
[i
] = (char) NETDB_EX_RTT
;
1229 j
= htonl((int) (n
->rtt
* 1000));
1231 memcpy(&buf
[i
], &j
, sizeof(int));
1235 buf
[i
] = (char) NETDB_EX_HOPS
;
1238 j
= htonl((int) (n
->hops
* 1000));
1240 memcpy(&buf
[i
], &j
, sizeof(int));
1244 if (i
+ rec_sz
> 4096) {
1257 memFree(buf
, MEM_4K_BUF
);
1260 reply
->setHeaders(Http::scBadRequest
, "Bad Request", NULL
, -1, squid_curtime
, -2);
1261 s
->replaceHttpReply(reply
);
1262 storeAppendPrintf(s
, "NETDB support not compiled into this Squid cache.\n");
1269 netdbExchangeStart(void *data
)
1272 CachePeer
*p
= (CachePeer
*)data
;
1273 static const SBuf
netDB("netdb");
1274 char *uri
= internalRemoteUri(p
->host
, p
->http_port
, "/squid-internal-dynamic/", netDB
);
1275 debugs(38, 3, "netdbExchangeStart: Requesting '" << uri
<< "'");
1276 assert(NULL
!= uri
);
1277 const MasterXaction::Pointer mx
= new MasterXaction(XactionInitiator::initIcmp
);
1278 HttpRequestPointer
req(HttpRequest::FromUrl(uri
, mx
));
1281 debugs(38, DBG_IMPORTANT
, "netdbExchangeStart: Bad URI " << uri
);
1285 netdbExchangeState
*ex
= new netdbExchangeState(p
, req
);
1286 ex
->e
= storeCreateEntry(uri
, uri
, RequestFlags(), Http::METHOD_GET
);
1287 assert(NULL
!= ex
->e
);
1289 StoreIOBuffer tempBuffer
;
1290 tempBuffer
.length
= ex
->buf_sz
;
1291 tempBuffer
.data
= ex
->buf
;
1293 ex
->sc
= storeClientListAdd(ex
->e
, ex
);
1295 storeClientCopy(ex
->sc
, ex
->e
, tempBuffer
,
1296 netdbExchangeHandleReply
, ex
);
1297 ex
->r
->flags
.loopDetected
= true; /* cheat! -- force direct */
1299 // XXX: send as Proxy-Authenticate instead
1301 ex
->r
->url
.userInfo(SBuf(p
->login
));
1303 FwdState::fwdStart(Comm::ConnectionPointer(), ex
->e
, ex
->r
.getRaw());
1308 netdbClosestParent(HttpRequest
* request
)
1311 CachePeer
*p
= NULL
;
1313 const ipcache_addrs
*ia
;
1316 n
= netdbLookupHost(request
->url
.host());
1320 ia
= ipcache_gethostbyname(request
->url
.host(), 0);
1323 n
= netdbLookupAddr(ia
->in_addrs
[ia
->cur
]);
1329 if (0 == n
->n_peers
)
1332 n
->last_use_time
= squid_curtime
;
1335 * Find the parent with the least RTT to the origin server.
1336 * Make sure we don't return a parent who is farther away than
1337 * we are. Note, the n->peers list is pre-sorted by RTT.
1339 for (i
= 0; i
< n
->n_peers
; ++i
) {
1343 if (n
->rtt
< h
->rtt
)
1346 p
= peerFindByName(h
->peername
);
1348 if (NULL
== p
) /* not found */
1351 if (neighborType(p
, request
->url
) != PEER_PARENT
)
1354 if (!peerHTTPOkay(p
, request
)) /* not allowed */