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