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