3 * $Id: asn.cc,v 1.103 2006/02/17 18:10:59 wessels Exp $
5 * DEBUG: section 53 AS Number handling
6 * AUTHOR: Duane Wessels, Kostas Anagnostakis
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
38 #include "HttpRequest.h"
39 #include "StoreClient.h"
43 #include "ACLSourceASN.h"
44 #include "ACLDestinationASN.h"
45 #include "ACLDestinationIP.h"
46 #include "HttpReply.h"
50 #define AS_REQBUF_SZ 4096
52 /* BEGIN of definitions for radix tree entries */
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))
61 /* END of definitions for radix tree entries */
63 /* Head for ip to asn radix tree */
65 struct squid_radix_node_head
*AS_tree_head
;
67 /* explicit instantiation required for some systems */
69 template cbdata_type List
<int>
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)
81 time_t expires
; /* NOTUSED */
92 char reqbuf
[AS_REQBUF_SZ
];
96 typedef struct _ASState ASState
;
98 typedef struct _as_info as_info
;
100 /* entry into the radix tree */
105 struct squid_radix_node e_nodes
[2];
111 typedef struct _rtentry rtentry_t
;
113 static int asnAddNet(char *, int);
115 static void asnCacheStart(int as
);
117 static STCB asHandleReply
;
119 static int destroyRadixNode(struct squid_radix_node
*rn
, void *w
);
121 static int printRadixNode(struct squid_radix_node
*rn
, void *sentry
);
123 void asnAclInitialize(ACL
* acls
);
125 static void asStateFree(void *data
);
127 static void destroyRadixNodeInfo(as_info
*);
129 static OBJH asnStats
;
134 asnMatchIp(List
<int> *data
, struct IN_ADDR addr
)
138 struct squid_radix_node
*rn
;
143 lh
= ntohl(addr
.s_addr
);
144 debug(53, 3) ("asnMatchIp: Called for %s.\n", inet_ntoa(addr
));
146 if (AS_tree_head
== NULL
)
149 if (addr
.s_addr
== no_addr
.s_addr
)
152 if (addr
.s_addr
== any_addr
.s_addr
)
155 store_m_int(lh
, m_addr
);
157 rn
= squid_rn_match(m_addr
, AS_tree_head
);
160 debug(53, 3) ("asnMatchIp: Address not in as db.\n");
164 debug(53, 3) ("asnMatchIp: Found in db!\n");
165 e
= ((rtentry_t
*) rn
)->e_info
;
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");
175 debug(53, 5) ("asnMatchIp: AS not in as db.\n");
180 ACLASN::prepareForUse()
182 for (List
<int> *i
= data
; i
; i
= i
->
184 asnCacheStart(i
->element
);
187 /* initialize the radix tree structure */
189 SQUIDCEXTERN
int squid_max_keylen
; /* yuck.. this is in lib/radix.c */
191 CBDATA_TYPE(ASState
);
195 static int inited
= 0;
196 squid_max_keylen
= 40;
197 CBDATA_INIT_TYPE(ASState
);
202 squid_rn_inithead(&AS_tree_head
, 8);
204 cachemgrRegister("asndb", "AS Number Database", asnStats
, 0, 1);
210 squid_rn_walktree(AS_tree_head
, destroyRadixNode
, AS_tree_head
);
212 destroyRadixNode((struct squid_radix_node
*) 0, (void *) AS_tree_head
);
216 asnStats(StoreEntry
* sentry
)
218 storeAppendPrintf(sentry
, "Address \tAS Numbers\n");
219 squid_rn_walktree(AS_tree_head
, printRadixNode
, sentry
);
226 asnCacheStart(int as
)
228 LOCAL_ARRAY(char, asres
, 4096);
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
);
239 asState
->request
= HTTPMSGLOCK(req
);
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
);
247 asState
->sc
= storeClientListAdd(e
, asState
);
253 StoreIOBuffer
readBuffer (AS_REQBUF_SZ
, asState
->offset
, asState
->reqbuf
);
254 storeClientCopy(asState
->sc
,
262 asHandleReply(void *data
, StoreIOBuffer result
)
264 ASState
*asState
= (ASState
*)data
;
265 StoreEntry
*e
= asState
->entry
;
268 char *buf
= asState
->reqbuf
;
271 debug(53, 3) ("asHandleReply: Called with size=%u\n", (unsigned int)result
.length
);
272 debug(53, 3) ("asHandleReply: buffer='%s'\n", buf
);
274 /* First figure out whether we should abort the request */
276 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
)) {
277 asStateFree(asState
);
281 if (result
.length
== 0 && asState
->dataRead
) {
282 debug(53, 3) ("asHandleReply: Done: %s\n", storeUrl(e
));
283 asStateFree(asState
);
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
);
289 } else if (HTTP_OK
!= e
->getReply()->sline
.status
) {
290 debug(53, 1) ("WARNING: AS %d whois request failed\n",
292 asStateFree(asState
);
297 * Next, attempt to parse our request
298 * Remembering that the actual buffer size is retsize + reqofs!
302 while (s
- buf
< (off_t
)(result
.length
+ asState
->reqofs
) && *s
!= '\0') {
303 while (*s
&& xisspace(*s
))
306 for (t
= s
; *t
; t
++) {
312 /* oof, word should continue on next block */
317 debug(53, 3) ("asHandleReply: AS# %s (%d)\n", s
, asState
->as_number
);
318 asnAddNet(s
, asState
->as_number
);
320 asState
->dataRead
= 1;
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
328 leftoversz
= (asState
->reqofs
+ result
.length
) - (s
- buf
);
330 assert(leftoversz
>= 0);
333 * Next, copy the left over data, from s to s + leftoversz to the
334 * beginning of the buffer
336 xmemmove(buf
, s
, leftoversz
);
339 * Next, update our offset and reqofs, and kick off a copy if required
341 asState
->offset
+= result
.length
;
343 asState
->reqofs
= leftoversz
;
345 debug(53, 3) ("asState->offset = %ld\n", (long int) asState
->offset
);
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
,
351 asState
->reqbuf
+ asState
->reqofs
);
352 storeClientCopy(asState
->sc
,
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
,
372 asStateFree(void *data
)
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
);
383 /* add a network (addr, mask) to the radix tree, with matching AS
387 asnAddNet(char *as_string
, int as_number
)
389 rtentry_t
*e
= (rtentry_t
*)xmalloc(sizeof(rtentry_t
));
391 struct squid_radix_node
*rn
;
392 char dbg1
[32], dbg2
[32];
393 List
<int> **Tail
= NULL
;
395 as_info
*asinfo
= NULL
;
397 struct IN_ADDR in_a
, in_m
;
402 t
= strchr(as_string
, '/');
405 debug(53, 3) ("asnAddNet: failed, invalid response from whois server.\n");
410 addr
= inet_addr(as_string
);
419 mask
= bitl
? 0xfffffffful
<< (32 - bitl
) : 0;
425 xstrncpy(dbg1
, inet_ntoa(in_a
), 32);
427 xstrncpy(dbg2
, inet_ntoa(in_m
), 32);
431 /*mask = ntohl(mask); */
432 debug(53, 3) ("asnAddNet: called for %s/%s\n", dbg1
, dbg2
);
434 memset(e
, '\0', sizeof(rtentry_t
));
436 store_m_int(addr
, e
->e_addr
);
438 store_m_int(mask
, e
->e_mask
);
440 rn
= squid_rn_lookup(e
->e_addr
, e
->e_mask
, AS_tree_head
);
443 asinfo
= ((rtentry_t
*) rn
)->e_info
;
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
);
449 debug(53, 3) ("asnAddNet: Warning: Found a network with multiple AS numbers!\n");
451 for (Tail
= &asinfo
->as_number
; *Tail
; Tail
= &(*Tail
)->next
)
454 q
= new List
<int> (as_number
);
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
);
472 debug(53, 3) ("asnAddNet: Could not add entry.\n");
482 destroyRadixNode(struct squid_radix_node
*rn
, void *w
)
485 struct squid_radix_node_head
*rnh
= (struct squid_radix_node_head
*) w
;
487 if (rn
&& !(rn
->rn_flags
& RNF_ROOT
))
489 rtentry_t
*e
= (rtentry_t
*) rn
;
490 rn
= squid_rn_delete(rn
->rn_key
, rn
->rn_mask
, rnh
);
493 debug(53, 3) ("destroyRadixNode: internal screwup\n");
495 destroyRadixNodeInfo(e
->e_info
);
504 destroyRadixNodeInfo(as_info
* e_info
)
506 List
<int> *prev
= NULL
;
507 List
<int> *data
= e_info
->as_number
;
519 mask_len(u_long mask
)
526 while ((mask
& 1) == 0) {
536 printRadixNode(struct squid_radix_node
*rn
, void *_sentry
)
538 StoreEntry
*sentry
= (StoreEntry
*)_sentry
;
539 rtentry_t
*e
= (rtentry_t
*) rn
;
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
)));
553 assert(asinfo
->as_number
);
555 for (q
= asinfo
->as_number
; q
; q
= q
->next
)
556 storeAppendPrintf(sentry
, " %d", q
->element
);
558 storeAppendPrintf(sentry
, "\n");
571 ACLASN::match(struct IN_ADDR toMatch
)
573 return asnMatchIp(data
, toMatch
);
581 List
<int> *ldata
= data
;
583 while (ldata
!= NULL
) {
584 snprintf(buf
, sizeof(buf
), "%d", ldata
->element
);
585 wordlistAdd(&W
, buf
);
593 ACLASN::empty () const
601 List
<int> **curlist
= &data
;
606 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
))
609 while ((t
= strtokFile())) {
610 q
= new List
<int> (atoi(t
));
616 ACLData
<struct IN_ADDR
> *
617 ACLASN::clone() const
620 fatal ("cloning of ACLASN not implemented");
622 return new ACLASN(*this);
625 /* explicit template instantiation required for some systems */
627 template class ACLStrategised
<struct IN_ADDR
>
631 ACL::Prototype
ACLASN::SourceRegistryProtoype(&ACLASN::SourceRegistryEntry_
, "src_as");
633 ACLStrategised
<struct IN_ADDR
> ACLASN::SourceRegistryEntry_(new ACLASN
, ACLSourceASNStrategy::Instance(), "src_as");
635 ACL::Prototype
ACLASN::DestinationRegistryProtoype(&ACLASN::DestinationRegistryEntry_
, "dst_as");
637 ACLStrategised
<struct IN_ADDR
> ACLASN::DestinationRegistryEntry_(new ACLASN
, ACLDestinationASNStrategy::Instance(), "dst_as");
640 ACLSourceASNStrategy::match (ACLData
<MatchType
> * &data
, ACLChecklist
*checklist
)
642 return data
->match(checklist
->src_addr
);
645 ACLSourceASNStrategy
*
646 ACLSourceASNStrategy::Instance()
651 ACLSourceASNStrategy
ACLSourceASNStrategy::Instance_
;
655 ACLDestinationASNStrategy::match (ACLData
<MatchType
> * &data
, ACLChecklist
*checklist
)
657 const ipcache_addrs
*ia
= ipcache_gethostbyname(checklist
->request
->host
, IP_LOOKUP_IF_MISS
);
660 for (int k
= 0; k
< (int) ia
->count
; k
++) {
661 if (data
->match(ia
->in_addrs
[k
]))
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());
673 return data
->match(no_addr
);
679 ACLDestinationASNStrategy
*
680 ACLDestinationASNStrategy::Instance()
685 ACLDestinationASNStrategy
ACLDestinationASNStrategy::Instance_
;