3 * $Id: asn.cc,v 1.116 2007/12/14 23:11:45 amosjeffries 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.
37 #include "CacheManager.h"
39 #include "HttpRequest.h"
40 #include "StoreClient.h"
44 #include "ACLSourceASN.h"
45 #include "ACLDestinationASN.h"
46 #include "ACLDestinationIP.h"
47 #include "HttpReply.h"
52 #define AS_REQBUF_SZ 4096
54 /* BEGIN of definitions for radix tree entries */
57 /* AYJ INET6 : Why are these objects being converted to raw u_char memory for use ? */
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))
66 /* END of definitions for radix tree entries */
68 /* Head for ip to asn radix tree */
70 struct squid_radix_node_head
*AS_tree_head
;
72 /* explicit instantiation required for some systems */
74 template cbdata_type List
<int>
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)
86 time_t expires
; /* NOTUSED */
97 char reqbuf
[AS_REQBUF_SZ
];
101 /** entry into the radix tree */
104 struct squid_radix_node e_nodes
[2];
110 static int asnAddNet(char *, int);
112 static void asnCacheStart(int as
);
114 static STCB asHandleReply
;
116 static int destroyRadixNode(struct squid_radix_node
*rn
, void *w
);
118 static int printRadixNode(struct squid_radix_node
*rn
, void *sentry
);
120 void asnAclInitialize(ACL
* acls
);
122 static void asStateFree(void *data
);
124 static void destroyRadixNodeInfo(as_info
*);
126 static OBJH asnStats
;
131 asnMatchIp(List
<int> *data
, IPAddress
&addr
)
133 struct squid_radix_node
*rn
;
139 debugs(53, 3, "asnMatchIp: Called for " << addr
);
141 if (AS_tree_head
== NULL
)
147 if (addr
.IsAnyAddr())
150 store_m_ADDR(addr
, m_addr
);
152 rn
= squid_rn_match(m_addr
, AS_tree_head
);
155 debugs(53, 3, "asnMatchIp: Address not in as db.");
159 debugs(53, 3, "asnMatchIp: Found in db!");
160 e
= ((rtentry_t
*) rn
)->e_info
;
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!");
170 debugs(53, 5, "asnMatchIp: AS not in as db.");
175 ACLASN::prepareForUse()
177 for (List
<int> *i
= data
; i
; i
= i
->
179 asnCacheStart(i
->element
);
182 /* initialize the radix tree structure */
184 SQUIDCEXTERN
int squid_max_keylen
; /* yuck.. this is in lib/radix.c */
186 CBDATA_TYPE(ASState
);
190 static int inited
= 0;
191 squid_max_keylen
= 40;
192 CBDATA_INIT_TYPE(ASState
);
197 squid_rn_inithead(&AS_tree_head
, 8);
201 asnRegisterWithCacheManager(CacheManager
& manager
)
203 manager
.registerAction("asndb", "AS Number Database", asnStats
, 0, 1);
209 squid_rn_walktree(AS_tree_head
, destroyRadixNode
, AS_tree_head
);
211 destroyRadixNode((struct squid_radix_node
*) 0, (void *) AS_tree_head
);
215 asnStats(StoreEntry
* sentry
)
217 storeAppendPrintf(sentry
, "Address \tAS Numbers\n");
218 squid_rn_walktree(AS_tree_head
, printRadixNode
, sentry
);
225 asnCacheStart(int as
)
227 LOCAL_ARRAY(char, asres
, 4096);
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
);
238 asState
->request
= HTTPMSGLOCK(req
);
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
);
249 asState
->sc
= storeClientListAdd(e
, asState
);
255 StoreIOBuffer
readBuffer (AS_REQBUF_SZ
, asState
->offset
, asState
->reqbuf
);
256 storeClientCopy(asState
->sc
,
264 asHandleReply(void *data
, StoreIOBuffer result
)
266 ASState
*asState
= (ASState
*)data
;
267 StoreEntry
*e
= asState
->entry
;
270 char *buf
= asState
->reqbuf
;
273 debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result
.length
);
274 debugs(53, 3, "asHandleReply: buffer='" << buf
<< "'");
276 /* First figure out whether we should abort the request */
278 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
)) {
279 asStateFree(asState
);
283 if (result
.length
== 0 && asState
->dataRead
) {
284 debugs(53, 3, "asHandleReply: Done: " << e
->url() );
285 asStateFree(asState
);
287 } else if (result
.flags
.error
) {
288 debugs(53, 1, "asHandleReply: Called with Error set and size=" << (unsigned int) result
.length
);
289 asStateFree(asState
);
291 } else if (HTTP_OK
!= e
->getReply()->sline
.status
) {
292 debugs(53, 1, "WARNING: AS " << asState
->as_number
<< " whois request failed");
293 asStateFree(asState
);
298 * Next, attempt to parse our request
299 * Remembering that the actual buffer size is retsize + reqofs!
303 while (s
- buf
< (off_t
)(result
.length
+ asState
->reqofs
) && *s
!= '\0') {
304 while (*s
&& xisspace(*s
))
307 for (t
= s
; *t
; t
++) {
313 /* oof, word should continue on next block */
318 debugs(53, 3, "asHandleReply: AS# " << s
<< " (" << asState
->as_number
<< ")");
319 asnAddNet(s
, asState
->as_number
);
321 asState
->dataRead
= 1;
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
329 leftoversz
= (asState
->reqofs
+ result
.length
) - (s
- buf
);
331 assert(leftoversz
>= 0);
334 * Next, copy the left over data, from s to s + leftoversz to the
335 * beginning of the buffer
337 xmemmove(buf
, s
, leftoversz
);
340 * Next, update our offset and reqofs, and kick off a copy if required
342 asState
->offset
+= result
.length
;
344 asState
->reqofs
= leftoversz
;
346 debugs(53, 3, "asState->offset = " << asState
->offset
);
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
,
352 asState
->reqbuf
+ asState
->reqofs
);
353 storeClientCopy(asState
->sc
,
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
,
373 asStateFree(void *data
)
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
);
384 /* add a network (addr, mask) to the radix tree, with matching AS
388 asnAddNet(char *as_string
, int as_number
)
392 struct squid_radix_node
*rn
;
393 List
<int> **Tail
= NULL
;
395 as_info
*asinfo
= NULL
;
402 t
= strchr(as_string
, '/');
405 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
416 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
417 t
= strchr(as_string
, '.');
419 // generate Netbits Format Mask
421 mask
.ApplyMask(bitl
, (t
!=NULL
?AF_INET
:AF_INET6
) );
423 debugs(53, 3, "asnAddNet: called for " << addr
<< "/" << mask
);
425 e
= (rtentry_t
*)xmalloc(sizeof(rtentry_t
));
427 memset(e
, '\0', sizeof(rtentry_t
));
429 store_m_ADDR(addr
, e
->e_addr
);
431 store_m_ADDR(mask
, e
->e_mask
);
433 rn
= squid_rn_lookup(e
->e_addr
, e
->e_mask
, AS_tree_head
);
436 asinfo
= ((rtentry_t
*) rn
)->e_info
;
438 if (asinfo
->as_number
->find(as_number
)) {
439 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr
<< "/" << bitl
<< "' for AS " << as_number
);
441 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
443 for (Tail
= &asinfo
->as_number
; *Tail
; Tail
= &(*Tail
)->next
)
446 q
= new List
<int> (as_number
);
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
);
462 if (rn
== 0) { /* assert might expand to nothing */
466 debugs(53, 3, "asnAddNet: Could not add entry.");
475 destroyRadixNode(struct squid_radix_node
*rn
, void *w
)
478 struct squid_radix_node_head
*rnh
= (struct squid_radix_node_head
*) w
;
480 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
);
486 debugs(53, 3, "destroyRadixNode: internal screwup");
488 destroyRadixNodeInfo(e
->e_info
);
497 destroyRadixNodeInfo(as_info
* e_info
)
499 List
<int> *prev
= NULL
;
500 List
<int> *data
= e_info
->as_number
;
512 printRadixNode(struct squid_radix_node
*rn
, void *_sentry
)
514 StoreEntry
*sentry
= (StoreEntry
*)_sentry
;
515 rtentry_t
*e
= (rtentry_t
*) rn
;
518 char buf
[MAX_IPSTRLEN
];
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
),
530 assert(asinfo
->as_number
);
532 for (q
= asinfo
->as_number
; q
; q
= q
->next
)
533 storeAppendPrintf(sentry
, " %d", q
->element
);
535 storeAppendPrintf(sentry
, "\n");
548 ACLASN::match(IPAddress toMatch
)
550 return asnMatchIp(data
, toMatch
);
558 List
<int> *ldata
= data
;
560 while (ldata
!= NULL
) {
561 snprintf(buf
, sizeof(buf
), "%d", ldata
->element
);
562 wordlistAdd(&W
, buf
);
570 ACLASN::empty () const
578 List
<int> **curlist
= &data
;
583 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
))
586 while ((t
= strtokFile())) {
587 q
= new List
<int> (atoi(t
));
594 ACLASN::clone() const
597 fatal ("cloning of ACLASN not implemented");
599 return new ACLASN(*this);
602 /* explicit template instantiation required for some systems */
604 template class ACLStrategised
<IPAddress
>
608 ACL::Prototype
ACLASN::SourceRegistryProtoype(&ACLASN::SourceRegistryEntry_
, "src_as");
610 ACLStrategised
<IPAddress
> ACLASN::SourceRegistryEntry_(new ACLASN
, ACLSourceASNStrategy::Instance(), "src_as");
612 ACL::Prototype
ACLASN::DestinationRegistryProtoype(&ACLASN::DestinationRegistryEntry_
, "dst_as");
614 ACLStrategised
<IPAddress
> ACLASN::DestinationRegistryEntry_(new ACLASN
, ACLDestinationASNStrategy::Instance(), "dst_as");
617 ACLSourceASNStrategy::match (ACLData
<IPAddress
> * &data
, ACLChecklist
*checklist
)
619 return data
->match(checklist
->src_addr
);
622 ACLSourceASNStrategy
*
623 ACLSourceASNStrategy::Instance()
628 ACLSourceASNStrategy
ACLSourceASNStrategy::Instance_
;
632 ACLDestinationASNStrategy::match (ACLData
<MatchType
> * &data
, ACLChecklist
*checklist
)
634 const ipcache_addrs
*ia
= ipcache_gethostbyname(checklist
->request
->GetHost(), IP_LOOKUP_IF_MISS
);
637 for (int k
= 0; k
< (int) ia
->count
; k
++) {
638 if (data
->match(ia
->in_addrs
[k
]))
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());
650 IPAddress noaddr
; noaddr
.SetNoAddr();
651 return data
->match(noaddr
);
657 ACLDestinationASNStrategy
*
658 ACLDestinationASNStrategy::Instance()
663 ACLDestinationASNStrategy
ACLDestinationASNStrategy::Instance_
;