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