]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Asn.cc
Tightened StoreEntry locking. Fixed entry touching and synchronization code:
[thirdparty/squid.git] / src / acl / Asn.cc
1
2 /*
3 * DEBUG: section 53 AS Number handling
4 * AUTHOR: Duane Wessels, Kostas Anagnostakis
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33
34 #include "squid.h"
35 #include "acl/Acl.h"
36 #include "acl/Asn.h"
37 #include "acl/Checklist.h"
38 #include "acl/DestinationAsn.h"
39 #include "acl/DestinationIp.h"
40 #include "acl/SourceAsn.h"
41 #include "cache_cf.h"
42 #include "forward.h"
43 #include "HttpReply.h"
44 #include "HttpRequest.h"
45 #include "ipcache.h"
46 #include "mgr/Registration.h"
47 #include "radix.h"
48 #include "RequestFlags.h"
49 #include "SquidConfig.h"
50 #include "Store.h"
51 #include "StoreClient.h"
52 #include "StoreClient.h"
53 #include "wordlist.h"
54
55 #define WHOIS_PORT 43
56 #define AS_REQBUF_SZ 4096
57
58 /* BEGIN of definitions for radix tree entries */
59
60 /* 32/128 bits address in memory with length */
61 class m_ADDR
62 {
63 public:
64 uint8_t len;
65 Ip::Address addr;
66
67 m_ADDR() : len(sizeof(Ip::Address)) {};
68 };
69
70 /* END of definitions for radix tree entries */
71
72 /* Head for ip to asn radix tree */
73
74 struct squid_radix_node_head *AS_tree_head;
75
76 /* explicit instantiation required for some systems */
77
78 /// \cond AUTODOCS-IGNORE
79 template cbdata_type CbDataList<int>::CBDATA_CbDataList;
80 /// \endcond
81
82 /**
83 * Structure for as number information. it could be simply
84 * a list but it's coded as a structure for future
85 * enhancements (e.g. expires)
86 */
87 struct as_info {
88 CbDataList<int> *as_number;
89 time_t expires; /* NOTUSED */
90 };
91
92 struct ASState {
93 StoreEntry *entry;
94 store_client *sc;
95 HttpRequest *request;
96 int as_number;
97 int64_t offset;
98 int reqofs;
99 char reqbuf[AS_REQBUF_SZ];
100 bool dataRead;
101 };
102
103 /** entry into the radix tree */
104 struct rtentry_t {
105 struct squid_radix_node e_nodes[2];
106 as_info *e_info;
107 m_ADDR e_addr;
108 m_ADDR e_mask;
109 };
110
111 static int asnAddNet(char *, int);
112
113 static void asnCacheStart(int as);
114
115 static STCB asHandleReply;
116
117 #if defined(__cplusplus)
118 extern "C" {
119 #endif
120
121 static int destroyRadixNode(struct squid_radix_node *rn, void *w);
122 static int printRadixNode(struct squid_radix_node *rn, void *sentry);
123
124 #if defined(__cplusplus)
125 }
126 #endif
127
128 void asnAclInitialize(ACL * acls);
129
130 static void asStateFree(void *data);
131
132 static void destroyRadixNodeInfo(as_info *);
133
134 static OBJH asnStats;
135
136 /* PUBLIC */
137
138 int
139 asnMatchIp(CbDataList<int> *data, Ip::Address &addr)
140 {
141 struct squid_radix_node *rn;
142 as_info *e;
143 m_ADDR m_addr;
144 CbDataList<int> *a = NULL;
145 CbDataList<int> *b = NULL;
146
147 debugs(53, 3, "asnMatchIp: Called for " << addr );
148
149 if (AS_tree_head == NULL)
150 return 0;
151
152 if (addr.IsNoAddr())
153 return 0;
154
155 if (addr.IsAnyAddr())
156 return 0;
157
158 m_addr.addr = addr;
159
160 rn = squid_rn_match(&m_addr, AS_tree_head);
161
162 if (rn == NULL) {
163 debugs(53, 3, "asnMatchIp: Address not in as db.");
164 return 0;
165 }
166
167 debugs(53, 3, "asnMatchIp: Found in db!");
168 e = ((rtentry_t *) rn)->e_info;
169 assert(e);
170
171 for (a = data; a; a = a->next)
172 for (b = e->as_number; b; b = b->next)
173 if (a->element == b->element) {
174 debugs(53, 5, "asnMatchIp: Found a match!");
175 return 1;
176 }
177
178 debugs(53, 5, "asnMatchIp: AS not in as db.");
179 return 0;
180 }
181
182 void
183 ACLASN::prepareForUse()
184 {
185 for (CbDataList<int> *i = data; i; i = i->
186 next)
187 asnCacheStart(i->element);
188 }
189
190 static void
191 asnRegisterWithCacheManager(void)
192 {
193 Mgr::RegisterAction("asndb", "AS Number Database", asnStats, 0, 1);
194 }
195
196 /* initialize the radix tree structure */
197
198 SQUIDCEXTERN int squid_max_keylen; /* yuck.. this is in lib/radix.c */
199
200 CBDATA_TYPE(ASState);
201 void
202 asnInit(void)
203 {
204 static bool inited = false;
205 squid_max_keylen = 40;
206 CBDATA_INIT_TYPE(ASState);
207
208 if (!inited) {
209 inited = true;
210 squid_rn_init();
211 }
212
213 squid_rn_inithead(&AS_tree_head, 8);
214
215 asnRegisterWithCacheManager();
216 }
217
218 void
219 asnFreeMemory(void)
220 {
221 squid_rn_walktree(AS_tree_head, destroyRadixNode, AS_tree_head);
222
223 destroyRadixNode((struct squid_radix_node *) 0, (void *) AS_tree_head);
224 }
225
226 static void
227 asnStats(StoreEntry * sentry)
228 {
229 storeAppendPrintf(sentry, "Address \tAS Numbers\n");
230 squid_rn_walktree(AS_tree_head, printRadixNode, sentry);
231 }
232
233 /* PRIVATE */
234
235 static void
236 asnCacheStart(int as)
237 {
238 LOCAL_ARRAY(char, asres, 4096);
239 StoreEntry *e;
240 ASState *asState;
241 asState = cbdataAlloc(ASState);
242 asState->dataRead = 0;
243 debugs(53, 3, "asnCacheStart: AS " << as);
244 snprintf(asres, 4096, "whois://%s/!gAS%d", Config.as_whois_server, as);
245 asState->as_number = as;
246 asState->request = HttpRequest::CreateFromUrl(asres);
247 assert(NULL != asState->request);
248 HTTPMSGLOCK(asState->request);
249
250 if ((e = storeGetPublic(asres, Http::METHOD_GET)) == NULL) {
251 e = storeCreateEntry(asres, asres, RequestFlags(), Http::METHOD_GET);
252 asState->sc = storeClientListAdd(e, asState);
253 FwdState::fwdStart(Comm::ConnectionPointer(), e, asState->request);
254 } else {
255 e->lock("Asn");
256 asState->sc = storeClientListAdd(e, asState);
257 }
258
259 asState->entry = e;
260 asState->offset = 0;
261 asState->reqofs = 0;
262 StoreIOBuffer readBuffer (AS_REQBUF_SZ, asState->offset, asState->reqbuf);
263 storeClientCopy(asState->sc,
264 e,
265 readBuffer,
266 asHandleReply,
267 asState);
268 }
269
270 static void
271 asHandleReply(void *data, StoreIOBuffer result)
272 {
273 ASState *asState = (ASState *)data;
274 StoreEntry *e = asState->entry;
275 char *s;
276 char *t;
277 char *buf = asState->reqbuf;
278 int leftoversz = -1;
279
280 debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result.length);
281 debugs(53, 3, "asHandleReply: buffer='" << buf << "'");
282
283 /* First figure out whether we should abort the request */
284
285 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
286 asStateFree(asState);
287 return;
288 }
289
290 if (result.length == 0 && asState->dataRead) {
291 debugs(53, 3, "asHandleReply: Done: " << e->url() );
292 asStateFree(asState);
293 return;
294 } else if (result.flags.error) {
295 debugs(53, DBG_IMPORTANT, "asHandleReply: Called with Error set and size=" << (unsigned int) result.length);
296 asStateFree(asState);
297 return;
298 } else if (e->getReply()->sline.status() != Http::scOkay) {
299 debugs(53, DBG_IMPORTANT, "WARNING: AS " << asState->as_number << " whois request failed");
300 asStateFree(asState);
301 return;
302 }
303
304 /*
305 * Next, attempt to parse our request
306 * Remembering that the actual buffer size is retsize + reqofs!
307 */
308 s = buf;
309
310 while ((size_t)(s - buf) < result.length + asState->reqofs && *s != '\0') {
311 while (*s && xisspace(*s))
312 ++s;
313
314 for (t = s; *t; ++t) {
315 if (xisspace(*t))
316 break;
317 }
318
319 if (*t == '\0') {
320 /* oof, word should continue on next block */
321 break;
322 }
323
324 *t = '\0';
325 debugs(53, 3, "asHandleReply: AS# " << s << " (" << asState->as_number << ")");
326 asnAddNet(s, asState->as_number);
327 s = t + 1;
328 asState->dataRead = 1;
329 }
330
331 /*
332 * Next, grab the end of the 'valid data' in the buffer, and figure
333 * out how much data is left in our buffer, which we need to keep
334 * around for the next request
335 */
336 leftoversz = (asState->reqofs + result.length) - (s - buf);
337
338 assert(leftoversz >= 0);
339
340 /*
341 * Next, copy the left over data, from s to s + leftoversz to the
342 * beginning of the buffer
343 */
344 memmove(buf, s, leftoversz);
345
346 /*
347 * Next, update our offset and reqofs, and kick off a copy if required
348 */
349 asState->offset += result.length;
350
351 asState->reqofs = leftoversz;
352
353 debugs(53, 3, "asState->offset = " << asState->offset);
354
355 if (e->store_status == STORE_PENDING) {
356 debugs(53, 3, "asHandleReply: store_status == STORE_PENDING: " << e->url() );
357 StoreIOBuffer tempBuffer (AS_REQBUF_SZ - asState->reqofs,
358 asState->offset,
359 asState->reqbuf + asState->reqofs);
360 storeClientCopy(asState->sc,
361 e,
362 tempBuffer,
363 asHandleReply,
364 asState);
365 } else {
366 StoreIOBuffer tempBuffer;
367 debugs(53, 3, "asHandleReply: store complete, but data received " << e->url() );
368 tempBuffer.offset = asState->offset;
369 tempBuffer.length = AS_REQBUF_SZ - asState->reqofs;
370 tempBuffer.data = asState->reqbuf + asState->reqofs;
371 storeClientCopy(asState->sc,
372 e,
373 tempBuffer,
374 asHandleReply,
375 asState);
376 }
377 }
378
379 static void
380 asStateFree(void *data)
381 {
382 ASState *asState = (ASState *)data;
383 debugs(53, 3, "asnStateFree: " << asState->entry->url() );
384 storeUnregister(asState->sc, asState->entry, asState);
385 asState->entry->unlock("Asn");
386 HTTPMSGUNLOCK(asState->request);
387 cbdataFree(asState);
388 }
389
390 /**
391 * add a network (addr, mask) to the radix tree, with matching AS number
392 */
393 static int
394 asnAddNet(char *as_string, int as_number)
395 {
396 struct squid_radix_node *rn;
397 CbDataList<int> **Tail = NULL;
398 CbDataList<int> *q = NULL;
399 as_info *asinfo = NULL;
400
401 Ip::Address mask;
402 Ip::Address addr;
403 char *t;
404 int bitl;
405
406 t = strchr(as_string, '/');
407
408 if (t == NULL) {
409 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
410 return 0;
411 }
412
413 *t = '\0';
414 addr = as_string;
415 bitl = atoi(t + 1);
416
417 if (bitl < 0)
418 bitl = 0;
419
420 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
421 t = strchr(as_string, '.');
422
423 // generate Netbits Format Mask
424 mask.SetNoAddr();
425 mask.ApplyMask(bitl, (t!=NULL?AF_INET:AF_INET6) );
426
427 debugs(53, 3, "asnAddNet: called for " << addr << "/" << mask );
428
429 rtentry_t *e = (rtentry_t *)xcalloc(1, sizeof(rtentry_t));
430
431 e->e_addr.addr = addr;
432
433 e->e_mask.addr = mask;
434
435 rn = squid_rn_lookup(&e->e_addr, &e->e_mask, AS_tree_head);
436
437 if (rn != NULL) {
438 asinfo = ((rtentry_t *) rn)->e_info;
439
440 if (asinfo->as_number->find(as_number)) {
441 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr << "/" << bitl << "' for AS " << as_number);
442 } else {
443 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
444
445 for (Tail = &asinfo->as_number; *Tail; Tail = &(*Tail)->next);
446 q = new CbDataList<int> (as_number);
447
448 *(Tail) = q;
449
450 e->e_info = asinfo;
451 }
452 } else {
453 q = new CbDataList<int> (as_number);
454 asinfo = (as_info *)xmalloc(sizeof(as_info));
455 asinfo->as_number = q;
456 squid_rn_addroute(&e->e_addr, &e->e_mask, AS_tree_head, e->e_nodes);
457 rn = squid_rn_match(&e->e_addr, AS_tree_head);
458 assert(rn != NULL);
459 e->e_info = asinfo;
460 }
461
462 if (rn == 0) { /* assert might expand to nothing */
463 xfree(asinfo);
464 delete q;
465 xfree(e);
466 debugs(53, 3, "asnAddNet: Could not add entry.");
467 return 0;
468 }
469
470 e->e_info = asinfo;
471 return 1;
472 }
473
474 static int
475 destroyRadixNode(struct squid_radix_node *rn, void *w)
476 {
477
478 struct squid_radix_node_head *rnh = (struct squid_radix_node_head *) w;
479
480 if (rn && !(rn->rn_flags & RNF_ROOT)) {
481 rtentry_t *e = (rtentry_t *) rn;
482 rn = squid_rn_delete(rn->rn_key, rn->rn_mask, rnh);
483
484 if (rn == 0)
485 debugs(53, 3, "destroyRadixNode: internal screwup");
486
487 destroyRadixNodeInfo(e->e_info);
488
489 xfree(rn);
490 }
491
492 return 1;
493 }
494
495 static void
496 destroyRadixNodeInfo(as_info * e_info)
497 {
498 CbDataList<int> *prev = NULL;
499 CbDataList<int> *data = e_info->as_number;
500
501 while (data) {
502 prev = data;
503 data = data->next;
504 delete prev;
505 }
506 }
507
508 static int
509 printRadixNode(struct squid_radix_node *rn, void *_sentry)
510 {
511 StoreEntry *sentry = (StoreEntry *)_sentry;
512 rtentry_t *e = (rtentry_t *) rn;
513 CbDataList<int> *q;
514 as_info *asinfo;
515 char buf[MAX_IPSTRLEN];
516 Ip::Address addr;
517 Ip::Address mask;
518
519 assert(e);
520 assert(e->e_info);
521 addr = e->e_addr.addr;
522 mask = e->e_mask.addr;
523 storeAppendPrintf(sentry, "%s/%d\t",
524 addr.NtoA(buf, MAX_IPSTRLEN),
525 mask.GetCIDR() );
526 asinfo = e->e_info;
527 assert(asinfo->as_number);
528
529 for (q = asinfo->as_number; q; q = q->next)
530 storeAppendPrintf(sentry, " %d", q->element);
531
532 storeAppendPrintf(sentry, "\n");
533
534 return 0;
535 }
536
537 ACLASN::~ACLASN()
538 {
539 if (data)
540 delete data;
541 }
542
543 bool
544
545 ACLASN::match(Ip::Address toMatch)
546 {
547 return asnMatchIp(data, toMatch);
548 }
549
550 wordlist *
551 ACLASN::dump()
552 {
553 wordlist *W = NULL;
554 char buf[32];
555 CbDataList<int> *ldata = data;
556
557 while (ldata != NULL) {
558 snprintf(buf, sizeof(buf), "%d", ldata->element);
559 wordlistAdd(&W, buf);
560 ldata = ldata->next;
561 }
562
563 return W;
564 }
565
566 bool
567 ACLASN::empty () const
568 {
569 return data == NULL;
570 }
571
572 void
573 ACLASN::parse()
574 {
575 CbDataList<int> **curlist = &data;
576 CbDataList<int> **Tail;
577 CbDataList<int> *q = NULL;
578 char *t = NULL;
579
580 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
581 while ((t = strtokFile())) {
582 q = new CbDataList<int> (atoi(t));
583 *(Tail) = q;
584 Tail = &q->next;
585 }
586 }
587
588 ACLData<Ip::Address> *
589 ACLASN::clone() const
590 {
591 if (data)
592 fatal ("cloning of ACLASN not implemented");
593
594 return new ACLASN(*this);
595 }
596
597 /* explicit template instantiation required for some systems */
598
599 template class ACLStrategised<Ip::Address>;
600
601 ACL::Prototype ACLASN::SourceRegistryProtoype(&ACLASN::SourceRegistryEntry_, "src_as");
602
603 ACLStrategised<Ip::Address> ACLASN::SourceRegistryEntry_(new ACLASN, ACLSourceASNStrategy::Instance(), "src_as");
604
605 ACL::Prototype ACLASN::DestinationRegistryProtoype(&ACLASN::DestinationRegistryEntry_, "dst_as");
606
607 ACLStrategised<Ip::Address> ACLASN::DestinationRegistryEntry_(new ACLASN, ACLDestinationASNStrategy::Instance(), "dst_as");
608
609 int
610 ACLSourceASNStrategy::match (ACLData<Ip::Address> * &data, ACLFilledChecklist *checklist, ACLFlags &)
611 {
612 return data->match(checklist->src_addr);
613 }
614
615 ACLSourceASNStrategy *
616 ACLSourceASNStrategy::Instance()
617 {
618 return &Instance_;
619 }
620
621 ACLSourceASNStrategy ACLSourceASNStrategy::Instance_;
622
623 int
624 ACLDestinationASNStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist, ACLFlags &)
625 {
626 const ipcache_addrs *ia = ipcache_gethostbyname(checklist->request->GetHost(), IP_LOOKUP_IF_MISS);
627
628 if (ia) {
629 for (int k = 0; k < (int) ia->count; ++k) {
630 if (data->match(ia->in_addrs[k]))
631 return 1;
632 }
633
634 return 0;
635
636 } else if (!checklist->request->flags.destinationIpLookedUp) {
637 /* No entry in cache, lookup not attempted */
638 /* XXX FIXME: allow accessing the acl name here */
639 debugs(28, 3, "asnMatchAcl: Can't yet compare '" << "unknown" /*name*/ << "' ACL for '" << checklist->request->GetHost() << "'");
640 checklist->changeState (DestinationIPLookup::Instance());
641 } else {
642 Ip::Address noaddr;
643 noaddr.SetNoAddr();
644 return data->match(noaddr);
645 }
646
647 return 0;
648 }
649
650 ACLDestinationASNStrategy *
651 ACLDestinationASNStrategy::Instance()
652 {
653 return &Instance_;
654 }
655
656 ACLDestinationASNStrategy ACLDestinationASNStrategy::Instance_;