]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/Asn.cc
Better fix for CBDATA type errors
[thirdparty/squid.git] / src / acl / Asn.cc
CommitLineData
445d18a4 1
3841dd46 2/*
445d18a4
AJ
3 * DEBUG: section 53 AS Number handling
4 * AUTHOR: Duane Wessels, Kostas Anagnostakis
3841dd46 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.
26ac0430 22 *
3841dd46 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.
26ac0430 27 *
3841dd46 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 *
3841dd46 32 */
33
582c2af2 34#include "squid.h"
445d18a4 35#include "acl/Acl.h"
3ad63615
AR
36#include "acl/Asn.h"
37#include "acl/Checklist.h"
445d18a4
AJ
38#include "acl/DestinationAsn.h"
39#include "acl/DestinationIp.h"
f206b652 40#include "acl/SourceAsn.h"
06390e4b 41#include "cache_cf.h"
eb13c21e 42#include "FwdState.h"
445d18a4 43#include "HttpReply.h"
f206b652 44#include "HttpRequest.h"
714e68b7 45#include "ipcache.h"
f206b652
FC
46#include "mgr/Registration.h"
47#include "radix.h"
48#include "RequestFlags.h"
4d5904f7 49#include "SquidConfig.h"
f206b652
FC
50#include "Store.h"
51#include "StoreClient.h"
e87137f1 52#include "StoreClient.h"
445d18a4
AJ
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
445d18a4
AJ
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
062e0b16
FC
117#if defined(__cplusplus)
118extern "C" {
119#endif
120
182fbea5
A
121 static int destroyRadixNode(struct squid_radix_node *rn, void *w);
122 static int printRadixNode(struct squid_radix_node *rn, void *sentry);
445d18a4 123
062e0b16
FC
124#if defined(__cplusplus)
125}
126#endif
127
445d18a4
AJ
128void asnAclInitialize(ACL * acls);
129
130static void asStateFree(void *data);
131
132static void destroyRadixNodeInfo(as_info *);
133
134static OBJH asnStats;
135
136/* PUBLIC */
137
138int
b7ac5457 139asnMatchIp(CbDataList<int> *data, Ip::Address &addr)
445d18a4
AJ
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
4dd643d5 152 if (addr.isNoAddr())
445d18a4
AJ
153 return 0;
154
4dd643d5 155 if (addr.isAnyAddr())
445d18a4
AJ
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
182void
183ACLASN::prepareForUse()
184{
185 for (CbDataList<int> *i = data; i; i = i->
186 next)
187 asnCacheStart(i->element);
188}
189
190static void
191asnRegisterWithCacheManager(void)
192{
8822ebee 193 Mgr::RegisterAction("asndb", "AS Number Database", asnStats, 0, 1);
445d18a4
AJ
194}
195
196/* initialize the radix tree structure */
197
198SQUIDCEXTERN int squid_max_keylen; /* yuck.. this is in lib/radix.c */
199
200CBDATA_TYPE(ASState);
201void
202asnInit(void)
203{
742a021b 204 static bool inited = false;
445d18a4
AJ
205 squid_max_keylen = 40;
206 CBDATA_INIT_TYPE(ASState);
207
742a021b
FC
208 if (!inited) {
209 inited = true;
445d18a4 210 squid_rn_init();
742a021b 211 }
445d18a4
AJ
212
213 squid_rn_inithead(&AS_tree_head, 8);
214
215 asnRegisterWithCacheManager();
216}
217
218void
219asnFreeMemory(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
226static void
227asnStats(StoreEntry * sentry)
228{
229 storeAppendPrintf(sentry, "Address \tAS Numbers\n");
230 squid_rn_walktree(AS_tree_head, printRadixNode, sentry);
231}
232
233/* PRIVATE */
234
445d18a4
AJ
235static void
236asnCacheStart(int as)
237{
238 LOCAL_ARRAY(char, asres, 4096);
239 StoreEntry *e;
445d18a4
AJ
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;
b248c2a3
AJ
246 asState->request = HttpRequest::CreateFromUrl(asres);
247 assert(NULL != asState->request);
248 HTTPMSGLOCK(asState->request);
445d18a4 249
c2a7cefd
AJ
250 if ((e = storeGetPublic(asres, Http::METHOD_GET)) == NULL) {
251 e = storeCreateEntry(asres, asres, RequestFlags(), Http::METHOD_GET);
445d18a4 252 asState->sc = storeClientListAdd(e, asState);
e83cc785 253 FwdState::fwdStart(Comm::ConnectionPointer(), e, asState->request);
445d18a4
AJ
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
271static void
272asHandleReply(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) {
e0236918 296 debugs(53, DBG_IMPORTANT, "asHandleReply: Called with Error set and size=" << (unsigned int) result.length);
445d18a4
AJ
297 asStateFree(asState);
298 return;
9b769c67 299 } else if (e->getReply()->sline.status() != Http::scOkay) {
e0236918 300 debugs(53, DBG_IMPORTANT, "WARNING: AS " << asState->as_number << " whois request failed");
445d18a4
AJ
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))
742a021b 313 ++s;
445d18a4 314
742a021b 315 for (t = s; *t; ++t) {
445d18a4
AJ
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 */
41d00cd3 345 memmove(buf, s, leftoversz);
445d18a4
AJ
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
380static void
381asStateFree(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
445d18a4
AJ
391/**
392 * add a network (addr, mask) to the radix tree, with matching AS number
393 */
394static int
395asnAddNet(char *as_string, int as_number)
396{
445d18a4
AJ
397 struct squid_radix_node *rn;
398 CbDataList<int> **Tail = NULL;
399 CbDataList<int> *q = NULL;
400 as_info *asinfo = NULL;
401
b7ac5457
AJ
402 Ip::Address mask;
403 Ip::Address addr;
445d18a4
AJ
404 char *t;
405 int bitl;
406
407 t = strchr(as_string, '/');
408
409 if (t == NULL) {
410 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
411 return 0;
412 }
413
414 *t = '\0';
415 addr = as_string;
416 bitl = atoi(t + 1);
417
418 if (bitl < 0)
419 bitl = 0;
420
421 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
422 t = strchr(as_string, '.');
423
424 // generate Netbits Format Mask
4dd643d5
AJ
425 mask.setNoAddr();
426 mask.applyMask(bitl, (t!=NULL?AF_INET:AF_INET6) );
445d18a4
AJ
427
428 debugs(53, 3, "asnAddNet: called for " << addr << "/" << mask );
429
726904ad 430 rtentry_t *e = (rtentry_t *)xcalloc(1, sizeof(rtentry_t));
445d18a4
AJ
431
432 e->e_addr.addr = addr;
433
434 e->e_mask.addr = mask;
435
436 rn = squid_rn_lookup(&e->e_addr, &e->e_mask, AS_tree_head);
437
438 if (rn != NULL) {
439 asinfo = ((rtentry_t *) rn)->e_info;
440
441 if (asinfo->as_number->find(as_number)) {
442 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr << "/" << bitl << "' for AS " << as_number);
443 } else {
444 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
445
446 for (Tail = &asinfo->as_number; *Tail; Tail = &(*Tail)->next);
447 q = new CbDataList<int> (as_number);
448
449 *(Tail) = q;
450
451 e->e_info = asinfo;
452 }
453 } else {
454 q = new CbDataList<int> (as_number);
455 asinfo = (as_info *)xmalloc(sizeof(as_info));
456 asinfo->as_number = q;
58ff1033 457 squid_rn_addroute(&e->e_addr, &e->e_mask, AS_tree_head, e->e_nodes);
445d18a4
AJ
458 rn = squid_rn_match(&e->e_addr, AS_tree_head);
459 assert(rn != NULL);
460 e->e_info = asinfo;
461 }
462
463 if (rn == 0) { /* assert might expand to nothing */
464 xfree(asinfo);
465 delete q;
466 xfree(e);
467 debugs(53, 3, "asnAddNet: Could not add entry.");
468 return 0;
469 }
470
471 e->e_info = asinfo;
472 return 1;
473}
474
475static int
476destroyRadixNode(struct squid_radix_node *rn, void *w)
477{
478
479 struct squid_radix_node_head *rnh = (struct squid_radix_node_head *) w;
480
481 if (rn && !(rn->rn_flags & RNF_ROOT)) {
482 rtentry_t *e = (rtentry_t *) rn;
483 rn = squid_rn_delete(rn->rn_key, rn->rn_mask, rnh);
484
485 if (rn == 0)
486 debugs(53, 3, "destroyRadixNode: internal screwup");
487
488 destroyRadixNodeInfo(e->e_info);
489
490 xfree(rn);
491 }
492
493 return 1;
494}
495
496static void
497destroyRadixNodeInfo(as_info * e_info)
498{
499 CbDataList<int> *prev = NULL;
500 CbDataList<int> *data = e_info->as_number;
501
502 while (data) {
503 prev = data;
504 data = data->next;
505 delete prev;
506 }
445d18a4
AJ
507}
508
509static int
510printRadixNode(struct squid_radix_node *rn, void *_sentry)
511{
512 StoreEntry *sentry = (StoreEntry *)_sentry;
513 rtentry_t *e = (rtentry_t *) rn;
514 CbDataList<int> *q;
515 as_info *asinfo;
516 char buf[MAX_IPSTRLEN];
b7ac5457
AJ
517 Ip::Address addr;
518 Ip::Address mask;
445d18a4
AJ
519
520 assert(e);
521 assert(e->e_info);
522 addr = e->e_addr.addr;
523 mask = e->e_mask.addr;
524 storeAppendPrintf(sentry, "%s/%d\t",
4dd643d5
AJ
525 addr.toStr(buf, MAX_IPSTRLEN),
526 mask.cidr() );
445d18a4
AJ
527 asinfo = e->e_info;
528 assert(asinfo->as_number);
529
530 for (q = asinfo->as_number; q; q = q->next)
531 storeAppendPrintf(sentry, " %d", q->element);
532
533 storeAppendPrintf(sentry, "\n");
534
535 return 0;
536}
537
538ACLASN::~ACLASN()
539{
540 if (data)
541 delete data;
542}
543
544bool
545
b7ac5457 546ACLASN::match(Ip::Address toMatch)
445d18a4
AJ
547{
548 return asnMatchIp(data, toMatch);
549}
550
551wordlist *
552ACLASN::dump()
553{
554 wordlist *W = NULL;
555 char buf[32];
556 CbDataList<int> *ldata = data;
557
558 while (ldata != NULL) {
559 snprintf(buf, sizeof(buf), "%d", ldata->element);
560 wordlistAdd(&W, buf);
561 ldata = ldata->next;
562 }
563
564 return W;
565}
566
567bool
568ACLASN::empty () const
569{
570 return data == NULL;
571}
572
573void
574ACLASN::parse()
575{
576 CbDataList<int> **curlist = &data;
577 CbDataList<int> **Tail;
578 CbDataList<int> *q = NULL;
579 char *t = NULL;
580
581 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
582 while ((t = strtokFile())) {
583 q = new CbDataList<int> (atoi(t));
584 *(Tail) = q;
585 Tail = &q->next;
586 }
587}
588
b7ac5457 589ACLData<Ip::Address> *
445d18a4
AJ
590ACLASN::clone() const
591{
592 if (data)
593 fatal ("cloning of ACLASN not implemented");
594
595 return new ACLASN(*this);
596}
597
598/* explicit template instantiation required for some systems */
599
b7ac5457 600template class ACLStrategised<Ip::Address>;
445d18a4
AJ
601
602ACL::Prototype ACLASN::SourceRegistryProtoype(&ACLASN::SourceRegistryEntry_, "src_as");
603
b7ac5457 604ACLStrategised<Ip::Address> ACLASN::SourceRegistryEntry_(new ACLASN, ACLSourceASNStrategy::Instance(), "src_as");
445d18a4
AJ
605
606ACL::Prototype ACLASN::DestinationRegistryProtoype(&ACLASN::DestinationRegistryEntry_, "dst_as");
607
b7ac5457 608ACLStrategised<Ip::Address> ACLASN::DestinationRegistryEntry_(new ACLASN, ACLDestinationASNStrategy::Instance(), "dst_as");
445d18a4
AJ
609
610int
33810b1d 611ACLSourceASNStrategy::match (ACLData<Ip::Address> * &data, ACLFilledChecklist *checklist, ACLFlags &)
445d18a4
AJ
612{
613 return data->match(checklist->src_addr);
614}
615
616ACLSourceASNStrategy *
617ACLSourceASNStrategy::Instance()
618{
619 return &Instance_;
620}
621
622ACLSourceASNStrategy ACLSourceASNStrategy::Instance_;
623
445d18a4 624int
33810b1d 625ACLDestinationASNStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist, ACLFlags &)
445d18a4
AJ
626{
627 const ipcache_addrs *ia = ipcache_gethostbyname(checklist->request->GetHost(), IP_LOOKUP_IF_MISS);
628
629 if (ia) {
742a021b 630 for (int k = 0; k < (int) ia->count; ++k) {
445d18a4
AJ
631 if (data->match(ia->in_addrs[k]))
632 return 1;
633 }
634
635 return 0;
636
450fe1cb 637 } else if (!checklist->request->flags.destinationIpLookedUp) {
445d18a4 638 /* No entry in cache, lookup not attempted */
0160be3b 639 debugs(28, 3, "asnMatchAcl: Can't yet compare '" << AclMatchedName << "' ACL for '" << checklist->request->GetHost() << "'");
6f58d7d7
AR
640 if (checklist->goAsync(DestinationIPLookup::Instance()))
641 return -1;
642 // else fall through to noaddr match, hiding the lookup failure (XXX)
643 }
e936c41c 644 Ip::Address noaddr;
4dd643d5 645 noaddr.setNoAddr();
e936c41c 646 return data->match(noaddr);
445d18a4
AJ
647}
648
649ACLDestinationASNStrategy *
650ACLDestinationASNStrategy::Instance()
651{
652 return &Instance_;
653}
3841dd46 654
445d18a4 655ACLDestinationASNStrategy ACLDestinationASNStrategy::Instance_;