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