]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Asn.cc
Merge from trunk
[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 "FwdState.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 class ASState
93 {
94 public:
95 ASState();
96 ~ASState();
97
98 StoreEntry *entry;
99 store_client *sc;
100 HttpRequest::Pointer request;
101 int as_number;
102 int64_t offset;
103 int reqofs;
104 char reqbuf[AS_REQBUF_SZ];
105 bool dataRead;
106 private:
107 CBDATA_CLASS2(ASState);
108 };
109
110 CBDATA_CLASS_INIT(ASState);
111
112 ASState::ASState() :
113 entry(NULL),
114 sc(NULL),
115 request(NULL),
116 as_number(0),
117 offset(0),
118 reqofs(0),
119 dataRead(false)
120 {
121 memset(reqbuf, 0, AS_REQBUF_SZ);
122 }
123
124 ASState::~ASState()
125 {
126 debugs(53, 3, entry->url());
127 storeUnregister(sc, entry, this);
128 entry->unlock("~ASState");
129 }
130
131 /** entry into the radix tree */
132 struct rtentry_t {
133 struct squid_radix_node e_nodes[2];
134 as_info *e_info;
135 m_ADDR e_addr;
136 m_ADDR e_mask;
137 };
138
139 static int asnAddNet(char *, int);
140
141 static void asnCacheStart(int as);
142
143 static STCB asHandleReply;
144
145 #if defined(__cplusplus)
146 extern "C" {
147 #endif
148
149 static int destroyRadixNode(struct squid_radix_node *rn, void *w);
150 static int printRadixNode(struct squid_radix_node *rn, void *sentry);
151
152 #if defined(__cplusplus)
153 }
154 #endif
155
156 void asnAclInitialize(ACL * acls);
157
158 static void destroyRadixNodeInfo(as_info *);
159
160 static OBJH asnStats;
161
162 /* PUBLIC */
163
164 int
165 asnMatchIp(CbDataList<int> *data, Ip::Address &addr)
166 {
167 struct squid_radix_node *rn;
168 as_info *e;
169 m_ADDR m_addr;
170 CbDataList<int> *a = NULL;
171 CbDataList<int> *b = NULL;
172
173 debugs(53, 3, "asnMatchIp: Called for " << addr );
174
175 if (AS_tree_head == NULL)
176 return 0;
177
178 if (addr.isNoAddr())
179 return 0;
180
181 if (addr.isAnyAddr())
182 return 0;
183
184 m_addr.addr = addr;
185
186 rn = squid_rn_match(&m_addr, AS_tree_head);
187
188 if (rn == NULL) {
189 debugs(53, 3, "asnMatchIp: Address not in as db.");
190 return 0;
191 }
192
193 debugs(53, 3, "asnMatchIp: Found in db!");
194 e = ((rtentry_t *) rn)->e_info;
195 assert(e);
196
197 for (a = data; a; a = a->next)
198 for (b = e->as_number; b; b = b->next)
199 if (a->element == b->element) {
200 debugs(53, 5, "asnMatchIp: Found a match!");
201 return 1;
202 }
203
204 debugs(53, 5, "asnMatchIp: AS not in as db.");
205 return 0;
206 }
207
208 void
209 ACLASN::prepareForUse()
210 {
211 for (CbDataList<int> *i = data; i; i = i->
212 next)
213 asnCacheStart(i->element);
214 }
215
216 static void
217 asnRegisterWithCacheManager(void)
218 {
219 Mgr::RegisterAction("asndb", "AS Number Database", asnStats, 0, 1);
220 }
221
222 /* initialize the radix tree structure */
223
224 SQUIDCEXTERN int squid_max_keylen; /* yuck.. this is in lib/radix.c */
225
226 void
227 asnInit(void)
228 {
229 static bool inited = false;
230 squid_max_keylen = 40;
231
232 if (!inited) {
233 inited = true;
234 squid_rn_init();
235 }
236
237 squid_rn_inithead(&AS_tree_head, 8);
238
239 asnRegisterWithCacheManager();
240 }
241
242 void
243 asnFreeMemory(void)
244 {
245 squid_rn_walktree(AS_tree_head, destroyRadixNode, AS_tree_head);
246
247 destroyRadixNode((struct squid_radix_node *) 0, (void *) AS_tree_head);
248 }
249
250 static void
251 asnStats(StoreEntry * sentry)
252 {
253 storeAppendPrintf(sentry, "Address \tAS Numbers\n");
254 squid_rn_walktree(AS_tree_head, printRadixNode, sentry);
255 }
256
257 /* PRIVATE */
258
259 static void
260 asnCacheStart(int as)
261 {
262 LOCAL_ARRAY(char, asres, 4096);
263 StoreEntry *e;
264 ASState *asState = new ASState;
265 debugs(53, 3, "AS " << as);
266 snprintf(asres, 4096, "whois://%s/!gAS%d", Config.as_whois_server, as);
267 asState->as_number = as;
268 asState->request = HttpRequest::CreateFromUrl(asres);
269 assert(asState->request != NULL);
270
271 if ((e = storeGetPublic(asres, Http::METHOD_GET)) == NULL) {
272 e = storeCreateEntry(asres, asres, RequestFlags(), Http::METHOD_GET);
273 asState->sc = storeClientListAdd(e, asState);
274 FwdState::fwdStart(Comm::ConnectionPointer(), e, asState->request.getRaw());
275 } else {
276 e->lock("Asn");
277 asState->sc = storeClientListAdd(e, asState);
278 }
279
280 asState->entry = e;
281 StoreIOBuffer readBuffer (AS_REQBUF_SZ, asState->offset, asState->reqbuf);
282 storeClientCopy(asState->sc, e, readBuffer, asHandleReply, asState);
283 }
284
285 static void
286 asHandleReply(void *data, StoreIOBuffer result)
287 {
288 ASState *asState = (ASState *)data;
289 StoreEntry *e = asState->entry;
290 char *s;
291 char *t;
292 char *buf = asState->reqbuf;
293 int leftoversz = -1;
294
295 debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result.length);
296 debugs(53, 3, "asHandleReply: buffer='" << buf << "'");
297
298 /* First figure out whether we should abort the request */
299
300 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
301 delete asState;
302 return;
303 }
304
305 if (result.length == 0 && asState->dataRead) {
306 debugs(53, 3, "asHandleReply: Done: " << e->url());
307 delete asState;
308 return;
309 } else if (result.flags.error) {
310 debugs(53, DBG_IMPORTANT, "asHandleReply: Called with Error set and size=" << (unsigned int) result.length);
311 delete asState;
312 return;
313 } else if (e->getReply()->sline.status() != Http::scOkay) {
314 debugs(53, DBG_IMPORTANT, "WARNING: AS " << asState->as_number << " whois request failed");
315 delete asState;
316 return;
317 }
318
319 /*
320 * Next, attempt to parse our request
321 * Remembering that the actual buffer size is retsize + reqofs!
322 */
323 s = buf;
324
325 while ((size_t)(s - buf) < result.length + asState->reqofs && *s != '\0') {
326 while (*s && xisspace(*s))
327 ++s;
328
329 for (t = s; *t; ++t) {
330 if (xisspace(*t))
331 break;
332 }
333
334 if (*t == '\0') {
335 /* oof, word should continue on next block */
336 break;
337 }
338
339 *t = '\0';
340 debugs(53, 3, "asHandleReply: AS# " << s << " (" << asState->as_number << ")");
341 asnAddNet(s, asState->as_number);
342 s = t + 1;
343 asState->dataRead = true;
344 }
345
346 /*
347 * Next, grab the end of the 'valid data' in the buffer, and figure
348 * out how much data is left in our buffer, which we need to keep
349 * around for the next request
350 */
351 leftoversz = (asState->reqofs + result.length) - (s - buf);
352
353 assert(leftoversz >= 0);
354
355 /*
356 * Next, copy the left over data, from s to s + leftoversz to the
357 * beginning of the buffer
358 */
359 memmove(buf, s, leftoversz);
360
361 /*
362 * Next, update our offset and reqofs, and kick off a copy if required
363 */
364 asState->offset += result.length;
365
366 asState->reqofs = leftoversz;
367
368 debugs(53, 3, "asState->offset = " << asState->offset);
369
370 if (e->store_status == STORE_PENDING) {
371 debugs(53, 3, "asHandleReply: store_status == STORE_PENDING: " << e->url() );
372 StoreIOBuffer tempBuffer (AS_REQBUF_SZ - asState->reqofs,
373 asState->offset,
374 asState->reqbuf + asState->reqofs);
375 storeClientCopy(asState->sc,
376 e,
377 tempBuffer,
378 asHandleReply,
379 asState);
380 } else {
381 StoreIOBuffer tempBuffer;
382 debugs(53, 3, "asHandleReply: store complete, but data received " << e->url() );
383 tempBuffer.offset = asState->offset;
384 tempBuffer.length = AS_REQBUF_SZ - asState->reqofs;
385 tempBuffer.data = asState->reqbuf + asState->reqofs;
386 storeClientCopy(asState->sc,
387 e,
388 tempBuffer,
389 asHandleReply,
390 asState);
391 }
392 }
393
394 /**
395 * add a network (addr, mask) to the radix tree, with matching AS number
396 */
397 static int
398 asnAddNet(char *as_string, int as_number)
399 {
400 struct squid_radix_node *rn;
401 CbDataList<int> **Tail = NULL;
402 CbDataList<int> *q = NULL;
403 as_info *asinfo = NULL;
404
405 Ip::Address mask;
406 Ip::Address addr;
407 char *t;
408 int bitl;
409
410 t = strchr(as_string, '/');
411
412 if (t == NULL) {
413 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
414 return 0;
415 }
416
417 *t = '\0';
418 addr = as_string;
419 bitl = atoi(t + 1);
420
421 if (bitl < 0)
422 bitl = 0;
423
424 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
425 t = strchr(as_string, '.');
426
427 // generate Netbits Format Mask
428 mask.setNoAddr();
429 mask.applyMask(bitl, (t!=NULL?AF_INET:AF_INET6) );
430
431 debugs(53, 3, "asnAddNet: called for " << addr << "/" << mask );
432
433 rtentry_t *e = (rtentry_t *)xcalloc(1, sizeof(rtentry_t));
434
435 e->e_addr.addr = addr;
436
437 e->e_mask.addr = mask;
438
439 rn = squid_rn_lookup(&e->e_addr, &e->e_mask, AS_tree_head);
440
441 if (rn != NULL) {
442 asinfo = ((rtentry_t *) rn)->e_info;
443
444 if (asinfo->as_number->find(as_number)) {
445 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr << "/" << bitl << "' for AS " << as_number);
446 } else {
447 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
448
449 for (Tail = &asinfo->as_number; *Tail; Tail = &(*Tail)->next);
450 q = new CbDataList<int> (as_number);
451
452 *(Tail) = q;
453
454 e->e_info = asinfo;
455 }
456 } else {
457 q = new CbDataList<int> (as_number);
458 asinfo = (as_info *)xmalloc(sizeof(as_info));
459 asinfo->as_number = q;
460 squid_rn_addroute(&e->e_addr, &e->e_mask, AS_tree_head, e->e_nodes);
461 rn = squid_rn_match(&e->e_addr, AS_tree_head);
462 assert(rn != NULL);
463 e->e_info = asinfo;
464 }
465
466 if (rn == 0) { /* assert might expand to nothing */
467 xfree(asinfo);
468 delete q;
469 xfree(e);
470 debugs(53, 3, "asnAddNet: Could not add entry.");
471 return 0;
472 }
473
474 e->e_info = asinfo;
475 return 1;
476 }
477
478 static int
479 destroyRadixNode(struct squid_radix_node *rn, void *w)
480 {
481
482 struct squid_radix_node_head *rnh = (struct squid_radix_node_head *) w;
483
484 if (rn && !(rn->rn_flags & RNF_ROOT)) {
485 rtentry_t *e = (rtentry_t *) rn;
486 rn = squid_rn_delete(rn->rn_key, rn->rn_mask, rnh);
487
488 if (rn == 0)
489 debugs(53, 3, "destroyRadixNode: internal screwup");
490
491 destroyRadixNodeInfo(e->e_info);
492
493 xfree(rn);
494 }
495
496 return 1;
497 }
498
499 static void
500 destroyRadixNodeInfo(as_info * e_info)
501 {
502 CbDataList<int> *prev = NULL;
503 CbDataList<int> *data = e_info->as_number;
504
505 while (data) {
506 prev = data;
507 data = data->next;
508 delete prev;
509 }
510 }
511
512 static int
513 printRadixNode(struct squid_radix_node *rn, void *_sentry)
514 {
515 StoreEntry *sentry = (StoreEntry *)_sentry;
516 rtentry_t *e = (rtentry_t *) rn;
517 CbDataList<int> *q;
518 as_info *asinfo;
519 char buf[MAX_IPSTRLEN];
520 Ip::Address addr;
521 Ip::Address mask;
522
523 assert(e);
524 assert(e->e_info);
525 addr = e->e_addr.addr;
526 mask = e->e_mask.addr;
527 storeAppendPrintf(sentry, "%s/%d\t",
528 addr.toStr(buf, MAX_IPSTRLEN),
529 mask.cidr() );
530 asinfo = e->e_info;
531 assert(asinfo->as_number);
532
533 for (q = asinfo->as_number; q; q = q->next)
534 storeAppendPrintf(sentry, " %d", q->element);
535
536 storeAppendPrintf(sentry, "\n");
537
538 return 0;
539 }
540
541 ACLASN::~ACLASN()
542 {
543 if (data)
544 delete data;
545 }
546
547 bool
548
549 ACLASN::match(Ip::Address toMatch)
550 {
551 return asnMatchIp(data, toMatch);
552 }
553
554 wordlist *
555 ACLASN::dump()
556 {
557 wordlist *W = NULL;
558 char buf[32];
559 CbDataList<int> *ldata = data;
560
561 while (ldata != NULL) {
562 snprintf(buf, sizeof(buf), "%d", ldata->element);
563 wordlistAdd(&W, buf);
564 ldata = ldata->next;
565 }
566
567 return W;
568 }
569
570 bool
571 ACLASN::empty () const
572 {
573 return data == NULL;
574 }
575
576 void
577 ACLASN::parse()
578 {
579 CbDataList<int> **curlist = &data;
580 CbDataList<int> **Tail;
581 CbDataList<int> *q = NULL;
582 char *t = NULL;
583
584 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
585 while ((t = strtokFile())) {
586 q = new CbDataList<int> (atoi(t));
587 *(Tail) = q;
588 Tail = &q->next;
589 }
590 }
591
592 ACLData<Ip::Address> *
593 ACLASN::clone() const
594 {
595 if (data)
596 fatal ("cloning of ACLASN not implemented");
597
598 return new ACLASN(*this);
599 }
600
601 /* explicit template instantiation required for some systems */
602
603 template class ACLStrategised<Ip::Address>;
604
605 ACL::Prototype ACLASN::SourceRegistryProtoype(&ACLASN::SourceRegistryEntry_, "src_as");
606
607 ACLStrategised<Ip::Address> ACLASN::SourceRegistryEntry_(new ACLASN, ACLSourceASNStrategy::Instance(), "src_as");
608
609 ACL::Prototype ACLASN::DestinationRegistryProtoype(&ACLASN::DestinationRegistryEntry_, "dst_as");
610
611 ACLStrategised<Ip::Address> ACLASN::DestinationRegistryEntry_(new ACLASN, ACLDestinationASNStrategy::Instance(), "dst_as");
612
613 int
614 ACLSourceASNStrategy::match (ACLData<Ip::Address> * &data, ACLFilledChecklist *checklist, ACLFlags &)
615 {
616 return data->match(checklist->src_addr);
617 }
618
619 ACLSourceASNStrategy *
620 ACLSourceASNStrategy::Instance()
621 {
622 return &Instance_;
623 }
624
625 ACLSourceASNStrategy ACLSourceASNStrategy::Instance_;
626
627 int
628 ACLDestinationASNStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist, ACLFlags &)
629 {
630 const ipcache_addrs *ia = ipcache_gethostbyname(checklist->request->GetHost(), IP_LOOKUP_IF_MISS);
631
632 if (ia) {
633 for (int k = 0; k < (int) ia->count; ++k) {
634 if (data->match(ia->in_addrs[k]))
635 return 1;
636 }
637
638 return 0;
639
640 } else if (!checklist->request->flags.destinationIpLookedUp) {
641 /* No entry in cache, lookup not attempted */
642 debugs(28, 3, "asnMatchAcl: Can't yet compare '" << AclMatchedName << "' ACL for '" << checklist->request->GetHost() << "'");
643 if (checklist->goAsync(DestinationIPLookup::Instance()))
644 return -1;
645 // else fall through to noaddr match, hiding the lookup failure (XXX)
646 }
647 Ip::Address noaddr;
648 noaddr.setNoAddr();
649 return data->match(noaddr);
650 }
651
652 ACLDestinationASNStrategy *
653 ACLDestinationASNStrategy::Instance()
654 {
655 return &Instance_;
656 }
657
658 ACLDestinationASNStrategy ACLDestinationASNStrategy::Instance_;