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