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