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