]> git.ipfire.org Git - thirdparty/squid.git/blame - src/net_db.cc
Avoid NULL http->entry pointer in clientBuildReplyHeader().
[thirdparty/squid.git] / src / net_db.cc
CommitLineData
87801fcb 1
516350ca 2/*
2b6662ba 3 * $Id: net_db.cc,v 1.156 2001/01/12 00:37:20 wessels Exp $
516350ca 4 *
67f46679 5 * DEBUG: section 38 Network Measurement Database
516350ca 6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
516350ca 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
516350ca 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
516350ca 34 */
35
87801fcb 36#include "squid.h"
37
a97cfa48 38#if USE_ICMP
8b833697 39
9ad1cbca 40typedef struct {
a74c5601 41 peer *p;
42 StoreEntry *e;
06d2839d 43 store_client *sc;
a74c5601 44 request_t *r;
45 off_t seen;
46 off_t used;
47 size_t buf_sz;
48 char *buf;
9ad1cbca 49} netdbExchangeState;
50
ce75f381 51static hash_table *addr_table = NULL;
365e5b34 52static hash_table *host_table = NULL;
87801fcb 53
f5b8bbc4 54static struct in_addr networkFromInaddr(struct in_addr a);
55static void netdbRelease(netdbEntry * n);
f5b8bbc4 56static void netdbHashInsert(netdbEntry * n, struct in_addr addr);
57static void netdbHashDelete(const char *key);
587d8445 58static void netdbHostInsert(netdbEntry * n, const char *hostname);
59static void netdbHostDelete(const net_db_name * x);
f5b8bbc4 60static void netdbPurgeLRU(void);
23d92c64 61static netdbEntry *netdbLookupHost(const char *key);
f5b8bbc4 62static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *);
63static net_db_peer *netdbPeerAdd(netdbEntry * n, peer * e);
858783c9 64static const char *netdbPeerName(const char *name);
b69f7771 65static IPH netdbSendPing;
79d39a72 66static QS sortPeerByRtt;
67static QS sortByRtt;
68static QS netdbLRU;
ec878047 69static FREE netdbFreeNameEntry;
70static FREE netdbFreeNetdbEntry;
9ad1cbca 71static STCB netdbExchangeHandleReply;
45c54bdc 72static void netdbExchangeDone(void *);
429fdbec 73
74/* We have to keep a local list of peer names. The Peers structure
75 * gets freed during a reconfigure. We want this database to
76 * remain persisitent, so _net_db_peer->peername points into this
77 * linked list */
78static wordlist *peer_names = NULL;
67508012 79
87801fcb 80static void
67508012 81netdbHashInsert(netdbEntry * n, struct in_addr addr)
87801fcb 82{
f2052513 83 xstrncpy(n->network, inet_ntoa(networkFromInaddr(addr)), 16);
186477c1 84 n->hash.key = n->network;
587d8445 85 assert(hash_lookup(addr_table, n->network) == NULL);
186477c1 86 hash_join(addr_table, &n->hash);
87801fcb 87}
88
89static void
0ee4272b 90netdbHashDelete(const char *key)
87801fcb 91{
67508012 92 hash_link *hptr = hash_lookup(addr_table, key);
87801fcb 93 if (hptr == NULL) {
94 debug_trap("netdbHashDelete: key not found");
95 return;
96 }
e0b1c6aa 97 hash_remove_link(addr_table, hptr);
67508012 98}
99
100static void
587d8445 101netdbHostInsert(netdbEntry * n, const char *hostname)
67508012 102{
7021844c 103 net_db_name *x = memAllocate(MEM_NET_DB_NAME);
186477c1 104 x->hash.key = xstrdup(hostname);
67508012 105 x->next = n->hosts;
106 n->hosts = x;
587d8445 107 x->net_db_entry = n;
108 assert(hash_lookup(host_table, hostname) == NULL);
186477c1 109 hash_join(host_table, &x->hash);
67508012 110 n->link_count++;
111}
112
113static void
587d8445 114netdbHostDelete(const net_db_name * x)
67508012 115{
116 netdbEntry *n;
b19361fd 117 net_db_name **X;
b19361fd 118 assert(x != NULL);
587d8445 119 assert(x->net_db_entry != NULL);
120 n = x->net_db_entry;
b19361fd 121 n->link_count--;
587d8445 122 for (X = &n->hosts; *X; X = &(*X)->next) {
123 if (*X == x) {
124 *X = x->next;
125 break;
126 }
127 }
128 hash_remove_link(host_table, (hash_link *) x);
186477c1 129 xfree(x->hash.key);
db1cd23c 130 memFree((void *) x, MEM_NET_DB_NAME);
87801fcb 131}
132
133static netdbEntry *
0ee4272b 134netdbLookupHost(const char *key)
87801fcb 135{
587d8445 136 net_db_name *x = (net_db_name *) hash_lookup(host_table, key);
137 return x ? x->net_db_entry : NULL;
87801fcb 138}
139
c907000b 140static void
141netdbRelease(netdbEntry * n)
142{
429fdbec 143 net_db_name *x;
587d8445 144 net_db_name *next;
c907000b 145 for (x = n->hosts; x; x = next) {
146 next = x->next;
587d8445 147 netdbHostDelete(x);
c907000b 148 }
149 n->hosts = NULL;
429fdbec 150 safe_free(n->peers);
429fdbec 151 n->peers = NULL;
152 n->n_peers = 0;
153 n->n_peers_alloc = 0;
c907000b 154 if (n->link_count == 0) {
155 netdbHashDelete(n->network);
db1cd23c 156 memFree(n, MEM_NETDBENTRY);
c907000b 157 }
158}
159
160static int
79d39a72 161netdbLRU(const void *A, const void *B)
c907000b 162{
79d39a72 163 const netdbEntry *const *n1 = A;
164 const netdbEntry *const *n2 = B;
c907000b 165 if ((*n1)->last_use_time > (*n2)->last_use_time)
166 return (1);
167 if ((*n1)->last_use_time < (*n2)->last_use_time)
168 return (-1);
169 return (0);
170}
171
c907000b 172static void
173netdbPurgeLRU(void)
174{
175 netdbEntry *n;
176 netdbEntry **list;
177 int k = 0;
178 int list_count = 0;
179 int removed = 0;
587d8445 180 list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
0f6bebac 181 hash_first(addr_table);
182 while ((n = (netdbEntry *) hash_next(addr_table))) {
587d8445 183 assert(list_count < memInUse(MEM_NETDBENTRY));
c907000b 184 *(list + list_count) = n;
185 list_count++;
c907000b 186 }
187 qsort((char *) list,
188 list_count,
189 sizeof(netdbEntry *),
79d39a72 190 netdbLRU);
c907000b 191 for (k = 0; k < list_count; k++) {
587d8445 192 if (memInUse(MEM_NETDBENTRY) < Config.Netdb.low)
c907000b 193 break;
194 netdbRelease(*(list + k));
195 removed++;
196 }
197 xfree(list);
198}
199
67508012 200static netdbEntry *
201netdbLookupAddr(struct in_addr addr)
87801fcb 202{
587d8445 203 netdbEntry *n;
67508012 204 char *key = inet_ntoa(networkFromInaddr(addr));
587d8445 205 n = (netdbEntry *) hash_lookup(addr_table, key);
206 return n;
87801fcb 207}
208
67508012 209static netdbEntry *
587d8445 210netdbAdd(struct in_addr addr)
87801fcb 211{
212 netdbEntry *n;
587d8445 213 if (memInUse(MEM_NETDBENTRY) > Config.Netdb.high)
c907000b 214 netdbPurgeLRU();
67508012 215 if ((n = netdbLookupAddr(addr)) == NULL) {
7021844c 216 n = memAllocate(MEM_NETDBENTRY);
67508012 217 netdbHashInsert(n, addr);
87801fcb 218 }
67508012 219 return n;
87801fcb 220}
221
222static void
82691c78 223netdbSendPing(const ipcache_addrs * ia, void *data)
87801fcb 224{
67508012 225 struct in_addr addr;
28c60158 226 char *hostname = ((generic_cbdata *) data)->data;
87801fcb 227 netdbEntry *n;
b19361fd 228 netdbEntry *na;
587d8445 229 net_db_name *x;
230 net_db_name **X;
28c60158 231 cbdataFree(data);
e5f6c5c2 232 if (ia == NULL) {
28c60158 233 xfree(hostname);
87801fcb 234 return;
cb190ed7 235 }
e5f6c5c2 236 addr = ia->in_addrs[ia->cur];
b19361fd 237 if ((n = netdbLookupHost(hostname)) == NULL) {
587d8445 238 n = netdbAdd(addr);
239 netdbHostInsert(n, hostname);
b19361fd 240 } else if ((na = netdbLookupAddr(addr)) != n) {
241 /*
242 *hostname moved from 'network n' to 'network na'!
243 */
244 if (na == NULL)
587d8445 245 na = netdbAdd(addr);
67f46679 246 debug(38, 3) ("netdbSendPing: %s moved from %s to %s\n",
b19361fd 247 hostname, n->network, na->network);
587d8445 248 x = (net_db_name *) hash_lookup(host_table, hostname);
249 if (x == NULL) {
67f46679 250 debug(38, 1) ("netdbSendPing: net_db_name list bug: %s not found", hostname);
28c60158 251 xfree(hostname);
587d8445 252 return;
253 }
254 /* remove net_db_name from 'network n' linked list */
255 for (X = &n->hosts; *X; X = &(*X)->next) {
256 if (*X == x) {
257 *X = x->next;
258 break;
259 }
260 }
261 n->link_count--;
262 /* point to 'network na' from host entry */
263 x->net_db_entry = na;
264 /* link net_db_name to 'network na' */
265 x->next = na->hosts;
266 na->hosts = x;
267 na->link_count++;
b19361fd 268 n = na;
269 }
674ac814 270 if (n->next_ping_time <= squid_curtime) {
271 debug(38, 3) ("netdbSendPing: pinging %s\n", hostname);
272 icmpDomainPing(addr, hostname);
273 n->pings_sent++;
274 n->next_ping_time = squid_curtime + Config.Netdb.period;
275 n->last_use_time = squid_curtime;
276 }
28c60158 277 xfree(hostname);
67508012 278}
279
67508012 280static struct in_addr
281networkFromInaddr(struct in_addr a)
282{
a9778d11 283 struct in_addr b;
284 b.s_addr = ntohl(a.s_addr);
429fdbec 285#if USE_CLASSFUL
67508012 286 if (IN_CLASSC(b.s_addr))
a9778d11 287 b.s_addr &= IN_CLASSC_NET;
67508012 288 else if (IN_CLASSB(b.s_addr))
a9778d11 289 b.s_addr &= IN_CLASSB_NET;
67508012 290 else if (IN_CLASSA(b.s_addr))
a9778d11 291 b.s_addr &= IN_CLASSA_NET;
429fdbec 292#else
293 /* use /24 for everything */
294 b.s_addr &= IN_CLASSC_NET;
295#endif
a9778d11 296 b.s_addr = htonl(b.s_addr);
67508012 297 return b;
298}
299
300static int
79d39a72 301sortByRtt(const void *A, const void *B)
67508012 302{
79d39a72 303 const netdbEntry *const *n1 = A;
304 const netdbEntry *const *n2 = B;
429fdbec 305 if ((*n1)->rtt > (*n2)->rtt)
67508012 306 return 1;
429fdbec 307 else if ((*n1)->rtt < (*n2)->rtt)
67508012 308 return -1;
309 else
310 return 0;
311}
312
429fdbec 313static net_db_peer *
314netdbPeerByName(const netdbEntry * n, const char *peername)
315{
316 int i;
317 net_db_peer *p = n->peers;
318 for (i = 0; i < n->n_peers; i++, p++) {
319 if (!strcmp(p->peername, peername))
320 return p;
321 }
322 return NULL;
323}
324
325static net_db_peer *
326netdbPeerAdd(netdbEntry * n, peer * e)
327{
328 net_db_peer *p;
329 net_db_peer *o;
330 int osize;
331 int i;
332 if (n->n_peers == n->n_peers_alloc) {
333 o = n->peers;
334 osize = n->n_peers_alloc;
335 if (n->n_peers_alloc == 0)
336 n->n_peers_alloc = 2;
337 else
338 n->n_peers_alloc <<= 1;
67f46679 339 debug(38, 3) ("netdbPeerAdd: Growing peer list for '%s' to %d\n",
429fdbec 340 n->network, n->n_peers_alloc);
341 n->peers = xcalloc(n->n_peers_alloc, sizeof(net_db_peer));
429fdbec 342 for (i = 0; i < osize; i++)
343 *(n->peers + i) = *(o + i);
344 if (osize) {
345 safe_free(o);
429fdbec 346 }
347 }
348 p = n->peers + n->n_peers;
349 p->peername = netdbPeerName(e->host);
350 n->n_peers++;
351 return p;
352}
353
354static int
79d39a72 355sortPeerByRtt(const void *A, const void *B)
429fdbec 356{
79d39a72 357 const net_db_peer *p1 = A;
358 const net_db_peer *p2 = B;
429fdbec 359 if (p1->rtt > p2->rtt)
360 return 1;
361 else if (p1->rtt < p2->rtt)
362 return -1;
363 else
364 return 0;
365}
366
367static void
368netdbSaveState(void *foo)
369{
370 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
08e8e020 371 Logfile *lf;
429fdbec 372 netdbEntry *n;
429fdbec 373 net_db_name *x;
374 struct timeval start = current_time;
375 int count = 0;
042461c3 376 snprintf(path, SQUID_MAXPATHLEN, "%s/netdb_state", storeSwapDir(0));
111b16cf 377 /*
378 * This was nicer when we were using stdio, but thanks to
379 * Solaris bugs, its a bad idea. fopen can fail if more than
380 * 256 FDs are open.
381 */
e3732f4f 382 /*
383 * unlink() is here because there is currently no way to make
384 * logfileOpen() use O_TRUNC.
385 */
386 unlink(path);
08e8e020 387 lf = logfileOpen(path, 4096, 0);
388 if (NULL == lf) {
a3d5953d 389 debug(50, 1) ("netdbSaveState: %s: %s\n", path, xstrerror());
429fdbec 390 return;
391 }
0f6bebac 392 hash_first(addr_table);
393 while ((n = (netdbEntry *) hash_next(addr_table))) {
429fdbec 394 if (n->pings_recv == 0)
395 continue;
08e8e020 396 logfilePrintf(lf, "%s %d %d %10.5f %10.5f %d %d",
429fdbec 397 n->network,
398 n->pings_sent,
399 n->pings_recv,
400 n->hops,
401 n->rtt,
402 (int) n->next_ping_time,
403 (int) n->last_use_time);
404 for (x = n->hosts; x; x = x->next)
186477c1 405 logfilePrintf(lf, " %s", hashKeyStr(&x->hash));
08e8e020 406 logfilePrintf(lf, "\n");
429fdbec 407 count++;
111b16cf 408#undef RBUF_SZ
429fdbec 409 }
08e8e020 410 logfileClose(lf);
587d8445 411 getCurrentTime();
67f46679 412 debug(38, 1) ("NETDB state saved; %d entries, %d msec\n",
429fdbec 413 count, tvSubMsec(start, current_time));
52040193 414 eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1);
429fdbec 415}
416
417static void
418netdbReloadState(void)
419{
420 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
1b4056ed 421 char *buf;
429fdbec 422 char *t;
111b16cf 423 char *s;
424 int fd;
425 int l;
426 struct stat sb;
429fdbec 427 netdbEntry *n;
428 netdbEntry N;
429 struct in_addr addr;
430 int count = 0;
431 struct timeval start = current_time;
042461c3 432 snprintf(path, SQUID_MAXPATHLEN, "%s/netdb_state", storeSwapDir(0));
111b16cf 433 /*
434 * This was nicer when we were using stdio, but thanks to
435 * Solaris bugs, its a bad idea. fopen can fail if more than
436 * 256 FDs are open.
437 */
c4aefe96 438 fd = file_open(path, O_RDONLY | O_TEXT);
111b16cf 439 if (fd < 0)
440 return;
441 if (fstat(fd, &sb) < 0)
429fdbec 442 return;
111b16cf 443 t = buf = xcalloc(1, sb.st_size + 1);
444 l = read(fd, buf, sb.st_size);
445 file_close(fd);
446 if (l <= 0)
447 return;
448 while ((s = strchr(t, '\n'))) {
449 char *q;
450 assert(s - buf < l);
451 *s = '\0';
429fdbec 452 memset(&N, '\0', sizeof(netdbEntry));
111b16cf 453 q = strtok(t, w_space);
454 t = s + 1;
be55bf07 455 if (NULL == q)
456 continue;
111b16cf 457 if (!safe_inet_addr(q, &addr))
429fdbec 458 continue;
587d8445 459 if (netdbLookupAddr(addr) != NULL) /* no dups! */
460 continue;
111b16cf 461 if ((q = strtok(NULL, w_space)) == NULL)
429fdbec 462 continue;
111b16cf 463 N.pings_sent = atoi(q);
464 if ((q = strtok(NULL, w_space)) == NULL)
429fdbec 465 continue;
111b16cf 466 N.pings_recv = atoi(q);
429fdbec 467 if (N.pings_recv == 0)
468 continue;
469 /* give this measurement low weight */
470 N.pings_sent = 1;
471 N.pings_recv = 1;
111b16cf 472 if ((q = strtok(NULL, w_space)) == NULL)
429fdbec 473 continue;
111b16cf 474 N.hops = atof(q);
475 if ((q = strtok(NULL, w_space)) == NULL)
429fdbec 476 continue;
111b16cf 477 N.rtt = atof(q);
478 if ((q = strtok(NULL, w_space)) == NULL)
429fdbec 479 continue;
111b16cf 480 N.next_ping_time = (time_t) atoi(q);
481 if ((q = strtok(NULL, w_space)) == NULL)
429fdbec 482 continue;
111b16cf 483 N.last_use_time = (time_t) atoi(q);
7021844c 484 n = memAllocate(MEM_NETDBENTRY);
d6d7104a 485 xmemcpy(n, &N, sizeof(netdbEntry));
429fdbec 486 netdbHashInsert(n, addr);
111b16cf 487 while ((q = strtok(NULL, w_space)) != NULL) {
488 if (netdbLookupHost(q) != NULL) /* no dups! */
587d8445 489 continue;
111b16cf 490 netdbHostInsert(n, q);
587d8445 491 }
429fdbec 492 count++;
493 }
111b16cf 494 xfree(buf);
587d8445 495 getCurrentTime();
67f46679 496 debug(38, 1) ("NETDB state reloaded; %d entries, %d msec\n",
429fdbec 497 count, tvSubMsec(start, current_time));
498}
499
858783c9 500static const char *
429fdbec 501netdbPeerName(const char *name)
502{
858783c9 503 const wordlist *w;
429fdbec 504 for (w = peer_names; w; w = w->next) {
505 if (!strcmp(w->key, name))
506 return w->key;
507 }
858783c9 508 return wordlistAdd(&peer_names, name);
429fdbec 509}
510
45c54bdc 511static void
512netdbFreeNetdbEntry(void *data)
513{
514 netdbEntry *n = data;
515 safe_free(n->peers);
db1cd23c 516 memFree(n, MEM_NETDBENTRY);
45c54bdc 517}
429fdbec 518
45c54bdc 519static void
520netdbFreeNameEntry(void *data)
521{
522 net_db_name *x = data;
186477c1 523 xfree(x->hash.key);
db1cd23c 524 memFree(x, MEM_NET_DB_NAME);
45c54bdc 525}
526
527static void
528netdbExchangeHandleReply(void *data, char *buf, ssize_t size)
529{
530 netdbExchangeState *ex = data;
531 int rec_sz = 0;
532 off_t o;
533 struct in_addr addr;
534 double rtt;
535 double hops;
536 char *p;
537 int j;
538 HttpReply *rep;
539 size_t hdr_sz;
70305391 540 int nused = 0;
45c54bdc 541 rec_sz = 0;
542 rec_sz += 1 + sizeof(addr.s_addr);
543 rec_sz += 1 + sizeof(int);
544 rec_sz += 1 + sizeof(int);
545 ex->seen = ex->used + size;
67f46679 546 debug(38, 3) ("netdbExchangeHandleReply: %d bytes\n", (int) size);
45c54bdc 547 if (!cbdataValid(ex->p)) {
67f46679 548 debug(38, 3) ("netdbExchangeHandleReply: Peer became invalid\n");
45c54bdc 549 netdbExchangeDone(ex);
550 return;
551 }
67f46679 552 debug(38, 3) ("netdbExchangeHandleReply: for '%s:%d'\n", ex->p->host, ex->p->http_port);
45c54bdc 553 p = buf;
554 if (0 == ex->used) {
555 /* skip reply headers */
556 if ((hdr_sz = headersEnd(p, size))) {
67f46679 557 debug(38, 5) ("netdbExchangeHandleReply: hdr_sz = %d\n", hdr_sz);
45c54bdc 558 rep = ex->e->mem_obj->reply;
559 if (0 == rep->sline.status)
9bc73deb 560 httpReplyParse(rep, buf, hdr_sz);
67f46679 561 debug(38, 3) ("netdbExchangeHandleReply: reply status %d\n",
70305391 562 rep->sline.status);
45c54bdc 563 if (HTTP_OK != rep->sline.status) {
564 netdbExchangeDone(ex);
565 return;
566 }
567 assert(size >= hdr_sz);
568 ex->used += hdr_sz;
569 size -= hdr_sz;
570 p += hdr_sz;
571 } else {
572 size = 0;
573 }
574 }
67f46679 575 debug(38, 5) ("netdbExchangeHandleReply: start parsing loop, size = %d\n",
70305391 576 size);
45c54bdc 577 while (size >= rec_sz) {
67f46679 578 debug(38, 5) ("netdbExchangeHandleReply: in parsing loop, size = %d\n",
70305391 579 size);
45c54bdc 580 addr.s_addr = any_addr.s_addr;
581 hops = rtt = 0.0;
582 for (o = 0; o < rec_sz;) {
583 switch ((int) *(p + o)) {
584 case NETDB_EX_NETWORK:
585 o++;
586 xmemcpy(&addr.s_addr, p + o, sizeof(addr.s_addr));
587 o += sizeof(addr.s_addr);
588 break;
589 case NETDB_EX_RTT:
590 o++;
591 xmemcpy(&j, p + o, sizeof(int));
592 o += sizeof(int);
593 rtt = (double) ntohl(j) / 1000.0;
594 break;
595 case NETDB_EX_HOPS:
596 o++;
597 xmemcpy(&j, p + o, sizeof(int));
598 o += sizeof(int);
599 hops = (double) ntohl(j) / 1000.0;
600 break;
d20b1cd0 601 default:
602 debug(38, 1) ("netdbExchangeHandleReply: corrupt data, aborting\n");
603 netdbExchangeDone(ex);
604 return;
45c54bdc 605 }
606 }
607 if (addr.s_addr != any_addr.s_addr && rtt > 0)
608 netdbExchangeUpdatePeer(addr, ex->p, rtt, hops);
609 assert(o == rec_sz);
610 ex->used += rec_sz;
611 size -= rec_sz;
612 p += rec_sz;
009730dc 613 /*
614 * This is a fairly cpu-intensive loop, break after adding
615 * just a few
616 */
617 if (++nused == 20)
c43f5247 618 break;
45c54bdc 619 }
67f46679 620 debug(38, 3) ("netdbExchangeHandleReply: used %d entries, (x %d bytes) == %d bytes total\n",
70305391 621 nused, rec_sz, nused * rec_sz);
67f46679 622 debug(38, 3) ("netdbExchangeHandleReply: seen %d, used %d\n", ex->seen, ex->used);
b7fe0ab0 623 if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) {
624 debug(38, 3) ("netdbExchangeHandleReply: ENTRY_ABORTED\n");
ce9b54e8 625 netdbExchangeDone(ex);
626 } else if (ex->e->store_status == STORE_PENDING) {
67f46679 627 debug(38, 3) ("netdbExchangeHandleReply: STORE_PENDING\n");
06d2839d 628 storeClientCopy(ex->sc, ex->e, ex->seen, ex->used, ex->buf_sz,
45c54bdc 629 ex->buf, netdbExchangeHandleReply, ex);
70305391 630 } else if (ex->seen < ex->e->mem_obj->inmem_hi) {
67f46679 631 debug(38, 3) ("netdbExchangeHandleReply: ex->e->mem_obj->inmem_hi\n");
06d2839d 632 storeClientCopy(ex->sc, ex->e, ex->seen, ex->used, ex->buf_sz,
70305391 633 ex->buf, netdbExchangeHandleReply, ex);
634 } else {
67f46679 635 debug(38, 3) ("netdbExchangeHandleReply: Done\n");
70305391 636 netdbExchangeDone(ex);
45c54bdc 637 }
638}
639
640static void
641netdbExchangeDone(void *data)
642{
70305391 643 netdbExchangeState *ex = data;
67f46679 644 debug(38, 3) ("netdbExchangeDone: %s\n", storeUrl(ex->e));
db1cd23c 645 memFree(ex->buf, MEM_4K_BUF);
45c54bdc 646 requestUnlink(ex->r);
06d2839d 647 storeUnregister(ex->sc, ex->e, ex);
45c54bdc 648 storeUnlockObject(ex->e);
649 cbdataUnlock(ex->p);
650 cbdataFree(ex);
651}
429fdbec 652
e97f40f4 653#endif /* USE_ICMP */
654
655/* PUBLIC FUNCTIONS */
656
67508012 657void
e97f40f4 658netdbInit(void)
659{
660#if USE_ICMP
aa9e2cab 661 int n;
19054954 662 if (addr_table)
663 return;
aa9e2cab 664 n = hashPrime(Config.Netdb.high / 4);
665 addr_table = hash_create((HASHCMP *) strcmp, n, hash_string);
666 n = hashPrime(3 * Config.Netdb.high / 4);
667 host_table = hash_create((HASHCMP *) strcmp, n, hash_string);
52040193 668 eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1);
429fdbec 669 netdbReloadState();
22f3fd98 670 cachemgrRegister("netdb",
671 "Network Measurement Database",
1da3b90b 672 netdbDump, 0, 1);
e97f40f4 673#endif
674}
675
676void
0ee4272b 677netdbPingSite(const char *hostname)
67508012 678{
e97f40f4 679#if USE_ICMP
67508012 680 netdbEntry *n;
28c60158 681 generic_cbdata *h;
e97f40f4 682 if ((n = netdbLookupHost(hostname)) != NULL)
683 if (n->next_ping_time > squid_curtime)
684 return;
28c60158 685 h = CBDATA_ALLOC(generic_cbdata, NULL);
686 h->data = xstrdup(hostname);
0383c140 687 ipcache_nbgethostbyname(hostname, netdbSendPing, h);
e97f40f4 688#endif
67508012 689}
690
e97f40f4 691void
0ee4272b 692netdbHandlePingReply(const struct sockaddr_in *from, int hops, int rtt)
4d311579 693{
e97f40f4 694#if USE_ICMP
695 netdbEntry *n;
696 int N;
67f46679 697 debug(38, 3) ("netdbHandlePingReply: from %s\n", inet_ntoa(from->sin_addr));
e97f40f4 698 if ((n = netdbLookupAddr(from->sin_addr)) == NULL)
699 return;
429fdbec 700 N = ++n->pings_recv;
701 if (N > 5)
702 N = 5;
b6a2f15e 703 if (rtt < 1.0)
704 rtt = 1.0;
e97f40f4 705 n->hops = ((n->hops * (N - 1)) + hops) / N;
706 n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
67f46679 707 debug(38, 3) ("netdbHandlePingReply: %s; rtt=%5.1f hops=%4.1f\n",
e97f40f4 708 n->network,
709 n->rtt,
710 n->hops);
711#endif
4d311579 712}
e5f6c5c2 713
714void
715netdbFreeMemory(void)
716{
e97f40f4 717#if USE_ICMP
ec878047 718 hashFreeItems(addr_table, netdbFreeNetdbEntry);
e5f6c5c2 719 hashFreeMemory(addr_table);
afe95a7e 720 addr_table = NULL;
ec878047 721 hashFreeItems(host_table, netdbFreeNameEntry);
722 hashFreeMemory(host_table);
afe95a7e 723 host_table = NULL;
429fdbec 724 wordlistDestroy(&peer_names);
725 peer_names = NULL;
e97f40f4 726#endif
e5f6c5c2 727}
728
e97f40f4 729int
730netdbHops(struct in_addr addr)
731{
732#if USE_ICMP
733 netdbEntry *n = netdbLookupAddr(addr);
734 if (n && n->pings_recv) {
735 n->last_use_time = squid_curtime;
736 return (int) (n->hops + 0.5);
737 }
738#endif
739 return 256;
740}
741
742void
743netdbDump(StoreEntry * sentry)
744{
745#if USE_ICMP
746 netdbEntry *n;
747 netdbEntry **list;
429fdbec 748 net_db_name *x;
e97f40f4 749 int k;
750 int i;
429fdbec 751 int j;
752 net_db_peer *p;
15576b6a 753 storeAppendPrintf(sentry, "Network DB Statistics:\n");
754 storeAppendPrintf(sentry, "%-16.16s %9s %7s %5s %s\n",
0ee4272b 755 "Network",
756 "recv/sent",
757 "RTT",
758 "Hops",
759 "Hostnames");
587d8445 760 list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
0ee4272b 761 i = 0;
0f6bebac 762 hash_first(addr_table);
763 while ((n = (netdbEntry *) hash_next(addr_table)))
0ee4272b 764 *(list + i++) = n;
587d8445 765 if (i != memInUse(MEM_NETDBENTRY))
67f46679 766 debug(38, 0) ("WARNING: netdb_addrs count off, found %d, expected %d\n",
587d8445 767 i, memInUse(MEM_NETDBENTRY));
0ee4272b 768 qsort((char *) list,
769 i,
770 sizeof(netdbEntry *),
79d39a72 771 sortByRtt);
0ee4272b 772 for (k = 0; k < i; k++) {
773 n = *(list + k);
15576b6a 774 storeAppendPrintf(sentry, "%-16.16s %4d/%4d %7.1f %5.1f",
0ee4272b 775 n->network,
776 n->pings_recv,
777 n->pings_sent,
778 n->rtt,
779 n->hops);
780 for (x = n->hosts; x; x = x->next)
186477c1 781 storeAppendPrintf(sentry, " %s", hashKeyStr(&x->hash));
1519582e 782 storeAppendPrintf(sentry, "\n");
429fdbec 783 p = n->peers;
784 for (j = 0; j < n->n_peers; j++, p++) {
15576b6a 785 storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n",
429fdbec 786 p->peername,
787 p->rtt,
788 p->hops);
789 }
0ee4272b 790 }
0ee4272b 791 xfree(list);
c8391077 792#else
793 storeAppendPrintf(sentry,
794 "NETDB support not compiled into this Squid cache.\n");
e97f40f4 795#endif
796}
48f44632 797
798int
799netdbHostHops(const char *host)
800{
801#if USE_ICMP
802 netdbEntry *n = netdbLookupHost(host);
429fdbec 803 if (n) {
804 n->last_use_time = squid_curtime;
48f44632 805 return (int) (n->hops + 0.5);
429fdbec 806 }
48f44632 807#endif
429fdbec 808 return 0;
48f44632 809}
810
811int
812netdbHostRtt(const char *host)
813{
814#if USE_ICMP
815 netdbEntry *n = netdbLookupHost(host);
429fdbec 816 if (n) {
817 n->last_use_time = squid_curtime;
48f44632 818 return (int) (n->rtt + 0.5);
429fdbec 819 }
820#endif
821 return 0;
822}
823
1d6ae62d 824void
825netdbHostData(const char *host, int *samp, int *rtt, int *hops)
826{
827#if USE_ICMP
5942e8d4 828 netdbEntry *n = netdbLookupHost(host);
829 if (n == NULL)
830 return;
831 *samp = n->pings_recv;
832 *rtt = (int) (n->rtt + 0.5);
833 *hops = (int) (n->hops + 0.5);
1d6ae62d 834#endif
835}
836
ee48bff0 837int
9ef28b60 838netdbHostPeerRtt(const char *host, peer * p)
ee48bff0 839{
840#if USE_ICMP
e6acd170 841 const netdbEntry *n = netdbLookupHost(host);
842 if (n) {
9ef28b60 843 const net_db_peer *np = netdbPeerByName(n, p->host);
844 if (np && np->expires >= squid_curtime)
845 return (int) (np->rtt + 0.5);
ee48bff0 846 }
847#endif
848 return 0;
849}
850
429fdbec 851void
852netdbUpdatePeer(request_t * r, peer * e, int irtt, int ihops)
853{
854#if USE_ICMP
855 netdbEntry *n;
856 double rtt = (double) irtt;
857 double hops = (double) ihops;
858 net_db_peer *p;
67f46679 859 debug(38, 3) ("netdbUpdatePeer: '%s', %d hops, %d rtt\n", r->host, ihops, irtt);
429fdbec 860 n = netdbLookupHost(r->host);
861 if (n == NULL) {
67f46679 862 debug(38, 3) ("netdbUpdatePeer: host '%s' not found\n", r->host);
429fdbec 863 return;
864 }
865 if ((p = netdbPeerByName(n, e->host)) == NULL)
866 p = netdbPeerAdd(n, e);
867 p->rtt = rtt;
868 p->hops = hops;
869 p->expires = squid_curtime + 3600;
870 if (n->n_peers < 2)
871 return;
872 qsort((char *) n->peers,
873 n->n_peers,
874 sizeof(net_db_peer),
79d39a72 875 sortPeerByRtt);
48f44632 876#endif
48f44632 877}
ce75f381 878
27efd484 879void
880netdbExchangeUpdatePeer(struct in_addr addr, peer * e, double rtt, double hops)
881{
882#if USE_ICMP
883 netdbEntry *n;
884 net_db_peer *p;
67f46679 885 debug(38, 5) ("netdbExchangeUpdatePeer: '%s', %0.1f hops, %0.1f rtt\n",
27efd484 886 inet_ntoa(addr), hops, rtt);
887 n = netdbLookupAddr(addr);
888 if (n == NULL)
889 n = netdbAdd(addr);
890 assert(NULL != n);
891 if ((p = netdbPeerByName(n, e->host)) == NULL)
892 p = netdbPeerAdd(n, e);
893 p->rtt = rtt;
894 p->hops = hops;
895 p->expires = squid_curtime + 3600; /* XXX ? */
896 if (n->n_peers < 2)
897 return;
898 qsort((char *) n->peers,
899 n->n_peers,
900 sizeof(net_db_peer),
901 sortPeerByRtt);
902#endif
903}
904
587d8445 905void
906netdbDeleteAddrNetwork(struct in_addr addr)
907{
908#if USE_ICMP
909 netdbEntry *n = netdbLookupAddr(addr);
910 if (n == NULL)
911 return;
fd9f2966 912 debug(38, 3) ("netdbDeleteAddrNetwork: %s\n", n->network);
587d8445 913 netdbRelease(n);
914#endif
915}
de2a0782 916
917void
918netdbBinaryExchange(StoreEntry * s)
919{
de2a0782 920 http_reply *reply = s->mem_obj->reply;
ccf44862 921 http_version_t version;
9ad1cbca 922#if USE_ICMP
de2a0782 923 netdbEntry *n;
de2a0782 924 int i;
925 int j;
926 int rec_sz;
927 char *buf;
928 struct in_addr addr;
929 storeBuffer(s);
930 httpReplyReset(reply);
1c867fef 931 httpBuildVersion(&version, 1, 0);
ccf44862 932 httpReplySetHeaders(reply, version, HTTP_OK, "OK",
de2a0782 933 NULL, -1, squid_curtime, -2);
934 httpReplySwapOut(reply, s);
935 rec_sz = 0;
936 rec_sz += 1 + sizeof(addr.s_addr);
937 rec_sz += 1 + sizeof(int);
938 rec_sz += 1 + sizeof(int);
939 buf = memAllocate(MEM_4K_BUF);
940 i = 0;
0f6bebac 941 hash_first(addr_table);
942 while ((n = (netdbEntry *) hash_next(addr_table))) {
de2a0782 943 if (0.0 == n->rtt)
944 continue;
67a863b6 945 if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */
946 continue;
de2a0782 947 if (!safe_inet_addr(n->network, &addr))
948 continue;
9ad1cbca 949 buf[i++] = (char) NETDB_EX_NETWORK;
de2a0782 950 xmemcpy(&buf[i], &addr.s_addr, sizeof(addr.s_addr));
951 i += sizeof(addr.s_addr);
9ad1cbca 952 buf[i++] = (char) NETDB_EX_RTT;
953 j = htonl((int) (n->rtt * 1000));
de2a0782 954 xmemcpy(&buf[i], &j, sizeof(int));
955 i += sizeof(int);
9ad1cbca 956 buf[i++] = (char) NETDB_EX_HOPS;
957 j = htonl((int) (n->hops * 1000));
de2a0782 958 xmemcpy(&buf[i], &j, sizeof(int));
959 i += sizeof(int);
84075e8f 960 if (i + rec_sz > 4096) {
de2a0782 961 storeAppend(s, buf, i);
962 i = 0;
963 }
964 }
84075e8f 965 if (i > 0) {
966 storeAppend(s, buf, i);
967 i = 0;
968 }
de2a0782 969 assert(0 == i);
970 storeBufferFlush(s);
db1cd23c 971 memFree(buf, MEM_4K_BUF);
de2a0782 972#else
9ad1cbca 973 httpReplyReset(reply);
1c867fef 974 httpBuildVersion(&version, 1, 0);
ccf44862 975 httpReplySetHeaders(reply, version, HTTP_BAD_REQUEST, "Bad Request",
9ad1cbca 976 NULL, -1, squid_curtime, -2);
de2a0782 977 storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
978#endif
9ad1cbca 979 storeComplete(s);
980}
981
28c60158 982#if USE_ICMP
983CBDATA_TYPE(netdbExchangeState);
984#endif
985
9ad1cbca 986void
987netdbExchangeStart(void *data)
988{
45c54bdc 989#if USE_ICMP
a74c5601 990 peer *p = data;
991 char *uri;
28c60158 992 netdbExchangeState *ex;
993 CBDATA_INIT_TYPE(netdbExchangeState);
994 ex = CBDATA_ALLOC(netdbExchangeState, NULL);
a74c5601 995 cbdataLock(p);
996 ex->p = p;
997 uri = internalRemoteUri(p->host, p->http_port, "/squid-internal-dynamic/", "netdb");
fd9f2966 998 debug(38, 3) ("netdbExchangeStart: Requesting '%s'\n", uri);
a74c5601 999 assert(NULL != uri);
1000 ex->r = urlParse(METHOD_GET, uri);
1001 if (NULL == ex->r) {
67f46679 1002 debug(38, 1) ("netdbExchangeStart: Bad URI %s\n", uri);
a74c5601 1003 return;
1004 }
1005 requestLink(ex->r);
1006 assert(NULL != ex->r);
1c867fef 1007 httpBuildVersion(&ex->r->http_ver, 1, 0);
92695e5e 1008 ex->e = storeCreateEntry(uri, uri, null_request_flags, METHOD_GET);
d20b1cd0 1009 ex->buf_sz = 4096;
bf32c208 1010 ex->buf = memAllocate(MEM_4K_BUF);
a74c5601 1011 assert(NULL != ex->e);
06d2839d 1012 ex->sc = storeClientListAdd(ex->e, ex);
1013 storeClientCopy(ex->sc, ex->e, ex->seen, ex->used, ex->buf_sz,
a74c5601 1014 ex->buf, netdbExchangeHandleReply, ex);
92695e5e 1015 ex->r->flags.loopdetect = 1; /* cheat! -- force direct */
9bc73deb 1016 if (p->login)
1017 xstrncpy(ex->r->login, p->login, MAX_LOGIN_SZ);
7e3ce7b9 1018 fwdStart(-1, ex->e, ex->r);
45c54bdc 1019#endif
de2a0782 1020}
69c95dd3 1021
1022peer *
5999b776 1023netdbClosestParent(request_t * request)
69c95dd3 1024{
69c95dd3 1025#if USE_ICMP
f720985e 1026 peer *p = NULL;
69c95dd3 1027 netdbEntry *n;
1028 const ipcache_addrs *ia;
1029 net_db_peer *h;
1030 int i;
8ff4505b 1031 n = netdbLookupHost(request->host);
69c95dd3 1032 if (NULL == n) {
1033 /* try IP addr */
8ff4505b 1034 ia = ipcache_gethostbyname(request->host, 0);
69c95dd3 1035 if (NULL != ia)
1036 n = netdbLookupAddr(ia->in_addrs[ia->cur]);
1037 }
1038 if (NULL == n)
1039 return NULL;
1040 if (0 == n->n_peers)
1041 return NULL;
1042 /*
1043 * Find the parent with the least RTT to the origin server.
1044 * Make sure we don't return a parent who is farther away than
1045 * we are. Note, the n->peers list is pre-sorted by RTT.
1046 */
1047 for (i = 0; i < n->n_peers; i++) {
1048 h = &n->peers[i];
1049 if (n->rtt > 0)
1050 if (n->rtt < h->rtt)
1051 break;
8ff4505b 1052 p = peerFindByName(h->peername);
5999b776 1053 if (NULL == p) /* not found */
8ff4505b 1054 continue;
39b80cd0 1055 if (neighborType(p, request) != PEER_PARENT)
1056 continue;
8ff4505b 1057 if (!peerHTTPOkay(p, request)) /* not allowed */
1058 continue;
1059 return p;
69c95dd3 1060 }
1061#endif
1062 return NULL;
1063}