net_db.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 38 Network Measurement Database */
10
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
19#include "squid.h"
20#include "CachePeer.h"
21#include "CachePeers.h"
22#include "cbdata.h"
23#include "event.h"
24#include "fde.h"
25#include "fs_io.h"
26#include "FwdState.h"
27#include "HttpReply.h"
28#include "icmp/net_db.h"
29#include "internal.h"
30#include "ip/Address.h"
31#include "log/File.h"
32#include "MemObject.h"
33#include "mgr/Registration.h"
34#include "mime_header.h"
35#include "neighbors.h"
36#include "PeerSelectState.h"
37#include "sbuf/SBuf.h"
38#include "SquidConfig.h"
39#include "Store.h"
40#include "StoreClient.h"
41#include "tools.h"
42#include "wordlist.h"
43
44#if HAVE_SYS_STAT_H
45#include <sys/stat.h>
46#endif
47
48#if USE_ICMP
49#include "icmp/IcmpSquid.h"
50#include "ipcache.h"
51#include "StoreClient.h"
52
53typedef enum {
58
60{
62
63public:
65 p(aPeer),
66 r(theReq)
67 {
68 assert(r);
69 // TODO: check if we actually need to do this. should be implicit
71 }
72
74 debugs(38, 3, e->url());
75 storeUnregister(sc, e, this);
76 e->unlock("netdbExchangeDone");
77 }
78
80 StoreEntry *e = nullptr;
81 store_client *sc = nullptr;
83
86
88};
89
91
92static hash_table *addr_table = nullptr;
93static hash_table *host_table = nullptr;
94
96static void netdbRelease(netdbEntry * n);
97
98static void netdbHashInsert(netdbEntry * n, Ip::Address &addr);
99static void netdbHashDelete(const char *key);
100static void netdbHostInsert(netdbEntry * n, const char *hostname);
101static void netdbHostDelete(const net_db_name * x);
102static void netdbPurgeLRU(void);
103static netdbEntry *netdbLookupHost(const char *key);
104static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *);
106static const char *netdbPeerName(const char *name);
109
110/* We have to keep a local list of CachePeer names. The Peers structure
111 * gets freed during a reconfigure. We want this database to
112 * remain persisitent, so _net_db_peer->peername points into this
113 * linked list */
114static wordlist *peer_names = nullptr;
115
116static void
118{
120 n->key = n->network;
121 assert(hash_lookup(addr_table, n->network) == nullptr);
123}
124
125static void
126netdbHashDelete(const char *key)
127{
128 hash_link *hptr = (hash_link *)hash_lookup(addr_table, key);
129
130 if (hptr == nullptr) {
131 debug_trap("netdbHashDelete: key not found");
132 return;
133 }
134
136}
137
138net_db_name::net_db_name(const char *hostname, netdbEntry *e) :
139 next(e ? e->hosts : nullptr),
140 net_db_entry(e)
141{
142 key = xstrdup(hostname);
143 if (e) {
144 e->hosts = this;
145 ++ e->link_count;
146 }
147}
148
149static void
150netdbHostInsert(netdbEntry * n, const char *hostname)
151{
152 net_db_name *x = new net_db_name(hostname, n);
153 assert(hash_lookup(host_table, hostname) == nullptr);
155}
156
157static void
159{
160 assert(x != nullptr);
161 assert(x->net_db_entry != nullptr);
162
163 netdbEntry *n = x->net_db_entry;
164 -- n->link_count;
165
166 for (auto **X = &n->hosts; *X; X = &(*X)->next) {
167 if (*X == x) {
168 *X = x->next;
169 break;
170 }
171 }
172
174 delete x;
175}
176
177static netdbEntry *
178netdbLookupHost(const char *key)
179{
181 return x ? x->net_db_entry : nullptr;
182}
183
184static void
186{
187 net_db_name *x;
188 net_db_name *next;
189
190 for (x = n->hosts; x; x = next) {
191 next = x->next;
193 }
194
195 n->hosts = nullptr;
196 safe_free(n->peers);
197 n->peers = nullptr;
198 n->n_peers = 0;
199 n->n_peers_alloc = 0;
200
201 if (n->link_count == 0) {
203 delete n;
204 }
205}
206
207static int
208netdbLRU(const void *A, const void *B)
209{
210 const netdbEntry *const *n1 = (const netdbEntry *const *)A;
211 const netdbEntry *const *n2 = (const netdbEntry *const *)B;
212
213 if ((*n1)->last_use_time > (*n2)->last_use_time)
214 return (1);
215
216 if ((*n1)->last_use_time < (*n2)->last_use_time)
217 return (-1);
218
219 return (0);
220}
221
222static void
224{
225 netdbEntry *n;
226 netdbEntry **list;
227 int k = 0;
228 int list_count = 0;
229 list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *));
231
232 while ((n = (netdbEntry *) hash_next(addr_table))) {
233 assert(list_count < netdbEntry::UseCount());
234 *(list + list_count) = n;
235 ++list_count;
236 }
237
238 qsort((char *) list,
239 list_count,
240 sizeof(netdbEntry *),
241 netdbLRU);
242
243 for (k = 0; k < list_count; ++k) {
244 if (netdbEntry::UseCount() < Config.Netdb.low)
245 break;
246
247 netdbRelease(*(list + k));
248 }
249
250 xfree(list);
251}
252
253static netdbEntry *
255{
256 netdbEntry *n;
257 char *key = new char[MAX_IPSTRLEN];
259 n = (netdbEntry *) hash_lookup(addr_table, key);
260 delete[] key;
261 return n;
262}
263
264static netdbEntry *
266{
267 netdbEntry *n;
268
269 if (netdbEntry::UseCount() > Config.Netdb.high)
271
272 if ((n = netdbLookupAddr(addr)) == nullptr) {
273 n = new netdbEntry;
274 netdbHashInsert(n, addr);
275 }
276
277 return n;
278}
279
280static void
281netdbSendPing(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
282{
283 Ip::Address addr;
284 char *hostname = nullptr;
285 static_cast<generic_cbdata *>(data)->unwrap(&hostname);
286 netdbEntry *n;
287 netdbEntry *na;
288 net_db_name *x;
289 net_db_name **X;
290
291 if (ia == nullptr) {
292 xfree(hostname);
293 return;
294 }
295
296 addr = ia->current();
297
298 if ((n = netdbLookupHost(hostname)) == nullptr) {
299 n = netdbAdd(addr);
300 netdbHostInsert(n, hostname);
301 } else if ((na = netdbLookupAddr(addr)) != n) {
302 /*
303 *hostname moved from 'network n' to 'network na'!
304 */
305
306 if (na == nullptr)
307 na = netdbAdd(addr);
308
309 debugs(38, 3, "netdbSendPing: " << hostname << " moved from " << n->network << " to " << na->network);
310
311 x = (net_db_name *) hash_lookup(host_table, hostname);
312
313 if (x == nullptr) {
314 debugs(38, DBG_IMPORTANT, "ERROR: Squid BUG: net_db_name list bug: " << hostname << " not found");
315 xfree(hostname);
316 return;
317 }
318
319 /* remove net_db_name from 'network n' linked list */
320 for (X = &n->hosts; *X; X = &(*X)->next) {
321 if (*X == x) {
322 *X = x->next;
323 break;
324 }
325 }
326
327 -- n->link_count;
328 /* point to 'network na' from host entry */
329 x->net_db_entry = na;
330 /* link net_db_name to 'network na' */
331 x->next = na->hosts;
332 na->hosts = x;
333 ++ na->link_count;
334 n = na;
335 }
336
337 if (n->next_ping_time <= squid_curtime) {
338 debugs(38, 3, "netdbSendPing: pinging " << hostname);
339 icmpEngine.DomainPing(addr, hostname);
340 ++ n->pings_sent;
343 }
344
345 xfree(hostname);
346}
347
350{
351 Ip::Address out;
352
353 out = in;
354
355 /* in IPv6 the 'network' should be the routing section. */
356 if ( in.isIPv6() ) {
357 out.applyMask(64, AF_INET6);
358 debugs(14, 5, "networkFromInaddr : Masked IPv6 Address to " << in << "/64 routing part.");
359 return out;
360 }
361
362#if USE_CLASSFUL
363 struct in_addr b;
364
365 in.getInAddr(b);
366
367 if (IN_CLASSC(b.s_addr))
368 b.s_addr &= IN_CLASSC_NET;
369 else if (IN_CLASSB(b.s_addr))
370 b.s_addr &= IN_CLASSB_NET;
371 else if (IN_CLASSA(b.s_addr))
372 b.s_addr &= IN_CLASSA_NET;
373
374 out = b;
375
376#endif
377
378 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << out << "/24.");
379
380 /* use /24 for everything under IPv4 */
381 out.applyMask(24, AF_INET);
382 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << in << "/24.");
383
384 return out;
385}
386
387static int
388sortByRtt(const void *A, const void *B)
389{
390 const netdbEntry *const *n1 = (const netdbEntry *const *)A;
391 const netdbEntry *const *n2 = (const netdbEntry *const *)B;
392
393 if ((*n1)->rtt > (*n2)->rtt)
394 return 1;
395 else if ((*n1)->rtt < (*n2)->rtt)
396 return -1;
397 else
398 return 0;
399}
400
401static net_db_peer *
402netdbPeerByName(const netdbEntry * n, const char *peername)
403{
404 int i;
405 net_db_peer *p = n->peers;
406
407 for (i = 0; i < n->n_peers; ++i, ++p) {
408 if (!strcmp(p->peername, peername))
409 return p;
410 }
411
412 return nullptr;
413}
414
415static net_db_peer *
417{
418 net_db_peer *p;
419 net_db_peer *o;
420 int osize;
421 int i;
422
423 if (n->n_peers == n->n_peers_alloc) {
424 o = n->peers;
425 osize = n->n_peers_alloc;
426
427 if (n->n_peers_alloc == 0)
428 n->n_peers_alloc = 2;
429 else
430 n->n_peers_alloc <<= 1;
431
432 debugs(38, 3, "netdbPeerAdd: Growing peer list for '" << n->network << "' to " << n->n_peers_alloc);
433
435
436 for (i = 0; i < osize; ++i)
437 *(n->peers + i) = *(o + i);
438
439 if (osize) {
440 safe_free(o);
441 }
442 }
443
444 p = n->peers + n->n_peers;
445 p->peername = netdbPeerName(e->host);
446 ++ n->n_peers;
447 return p;
448}
449
450static int
451sortPeerByRtt(const void *A, const void *B)
452{
453 const net_db_peer *p1 = (net_db_peer *)A;
454 const net_db_peer *p2 = (net_db_peer *)B;
455
456 if (p1->rtt > p2->rtt)
457 return 1;
458 else if (p1->rtt < p2->rtt)
459 return -1;
460 else
461 return 0;
462}
463
464static void
466{
467 if (strcmp(Config.netdbFilename, "none") == 0)
468 return;
469
470 Logfile *lf;
471 netdbEntry *n;
472 net_db_name *x;
473
474 struct timeval start = current_time;
475 int count = 0;
476 /*
477 * This was nicer when we were using stdio, but thanks to
478 * Solaris bugs, its a bad idea. fopen can fail if more than
479 * 256 FDs are open.
480 */
481 /*
482 * unlink() is here because there is currently no way to make
483 * logfileOpen() use O_TRUNC.
484 */
485 unlink(Config.netdbFilename);
486 lf = logfileOpen(Config.netdbFilename, 4096, 0);
487
488 if (!lf) {
489 int xerrno = errno;
490 debugs(50, DBG_IMPORTANT, MYNAME << Config.netdbFilename << ": " << xstrerr(xerrno));
491 return;
492 }
493
495
496 while ((n = (netdbEntry *) hash_next(addr_table))) {
497 if (n->pings_recv == 0)
498 continue;
499
500 logfilePrintf(lf, "%s %d %d %10.5f %10.5f %d %d",
501 n->network,
502 n->pings_sent,
503 n->pings_recv,
504 n->hops,
505 n->rtt,
506 (int) n->next_ping_time,
507 (int) n->last_use_time);
508
509 for (x = n->hosts; x; x = x->next)
510 logfilePrintf(lf, " %s", hashKeyStr(x));
511
512 logfilePrintf(lf, "\n");
513
514 ++count;
515
516#undef RBUF_SZ
517
518 }
519
520 logfileClose(lf);
522 debugs(38, DBG_IMPORTANT, "NETDB state saved; " <<
523 count << " entries, " <<
524 tvSubMsec(start, current_time) << " msec" );
525 eventAddIsh("netdbSaveState", netdbSaveState, nullptr, 3600.0, 1);
526}
527
528static void
530{
531 if (strcmp(Config.netdbFilename, "none") == 0)
532 return;
533
534 char *s;
535 int fd;
536 int l;
537
538 struct stat sb;
539 netdbEntry *n;
540 netdbEntry N;
541
542 Ip::Address addr;
543 int count = 0;
544
545 struct timeval start = current_time;
546 /*
547 * This was nicer when we were using stdio, but thanks to
548 * Solaris bugs, its a bad idea. fopen can fail if more than
549 * 256 FDs are open.
550 */
551 fd = file_open(Config.netdbFilename, O_RDONLY | O_BINARY);
552
553 if (fd < 0)
554 return;
555
556 if (fstat(fd, &sb) < 0) {
557 file_close(fd);
558 return;
559 }
560
561 char *t;
562 char *buf = (char *)xcalloc(1, sb.st_size + 1);
563 t = buf;
564 l = FD_READ_METHOD(fd, buf, sb.st_size);
565 file_close(fd);
566
567 if (l <= 0) {
568 safe_free (buf);
569 return;
570 };
571
572 while ((s = strchr(t, '\n'))) {
573 char *q;
574 assert(s - buf < l);
575 *s = '\0';
576 N = netdbEntry();
577 q = strtok(t, w_space);
578 t = s + 1;
579
580 if (nullptr == q)
581 continue;
582
583 if (! (addr = q) )
584 continue;
585
586 if (netdbLookupAddr(addr) != nullptr) /* no dups! */
587 continue;
588
589 if ((q = strtok(nullptr, w_space)) == nullptr)
590 continue;
591
592 N.pings_sent = atoi(q);
593
594 if ((q = strtok(nullptr, w_space)) == nullptr)
595 continue;
596
597 N.pings_recv = atoi(q);
598
599 if (N.pings_recv == 0)
600 continue;
601
602 /* give this measurement low weight */
603 N.pings_sent = 1;
604
605 N.pings_recv = 1;
606
607 if ((q = strtok(nullptr, w_space)) == nullptr)
608 continue;
609
610 N.hops = atof(q);
611
612 if ((q = strtok(nullptr, w_space)) == nullptr)
613 continue;
614
615 N.rtt = atof(q);
616
617 if ((q = strtok(nullptr, w_space)) == nullptr)
618 continue;
619
620 N.next_ping_time = (time_t) atoi(q);
621
622 if ((q = strtok(nullptr, w_space)) == nullptr)
623 continue;
624
625 N.last_use_time = (time_t) atoi(q);
626
627 n = new netdbEntry;
628
629 memcpy(n, &N, sizeof(netdbEntry));
630
631 netdbHashInsert(n, addr);
632
633 while ((q = strtok(nullptr, w_space)) != nullptr) {
634 if (netdbLookupHost(q) != nullptr) /* no dups! */
635 continue;
636
637 netdbHostInsert(n, q);
638 }
639
640 ++count;
641 }
642
643 xfree(buf);
645 debugs(38, DBG_IMPORTANT, "NETDB state reloaded; " <<
646 count << " entries, " <<
647 tvSubMsec(start, current_time) << " msec" );
648}
649
650static const char *
651netdbPeerName(const char *name)
652{
653 const wordlist *w;
654
655 for (w = peer_names; w; w = w->next) {
656 if (!strcmp(w->key, name))
657 return w->key;
658 }
659
660 return wordlistAdd(&peer_names, name);
661}
662
663static void
665{
666 Ip::Address addr;
667
669
670 struct in_addr line_addr;
671 double rtt;
672 double hops;
673 int j;
674 int nused = 0;
675
676 size_t rec_sz = 0; // received record size (TODO: make const)
677 rec_sz += 1 + sizeof(struct in_addr);
678 rec_sz += 1 + sizeof(int);
679 rec_sz += 1 + sizeof(int);
680 // to make progress without growing buffer space, we must parse at least one record per call
681 Assure(rec_sz <= ex->parsingBuffer.capacity());
682 debugs(38, 3, "netdbExchangeHandleReply: " << receivedData.length << " read bytes");
683
684 if (!ex->p.valid()) {
685 debugs(38, 3, "netdbExchangeHandleReply: Peer became invalid");
686 delete ex;
687 return;
688 }
689
690 debugs(38, 3, "for " << *ex->p);
691
692 if (receivedData.flags.error) {
693 delete ex;
694 return;
695 }
696
697 if (ex->connstate == STATE_HEADER) {
698 const auto scode = ex->e->mem().baseReply().sline.status();
699 assert(scode != Http::scNone);
700 debugs(38, 3, "reply status " << scode);
701 if (scode != Http::scOkay) {
702 delete ex;
703 return;
704 }
705 ex->connstate = STATE_BODY;
706 }
707
709
710 ex->parsingBuffer.appended(receivedData.data, receivedData.length);
711 auto p = ex->parsingBuffer.c_str(); // current parsing position
712 auto size = ex->parsingBuffer.contentSize(); // bytes we still need to parse
713
714 /* If we get here, we have some body to parse .. */
715 debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size);
716
717 while (size >= rec_sz) {
718 debugs(38, 5, "netdbExchangeHandleReply: in parsing loop, size = " << size);
719 addr.setAnyAddr();
720 hops = rtt = 0.0;
721
722 size_t o; // current record parsing offset
723 for (o = 0; o < rec_sz;) {
724 switch ((int) *(p + o)) {
725
726 case NETDB_EX_NETWORK:
727 ++o;
728 // XXX: NetDB can still only send IPv4
729 memcpy(&line_addr, p + o, sizeof(struct in_addr));
730 addr = line_addr;
731 o += sizeof(struct in_addr);
732 break;
733
734 case NETDB_EX_RTT:
735 ++o;
736 memcpy(&j, p + o, sizeof(int));
737 o += sizeof(int);
738 rtt = (double) ntohl(j) / 1000.0;
739 break;
740
741 case NETDB_EX_HOPS:
742 ++o;
743 memcpy(&j, p + o, sizeof(int));
744 o += sizeof(int);
745 hops = (double) ntohl(j) / 1000.0;
746 break;
747
748 default:
749 debugs(38, DBG_IMPORTANT, "ERROR: netdbExchangeHandleReply: corrupt data, aborting");
750 delete ex;
751 return;
752 }
753 }
754
755 if (!addr.isAnyAddr() && rtt > 0)
756 netdbExchangeUpdatePeer(addr, ex->p.get(), rtt, hops);
757
758 assert(o == rec_sz);
759
760 size -= rec_sz;
761
762 p += rec_sz;
763
764 ++nused;
765 }
766
767 const auto parsedSize = ex->parsingBuffer.contentSize() - size;
768 ex->parsingBuffer.consume(parsedSize);
769
770 debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size << " bytes");
771
772 debugs(38, 3, "netdbExchangeHandleReply: used " << nused <<
773 " entries, (x " << rec_sz << " bytes) == " << nused * rec_sz <<
774 " bytes total");
775
776 if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) {
777 debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED");
778 delete ex;
779 return;
780 }
781
782 if (ex->sc->atEof()) {
783 if (const auto leftoverBytes = ex->parsingBuffer.contentSize())
784 debugs(38, 2, "discarding a partially received record due to Store EOF: " << leftoverBytes);
785 delete ex;
786 return;
787 }
788
789 // TODO: To protect us from a broken peer sending an "infinite" stream of
790 // new addresses, limit the cumulative number of received bytes or records?
791
792 const auto remainingSpace = ex->parsingBuffer.space().positionAt(receivedData.offset + receivedData.length);
793 // rec_sz is at most buffer capacity, and we consume all fully loaded records
794 Assure(remainingSpace.length);
795 storeClientCopy(ex->sc, ex->e, remainingSpace, netdbExchangeHandleReply, ex);
796}
797
798#endif /* USE_ICMP */
799
800/* PUBLIC FUNCTIONS */
801
802void
804{
805#if USE_ICMP
806 Mgr::RegisterAction("netdb", "Network Measurement Database", netdbDump, 0, 1);
807
808 if (addr_table)
809 return;
810
811 int n = hashPrime(Config.Netdb.high / 4);
812
813 addr_table = hash_create((HASHCMP *) strcmp, n, hash_string);
814
815 n = hashPrime(3 * Config.Netdb.high / 4);
816
817 host_table = hash_create((HASHCMP *) strcmp, n, hash_string);
818
819 eventAddIsh("netdbSaveState", netdbSaveState, nullptr, 3600.0, 1);
820
822
823#endif
824}
825
826void
827netdbPingSite(const char *hostname)
828{
829#if USE_ICMP
830 netdbEntry *n;
831
832 if ((n = netdbLookupHost(hostname)) != nullptr)
834 return;
835
837 new generic_cbdata(xstrdup(hostname)));
838#else
839 (void)hostname;
840#endif
841}
842
843void
844netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
845{
846#if USE_ICMP
847 netdbEntry *n;
848 int N;
849 debugs(38, 3, "netdbHandlePingReply: from " << from);
850
851 if ((n = netdbLookupAddr(from)) == nullptr)
852 return;
853
854 N = ++n->pings_recv;
855
856 if (N > 5)
857 N = 5;
858
859 if (rtt < 1)
860 rtt = 1;
861
862 n->hops = ((n->hops * (N - 1)) + hops) / N;
863
864 n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
865
866 debugs(38, 3, "netdbHandlePingReply: " << n->network << "; rtt="<<
867 std::setw(5)<< std::setprecision(2) << n->rtt << " hops="<<
868 std::setw(4) << n->hops);
869#else
870 (void)from;
871 (void)hops;
872 (void)rtt;
873#endif
874}
875
876void
878{
879#if USE_ICMP
880 netdbEntry *n;
881 netdbEntry **list;
882 net_db_name *x;
883 int k;
884 int i;
885 int j;
886 net_db_peer *p;
887 storeAppendPrintf(sentry, "Network DB Statistics:\n");
888 storeAppendPrintf(sentry, "%-46.46s %9s %7s %5s %s\n", /* Max between 16 (IPv4) or 46 (IPv6) */
889 "Network",
890 "recv/sent",
891 "RTT",
892 "Hops",
893 "Hostnames");
894 list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *));
895 i = 0;
897
898 while ((n = (netdbEntry *) hash_next(addr_table))) {
899 *(list + i) = n;
900 ++i;
901 }
902
903 if (i != netdbEntry::UseCount())
904 debugs(38, DBG_CRITICAL, "WARNING: netdb_addrs count off, found " << i <<
905 ", expected " << netdbEntry::UseCount());
906
907 qsort((char *) list,
908 i,
909 sizeof(netdbEntry *),
910 sortByRtt);
911
912 for (k = 0; k < i; ++k) {
913 n = *(list + k);
914 storeAppendPrintf(sentry, "%-46.46s %4d/%4d %7.1f %5.1f", /* Max between 16 (IPv4) or 46 (IPv6) */
915 n->network,
916 n->pings_recv,
917 n->pings_sent,
918 n->rtt,
919 n->hops);
920
921 for (x = n->hosts; x; x = x->next)
922 storeAppendPrintf(sentry, " %s", hashKeyStr(x));
923
924 storeAppendPrintf(sentry, "\n");
925
926 p = n->peers;
927
928 for (j = 0; j < n->n_peers; ++j, ++p) {
929 storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n",
930 p->peername,
931 p->rtt,
932 p->hops);
933 }
934 }
935
936 xfree(list);
937#else
938
939 storeAppendPrintf(sentry,"NETDB support not compiled into this Squid cache.\n");
940#endif
941}
942
943int
944netdbHostHops(const char *host)
945{
946#if USE_ICMP
947 netdbEntry *n = netdbLookupHost(host);
948
949 if (n) {
951 return (int) (n->hops + 0.5);
952 }
953#else
954 (void)host;
955#endif
956 return 0;
957}
958
959int
960netdbHostRtt(const char *host)
961{
962#if USE_ICMP
963 netdbEntry *n = netdbLookupHost(host);
964
965 if (n) {
967 return (int) (n->rtt + 0.5);
968 }
969
970#else
971 (void)host;
972#endif
973 return 0;
974}
975
976void
977netdbHostData(const char *host, int *samp, int *rtt, int *hops)
978{
979#if USE_ICMP
980 netdbEntry *n = netdbLookupHost(host);
981
982 if (n == nullptr)
983 return;
984
985 *samp = n->pings_recv;
986
987 *rtt = (int) (n->rtt + 0.5);
988
989 *hops = (int) (n->hops + 0.5);
990
992
993#else
994 (void)hops;
995 (void)rtt;
996 (void)samp;
997 (void)host;
998#endif
999}
1000
1001void
1002netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
1003{
1004#if USE_ICMP
1005 netdbEntry *n;
1006 double rtt = (double) irtt;
1007 double hops = (double) ihops;
1008 net_db_peer *p;
1009 debugs(38, 3, url.host() << ", " << ihops << " hops, " << irtt << " rtt");
1010 n = netdbLookupHost(url.host());
1011
1012 if (n == nullptr) {
1013 debugs(38, 3, "host " << url.host() << " not found");
1014 return;
1015 }
1016
1017 if ((p = netdbPeerByName(n, e->host)) == nullptr)
1018 p = netdbPeerAdd(n, e);
1019
1020 p->rtt = rtt;
1021
1022 p->hops = hops;
1023
1024 p->expires = squid_curtime + 3600;
1025
1026 if (n->n_peers < 2)
1027 return;
1028
1029 qsort((char *) n->peers,
1030 n->n_peers,
1031 sizeof(net_db_peer),
1033
1034#else
1035 (void)ihops;
1036 (void)irtt;
1037 (void)e;
1038 (void)url;
1039#endif
1040}
1041
1042void
1043netdbExchangeUpdatePeer(Ip::Address &addr, CachePeer * e, double rtt, double hops)
1044{
1045#if USE_ICMP
1046 netdbEntry *n;
1047 net_db_peer *p;
1048 debugs(38, 5, "netdbExchangeUpdatePeer: '" << addr << "', "<<
1049 std::setfill('0')<< std::setprecision(2) << hops << " hops, " <<
1050 rtt << " rtt");
1051
1052 if ( !addr.isIPv4() ) {
1053 debugs(38, 5, "netdbExchangeUpdatePeer: Aborting peer update for '" << addr << "', NetDB cannot handle IPv6.");
1054 return;
1055 }
1056
1057 n = netdbLookupAddr(addr);
1058
1059 if (n == nullptr)
1060 n = netdbAdd(addr);
1061
1062 assert(nullptr != n);
1063
1064 if ((p = netdbPeerByName(n, e->host)) == nullptr)
1065 p = netdbPeerAdd(n, e);
1066
1067 p->rtt = rtt;
1068
1069 p->hops = hops;
1070
1071 p->expires = squid_curtime + 3600; /* XXX ? */
1072
1073 if (n->n_peers < 2)
1074 return;
1075
1076 qsort((char *) n->peers,
1077 n->n_peers,
1078 sizeof(net_db_peer),
1080
1081#else
1082 (void)hops;
1083 (void)rtt;
1084 (void)e;
1085 (void)addr;
1086#endif
1087}
1088
1089void
1091{
1092#if USE_ICMP
1093 netdbEntry *n = netdbLookupAddr(addr);
1094
1095 if (n == nullptr)
1096 return;
1097
1098 debugs(38, 3, "netdbDeleteAddrNetwork: " << n->network);
1099
1100 netdbRelease(n);
1101#else
1102 (void)addr;
1103#endif
1104}
1105
1106void
1108{
1109 HttpReply *reply = new HttpReply;
1110#if USE_ICMP
1111
1112 Ip::Address addr;
1113
1114 netdbEntry *n;
1115 int i;
1116 int j;
1117 int rec_sz;
1118 char *buf;
1119
1120 struct in_addr line_addr;
1121 s->buffer();
1122 reply->setHeaders(Http::scOkay, "OK", nullptr, -1, squid_curtime, -2);
1123 s->replaceHttpReply(reply);
1124 rec_sz = 0;
1125 rec_sz += 1 + sizeof(struct in_addr);
1126 rec_sz += 1 + sizeof(int);
1127 rec_sz += 1 + sizeof(int);
1128 buf = (char *)memAllocate(MEM_4K_BUF);
1129 i = 0;
1131
1132 while ((n = (netdbEntry *) hash_next(addr_table))) {
1133 if (0.0 == n->rtt)
1134 continue;
1135
1136 if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */
1137 continue;
1138
1139 if (! (addr = n->network) )
1140 continue;
1141
1142 // XXX: NetDB cannot yet handle IPv6 addresses
1143 if ( !addr.isIPv4() )
1144 continue;
1145
1146 buf[i] = (char) NETDB_EX_NETWORK;
1147 ++i;
1148
1149 addr.getInAddr(line_addr);
1150 memcpy(&buf[i], &line_addr, sizeof(struct in_addr));
1151
1152 i += sizeof(struct in_addr);
1153
1154 buf[i] = (char) NETDB_EX_RTT;
1155 ++i;
1156
1157 j = htonl((int) (n->rtt * 1000));
1158
1159 memcpy(&buf[i], &j, sizeof(int));
1160
1161 i += sizeof(int);
1162
1163 buf[i] = (char) NETDB_EX_HOPS;
1164 ++i;
1165
1166 j = htonl((int) (n->hops * 1000));
1167
1168 memcpy(&buf[i], &j, sizeof(int));
1169
1170 i += sizeof(int);
1171
1172 if (i + rec_sz > 4096) {
1173 s->append(buf, i);
1174 i = 0;
1175 }
1176 }
1177
1178 if (i > 0) {
1179 s->append(buf, i);
1180 i = 0;
1181 }
1182
1183 assert(0 == i);
1184 s->flush();
1185 memFree(buf, MEM_4K_BUF);
1186#else
1187
1188 reply->setHeaders(Http::scBadRequest, "Bad Request", nullptr, -1, squid_curtime, -2);
1189 s->replaceHttpReply(reply);
1190 storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
1191#endif
1192
1193 s->complete();
1194}
1195
1196void
1198{
1199#if USE_ICMP
1200 CachePeer *p = (CachePeer *)data;
1201 static const SBuf netDB("netdb");
1202 char *uri = internalRemoteUri(p->secure.encryptTransport, p->host, p->http_port, "/squid-internal-dynamic/", netDB);
1203 debugs(38, 3, "Requesting '" << uri << "'");
1204 const auto mx = MasterXaction::MakePortless<XactionInitiator::initIcmp>();
1206
1207 if (!req) {
1208 debugs(38, DBG_IMPORTANT, "ERROR: " << MYNAME << ": Bad URI " << uri);
1209 return;
1210 }
1211
1212 netdbExchangeState *ex = new netdbExchangeState(p, req);
1213 ex->e = storeCreateEntry(uri, uri, RequestFlags(), Http::METHOD_GET);
1214 assert(nullptr != ex->e);
1215
1216 ex->sc = storeClientListAdd(ex->e, ex);
1218
1219 ex->r->flags.loopDetected = true; /* cheat! -- force direct */
1220
1221 // XXX: send as Proxy-Authenticate instead
1222 if (p->login)
1223 ex->r->url.userInfo(SBuf(p->login));
1224
1226#else
1227 (void)data;
1228#endif
1229}
1230
1231#if USE_ICMP
1234static CachePeer *
1235findUsableParentAtHostname(PeerSelector *ps, const char * const hostname, const HttpRequest &request)
1236{
1237 for (const auto &peer: CurrentCachePeers()) {
1238 const auto p = peer.get();
1239 // Both fields should be lowercase, but no code ensures that invariant.
1240 // TODO: net_db_peer should point to CachePeer instead of its hostname!
1241 if (strcasecmp(p->host, hostname) != 0)
1242 continue;
1243
1244 if (neighborType(p, request.url) != PEER_PARENT)
1245 continue;
1246
1247 if (!peerHTTPOkay(p, ps))
1248 continue;
1249
1250 return p;
1251 }
1252
1253 return nullptr;
1254}
1255#endif
1256
1257CachePeer *
1259{
1260#if USE_ICMP
1261 assert(ps);
1262 HttpRequest *request = ps->request;
1263
1264 netdbEntry *n;
1265 const ipcache_addrs *ia;
1266 net_db_peer *h;
1267 int i;
1268 n = netdbLookupHost(request->url.host());
1269
1270 if (nullptr == n) {
1271 /* try IP addr */
1272 ia = ipcache_gethostbyname(request->url.host(), 0);
1273
1274 if (nullptr != ia)
1275 n = netdbLookupAddr(ia->current());
1276 }
1277
1278 if (nullptr == n)
1279 return nullptr;
1280
1281 if (0 == n->n_peers)
1282 return nullptr;
1283
1285
1286 /*
1287 * Find the parent with the least RTT to the origin server.
1288 * Make sure we don't return a parent who is farther away than
1289 * we are. Note, the n->peers list is pre-sorted by RTT.
1290 */
1291 for (i = 0; i < n->n_peers; ++i) {
1292 h = &n->peers[i];
1293
1294 if (n->rtt > 0)
1295 if (n->rtt < h->rtt)
1296 break;
1297
1298 if (const auto p = findUsableParentAtHostname(ps, h->peername, *request))
1299 return p;
1300 }
1301
1302#else
1303 (void)ps;
1304#endif
1305 return nullptr;
1306}
1307
#define Assure(condition)
Definition: Assure.h:35
const CachePeers & CurrentCachePeers()
Definition: CachePeers.cc:41
IcmpSquid icmpEngine
Definition: IcmpSquid.cc:26
int size
Definition: ModDevPoll.cc:75
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
void(void *, StoreIOBuffer) STCB
Definition: StoreClient.h:32
#define assert(EX)
Definition: assert.h:17
Definition: Uri.h:31
void host(const char *src)
Definition: Uri.cc:100
void userInfo(const SBuf &s)
Definition: Uri.h:79
unsigned short http_port
Definition: CachePeer.h:103
char * login
Definition: CachePeer.h:203
char * host
Definition: CachePeer.h:65
Security::PeerOptions secure
security settings for peer connection
Definition: CachePeer.h:219
Cbc * valid() const
was set and is valid
Definition: CbcPointer.h:41
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:159
const Ip::Address & current() const
Definition: ipcache.h:59
encapsulates DNS lookup results
Definition: LookupDetails.h:23
static void fwdStart(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *)
Same as Start() but no master xaction info (AccessLogEntry) available.
Definition: FwdState.cc:412
Http::StatusLine sline
Definition: HttpReply.h:56
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:528
RequestFlags flags
Definition: HttpRequest.h:141
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
AnyP::ProtocolVersion http_ver
Definition: Message.h:72
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
void DomainPing(Ip::Address &to, const char *domain)
Definition: IcmpSquid.cc:177
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:792
bool isIPv4() const
Definition: Address.cc:158
bool isAnyAddr() const
Definition: Address.cc:170
bool isIPv6() const
Definition: Address.cc:164
bool getInAddr(struct in_addr &) const
Definition: Address.cc:1020
int applyMask(const Address &mask)
Definition: Address.cc:87
void setAnyAddr()
NOTE: Does NOT clear the Port stored. Only the Address and Type.
Definition: Address.cc:177
Definition: File.h:39
const HttpReply & baseReply() const
Definition: MemObject.h:60
HttpRequest * request
C * getRaw() const
Definition: RefCount.h:89
bool loopDetected
Definition: RequestFlags.h:40
Definition: SBuf.h:94
bool encryptTransport
whether transport encryption (TLS/SSL) is to be used on connections to the peer
Definition: PeerOptions.h:147
struct SquidConfig::@105 Netdb
char * netdbFilename
Definition: SquidConfig.h:225
time_t period
Definition: SquidConfig.h:274
uint16_t flags
Definition: Store.h:232
MemObject & mem()
Definition: Store.h:51
int unlock(const char *context)
Definition: store.cc:455
const char * url() const
Definition: store.cc:1552
void complete()
Definition: store.cc:1017
void flush() override
Definition: store.cc:1598
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1691
void append(char const *, int) override
Appends a c-string to existing packed data.
Definition: store.cc:789
void buffer() override
Definition: store.cc:1587
unsigned error
Definition: StoreIOBuffer.h:55
int64_t offset
Definition: StoreIOBuffer.h:58
struct StoreIOBuffer::@141 flags
StoreIOBuffer & positionAt(const int64_t newOffset)
convenience method for changing the offset of a being-configured buffer
Definition: StoreIOBuffer.h:47
void consume(size_t)
get rid of previously appended() prefix of a given size
const char * c_str()
a NUL-terminated version of content(); same lifetime as content()
Definition: ParsingBuffer.h:45
size_t contentSize() const
the total number of append()ed bytes that were not consume()d
StoreIOBuffer space()
StoreIOBuffer makeInitialSpace()
Definition: ParsingBuffer.h:84
void appended(const char *, size_t)
remember the new bytes received into the previously provided space()
net_db_name(const char *name, netdbEntry *)
Definition: net_db.cc:138
netdbEntry * net_db_entry
Definition: net_db.h:33
net_db_name * next
Definition: net_db.h:32
double hops
Definition: net_db.h:43
time_t expires
Definition: net_db.h:45
double rtt
Definition: net_db.h:44
const char * peername
associated CachePeer::host (i.e. cache_peer hostname, not name=value!)
Definition: net_db.h:41
int link_count
Definition: net_db.h:63
char network[MAX_IPSTRLEN]
Definition: net_db.h:56
double hops
Definition: net_db.h:59
int pings_recv
Definition: net_db.h:58
int n_peers_alloc
Definition: net_db.h:66
net_db_peer * peers
Definition: net_db.h:65
int pings_sent
Definition: net_db.h:57
time_t next_ping_time
Definition: net_db.h:61
time_t last_use_time
Definition: net_db.h:62
net_db_name * hosts
Definition: net_db.h:64
int n_peers
Definition: net_db.h:67
double rtt
Definition: net_db.h:60
CBDATA_CLASS(netdbExchangeState)
HttpRequestPointer r
Definition: net_db.cc:82
CbcPointer< CachePeer > p
Definition: net_db.cc:79
netdb_conn_state_t connstate
Definition: net_db.cc:87
netdbExchangeState(CachePeer *aPeer, const HttpRequestPointer &theReq)
Definition: net_db.cc:64
store_client * sc
Definition: net_db.cc:81
StoreEntry * e
Definition: net_db.cc:80
Store::ParsingBuffer parsingBuffer
for receiving a NetDB reply body from Store and interpreting it
Definition: net_db.cc:85
bool atEof() const
Definition: StoreClient.h:109
char * key
Definition: wordlist.h:32
wordlist * next
Definition: wordlist.h:33
#define w_space
#define MYNAME
Definition: Stream.h:236
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
#define O_BINARY
Definition: defines.h:136
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
@ PEER_PARENT
Definition: enums.h:30
@ ENTRY_ABORTED
Definition: enums.h:115
@ NETDB_EX_HOPS
Definition: enums.h:186
@ NETDB_EX_RTT
Definition: enums.h:185
@ NETDB_EX_NETWORK
Definition: enums.h:184
void eventAddIsh(const char *name, EVH *func, void *arg, double delta_ish, int weight)
Definition: event.cc:114
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
int file_open(const char *path, int mode)
Definition: fs_io.cc:45
void file_close(int fd)
Definition: fs_io.cc:73
const ipcache_addrs * ipcache_gethostbyname(const char *name, int flags)
Definition: ipcache.cc:728
void ipcache_nbgethostbyname(const char *name, IPH *handler, void *handlerData)
Definition: ipcache.cc:608
SQUIDCEXTERN hash_table * hash_create(HASHCMP *, int, HASHHASH *)
Definition: hash.cc:108
SQUIDCEXTERN void hash_join(hash_table *, hash_link *)
Definition: hash.cc:131
SQUIDCEXTERN void hash_first(hash_table *)
Definition: hash.cc:172
SQUIDCEXTERN int hashPrime(int n)
Definition: hash.cc:293
int HASHCMP(const void *, const void *)
Definition: hash.h:13
SQUIDCEXTERN HASHHASH hash_string
Definition: hash.h:45
SQUIDCEXTERN hash_link * hash_next(hash_table *)
Definition: hash.cc:188
SQUIDCEXTERN void hash_remove_link(hash_table *, hash_link *)
Definition: hash.cc:220
SQUIDCEXTERN const char * hashKeyStr(const hash_link *)
Definition: hash.cc:313
SQUIDCEXTERN hash_link * hash_lookup(hash_table *, const void *)
Definition: hash.cc:146
char * internalRemoteUri(bool encrypt, const char *host, unsigned short port, const char *dir, const SBuf &name)
Definition: internal.cc:96
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
void IPH(const ipcache_addrs *, const Dns::LookupDetails &details, void *)
Definition: ipcache.h:227
void logfileClose(Logfile *lf)
Definition: File.cc:92
void logfilePrintf(Logfile *lf, const char *fmt,...)
Definition: File.cc:114
Logfile * logfileOpen(const char *path, size_t bufsz, int fatal_flag)
Definition: File.cc:40
static uint32 A
Definition: md4.c:43
static uint32 B
Definition: md4.c:43
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: minimal.cc:60
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition: old_api.cc:213
@ MEM_4K_BUF
Definition: forward.h:43
@ scBadRequest
Definition: StatusCode.h:44
@ scNone
Definition: StatusCode.h:21
@ scOkay
Definition: StatusCode.h:26
@ METHOD_GET
Definition: MethodType.h:25
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
void RegisterAction(char const *action, char const *desc, OBJH *handler, int pw_req_flag, int atomic)
Definition: Registration.cc:16
#define xfree
#define xstrdup
int peerHTTPOkay(const CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:257
peer_t neighborType(const CachePeer *p, const AnyP::Uri &url)
Definition: neighbors.cc:114
static void netdbHashInsert(netdbEntry *n, Ip::Address &addr)
Definition: net_db.cc:117
static void netdbRelease(netdbEntry *n)
Definition: net_db.cc:185
void netdbPingSite(const char *hostname)
Definition: net_db.cc:827
void netdbHostData(const char *host, int *samp, int *rtt, int *hops)
Definition: net_db.cc:977
static void netdbHashDelete(const char *key)
Definition: net_db.cc:126
void netdbDump(StoreEntry *sentry)
Definition: net_db.cc:877
static void netdbSaveState(void *)
Definition: net_db.cc:465
int netdbHostHops(const char *host)
Definition: net_db.cc:944
int netdbHostRtt(const char *host)
Definition: net_db.cc:960
static STCB netdbExchangeHandleReply
Definition: net_db.cc:108
static netdbEntry * netdbLookupHost(const char *key)
Definition: net_db.cc:178
static net_db_peer * netdbPeerByName(const netdbEntry *n, const char *)
Definition: net_db.cc:402
void netdbExchangeUpdatePeer(Ip::Address &addr, CachePeer *e, double rtt, double hops)
Definition: net_db.cc:1043
static IPH netdbSendPing
Definition: net_db.cc:107
static netdbEntry * netdbAdd(Ip::Address &addr)
Definition: net_db.cc:265
static void netdbPurgeLRU(void)
Definition: net_db.cc:223
static void netdbHostInsert(netdbEntry *n, const char *hostname)
Definition: net_db.cc:150
void netdbInit(void)
Definition: net_db.cc:803
static netdbEntry * netdbLookupAddr(const Ip::Address &addr)
Definition: net_db.cc:254
static void netdbHostDelete(const net_db_name *x)
Definition: net_db.cc:158
static hash_table * host_table
Definition: net_db.cc:93
static void netdbReloadState(void)
Definition: net_db.cc:529
static hash_table * addr_table
Definition: net_db.cc:92
void netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
Definition: net_db.cc:1002
void netdbDeleteAddrNetwork(Ip::Address &addr)
Definition: net_db.cc:1090
static wordlist * peer_names
Definition: net_db.cc:114
static CachePeer * findUsableParentAtHostname(PeerSelector *ps, const char *const hostname, const HttpRequest &request)
Definition: net_db.cc:1235
CBDATA_CLASS_INIT(netdbExchangeState)
static const char * netdbPeerName(const char *name)
Definition: net_db.cc:651
void netdbBinaryExchange(StoreEntry *s)
Definition: net_db.cc:1107
Ip::Address networkFromInaddr(const Ip::Address &a)
Definition: net_db.cc:349
netdb_conn_state_t
Definition: net_db.cc:53
@ STATE_HEADER
Definition: net_db.cc:55
@ STATE_BODY
Definition: net_db.cc:56
@ STATE_NONE
Definition: net_db.cc:54
static int sortPeerByRtt(const void *A, const void *B)
Definition: net_db.cc:451
static net_db_peer * netdbPeerAdd(netdbEntry *n, CachePeer *e)
Definition: net_db.cc:416
static int sortByRtt(const void *A, const void *B)
Definition: net_db.cc:388
CachePeer * netdbClosestParent(PeerSelector *ps)
Definition: net_db.cc:1258
void netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
Definition: net_db.cc:844
void netdbExchangeStart(void *data)
Definition: net_db.cc:1197
static int netdbLRU(const void *A, const void *B)
Definition: net_db.cc:208
static struct stat sb
Definition: squidclient.cc:71
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:841
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:745
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
store_client * storeClientListAdd(StoreEntry *e, void *data)
void EVH void double
Definition: stub_event.cc:16
int unsigned int
Definition: stub_fd.cc:19
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:17
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:51
void debug_trap(const char *message)
Definition: tools.cc:458
const char * wordlistAdd(wordlist **list, const char *key)
Definition: wordlist.cc:25
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
#define safe_free(x)
Definition: xalloc.h:73
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors