3 * DEBUG: section 53 AS Number handling
4 * AUTHOR: Duane Wessels, Kostas Anagnostakis
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "mgr/Registration.h"
37 #include "HttpRequest.h"
38 #include "StoreClient.h"
42 #include "acl/Checklist.h"
43 #include "acl/SourceAsn.h"
44 #include "acl/DestinationAsn.h"
45 #include "acl/DestinationIp.h"
47 #include "HttpReply.h"
50 #include "StoreClient.h"
54 #define AS_REQBUF_SZ 4096
56 /* BEGIN of definitions for radix tree entries */
58 /* 32/128 bits address in memory with length */
65 m_ADDR() : len(sizeof(Ip::Address
)) {};
68 /* END of definitions for radix tree entries */
70 /* Head for ip to asn radix tree */
72 struct squid_radix_node_head
*AS_tree_head
;
74 /* explicit instantiation required for some systems */
76 /// \cond AUTODOCS-IGNORE
77 template cbdata_type CbDataList
<int>::CBDATA_CbDataList
;
81 * Structure for as number information. it could be simply
82 * a list but it's coded as a structure for future
83 * enhancements (e.g. expires)
86 CbDataList
<int> *as_number
;
87 time_t expires
; /* NOTUSED */
97 char reqbuf
[AS_REQBUF_SZ
];
101 /** entry into the radix tree */
103 struct squid_radix_node e_nodes
[2];
109 static int asnAddNet(char *, int);
111 static void asnCacheStart(int as
);
113 static STCB asHandleReply
;
115 #if defined(__cplusplus)
119 static int destroyRadixNode(struct squid_radix_node
*rn
, void *w
);
120 static int printRadixNode(struct squid_radix_node
*rn
, void *sentry
);
122 #if defined(__cplusplus)
126 void asnAclInitialize(ACL
* acls
);
128 static void asStateFree(void *data
);
130 static void destroyRadixNodeInfo(as_info
*);
132 static OBJH asnStats
;
137 asnMatchIp(CbDataList
<int> *data
, Ip::Address
&addr
)
139 struct squid_radix_node
*rn
;
142 CbDataList
<int> *a
= NULL
;
143 CbDataList
<int> *b
= NULL
;
145 debugs(53, 3, "asnMatchIp: Called for " << addr
);
147 if (AS_tree_head
== NULL
)
153 if (addr
.IsAnyAddr())
158 rn
= squid_rn_match(&m_addr
, AS_tree_head
);
161 debugs(53, 3, "asnMatchIp: Address not in as db.");
165 debugs(53, 3, "asnMatchIp: Found in db!");
166 e
= ((rtentry_t
*) rn
)->e_info
;
169 for (a
= data
; a
; a
= a
->next
)
170 for (b
= e
->as_number
; b
; b
= b
->next
)
171 if (a
->element
== b
->element
) {
172 debugs(53, 5, "asnMatchIp: Found a match!");
176 debugs(53, 5, "asnMatchIp: AS not in as db.");
181 ACLASN::prepareForUse()
183 for (CbDataList
<int> *i
= data
; i
; i
= i
->
185 asnCacheStart(i
->element
);
189 asnRegisterWithCacheManager(void)
191 Mgr::RegisterAction("asndb", "AS Number Database", asnStats
, 0, 1);
194 /* initialize the radix tree structure */
196 SQUIDCEXTERN
int squid_max_keylen
; /* yuck.. this is in lib/radix.c */
198 CBDATA_TYPE(ASState
);
202 static bool inited
= false;
203 squid_max_keylen
= 40;
204 CBDATA_INIT_TYPE(ASState
);
211 squid_rn_inithead(&AS_tree_head
, 8);
213 asnRegisterWithCacheManager();
219 squid_rn_walktree(AS_tree_head
, destroyRadixNode
, AS_tree_head
);
221 destroyRadixNode((struct squid_radix_node
*) 0, (void *) AS_tree_head
);
225 asnStats(StoreEntry
* sentry
)
227 storeAppendPrintf(sentry
, "Address \tAS Numbers\n");
228 squid_rn_walktree(AS_tree_head
, printRadixNode
, sentry
);
234 asnCacheStart(int as
)
236 LOCAL_ARRAY(char, asres
, 4096);
240 asState
= cbdataAlloc(ASState
);
241 asState
->dataRead
= 0;
242 debugs(53, 3, "asnCacheStart: AS " << as
);
243 snprintf(asres
, 4096, "whois://%s/!gAS%d", Config
.as_whois_server
, as
);
244 asState
->as_number
= as
;
245 req
= HttpRequest::CreateFromUrl(asres
);
247 asState
->request
= HTTPMSGLOCK(req
);
249 if ((e
= storeGetPublic(asres
, METHOD_GET
)) == NULL
) {
250 e
= storeCreateEntry(asres
, asres
, request_flags(), METHOD_GET
);
251 asState
->sc
= storeClientListAdd(e
, asState
);
252 FwdState::fwdStart(Comm::ConnectionPointer(), e
, asState
->request
);
256 asState
->sc
= storeClientListAdd(e
, asState
);
262 StoreIOBuffer
readBuffer (AS_REQBUF_SZ
, asState
->offset
, asState
->reqbuf
);
263 storeClientCopy(asState
->sc
,
271 asHandleReply(void *data
, StoreIOBuffer result
)
273 ASState
*asState
= (ASState
*)data
;
274 StoreEntry
*e
= asState
->entry
;
277 char *buf
= asState
->reqbuf
;
280 debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result
.length
);
281 debugs(53, 3, "asHandleReply: buffer='" << buf
<< "'");
283 /* First figure out whether we should abort the request */
285 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
)) {
286 asStateFree(asState
);
290 if (result
.length
== 0 && asState
->dataRead
) {
291 debugs(53, 3, "asHandleReply: Done: " << e
->url() );
292 asStateFree(asState
);
294 } else if (result
.flags
.error
) {
295 debugs(53, DBG_IMPORTANT
, "asHandleReply: Called with Error set and size=" << (unsigned int) result
.length
);
296 asStateFree(asState
);
298 } else if (HTTP_OK
!= e
->getReply()->sline
.status
) {
299 debugs(53, DBG_IMPORTANT
, "WARNING: AS " << asState
->as_number
<< " whois request failed");
300 asStateFree(asState
);
305 * Next, attempt to parse our request
306 * Remembering that the actual buffer size is retsize + reqofs!
310 while ((size_t)(s
- buf
) < result
.length
+ asState
->reqofs
&& *s
!= '\0') {
311 while (*s
&& xisspace(*s
))
314 for (t
= s
; *t
; ++t
) {
320 /* oof, word should continue on next block */
325 debugs(53, 3, "asHandleReply: AS# " << s
<< " (" << asState
->as_number
<< ")");
326 asnAddNet(s
, asState
->as_number
);
328 asState
->dataRead
= 1;
332 * Next, grab the end of the 'valid data' in the buffer, and figure
333 * out how much data is left in our buffer, which we need to keep
334 * around for the next request
336 leftoversz
= (asState
->reqofs
+ result
.length
) - (s
- buf
);
338 assert(leftoversz
>= 0);
341 * Next, copy the left over data, from s to s + leftoversz to the
342 * beginning of the buffer
344 memmove(buf
, s
, leftoversz
);
347 * Next, update our offset and reqofs, and kick off a copy if required
349 asState
->offset
+= result
.length
;
351 asState
->reqofs
= leftoversz
;
353 debugs(53, 3, "asState->offset = " << asState
->offset
);
355 if (e
->store_status
== STORE_PENDING
) {
356 debugs(53, 3, "asHandleReply: store_status == STORE_PENDING: " << e
->url() );
357 StoreIOBuffer
tempBuffer (AS_REQBUF_SZ
- asState
->reqofs
,
359 asState
->reqbuf
+ asState
->reqofs
);
360 storeClientCopy(asState
->sc
,
366 StoreIOBuffer tempBuffer
;
367 debugs(53, 3, "asHandleReply: store complete, but data received " << e
->url() );
368 tempBuffer
.offset
= asState
->offset
;
369 tempBuffer
.length
= AS_REQBUF_SZ
- asState
->reqofs
;
370 tempBuffer
.data
= asState
->reqbuf
+ asState
->reqofs
;
371 storeClientCopy(asState
->sc
,
380 asStateFree(void *data
)
382 ASState
*asState
= (ASState
*)data
;
383 debugs(53, 3, "asnStateFree: " << asState
->entry
->url() );
384 storeUnregister(asState
->sc
, asState
->entry
, asState
);
385 asState
->entry
->unlock();
386 HTTPMSGUNLOCK(asState
->request
);
391 * add a network (addr, mask) to the radix tree, with matching AS number
394 asnAddNet(char *as_string
, int as_number
)
398 struct squid_radix_node
*rn
;
399 CbDataList
<int> **Tail
= NULL
;
400 CbDataList
<int> *q
= NULL
;
401 as_info
*asinfo
= NULL
;
408 t
= strchr(as_string
, '/');
411 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
422 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
423 t
= strchr(as_string
, '.');
425 // generate Netbits Format Mask
427 mask
.ApplyMask(bitl
, (t
!=NULL
?AF_INET
:AF_INET6
) );
429 debugs(53, 3, "asnAddNet: called for " << addr
<< "/" << mask
);
431 e
= (rtentry_t
*)xmalloc(sizeof(rtentry_t
));
433 memset(e
, '\0', sizeof(rtentry_t
));
435 e
->e_addr
.addr
= addr
;
437 e
->e_mask
.addr
= mask
;
439 rn
= squid_rn_lookup(&e
->e_addr
, &e
->e_mask
, AS_tree_head
);
442 asinfo
= ((rtentry_t
*) rn
)->e_info
;
444 if (asinfo
->as_number
->find(as_number
)) {
445 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr
<< "/" << bitl
<< "' for AS " << as_number
);
447 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
449 for (Tail
= &asinfo
->as_number
; *Tail
; Tail
= &(*Tail
)->next
);
450 q
= new CbDataList
<int> (as_number
);
457 q
= new CbDataList
<int> (as_number
);
458 asinfo
= (as_info
*)xmalloc(sizeof(as_info
));
459 asinfo
->as_number
= q
;
460 rn
= squid_rn_addroute(&e
->e_addr
, &e
->e_mask
, AS_tree_head
, e
->e_nodes
);
461 rn
= squid_rn_match(&e
->e_addr
, AS_tree_head
);
466 if (rn
== 0) { /* assert might expand to nothing */
470 debugs(53, 3, "asnAddNet: Could not add entry.");
479 destroyRadixNode(struct squid_radix_node
*rn
, void *w
)
482 struct squid_radix_node_head
*rnh
= (struct squid_radix_node_head
*) w
;
484 if (rn
&& !(rn
->rn_flags
& RNF_ROOT
)) {
485 rtentry_t
*e
= (rtentry_t
*) rn
;
486 rn
= squid_rn_delete(rn
->rn_key
, rn
->rn_mask
, rnh
);
489 debugs(53, 3, "destroyRadixNode: internal screwup");
491 destroyRadixNodeInfo(e
->e_info
);
500 destroyRadixNodeInfo(as_info
* e_info
)
502 CbDataList
<int> *prev
= NULL
;
503 CbDataList
<int> *data
= e_info
->as_number
;
515 printRadixNode(struct squid_radix_node
*rn
, void *_sentry
)
517 StoreEntry
*sentry
= (StoreEntry
*)_sentry
;
518 rtentry_t
*e
= (rtentry_t
*) rn
;
521 char buf
[MAX_IPSTRLEN
];
527 addr
= e
->e_addr
.addr
;
528 mask
= e
->e_mask
.addr
;
529 storeAppendPrintf(sentry
, "%s/%d\t",
530 addr
.NtoA(buf
, MAX_IPSTRLEN
),
533 assert(asinfo
->as_number
);
535 for (q
= asinfo
->as_number
; q
; q
= q
->next
)
536 storeAppendPrintf(sentry
, " %d", q
->element
);
538 storeAppendPrintf(sentry
, "\n");
551 ACLASN::match(Ip::Address toMatch
)
553 return asnMatchIp(data
, toMatch
);
561 CbDataList
<int> *ldata
= data
;
563 while (ldata
!= NULL
) {
564 snprintf(buf
, sizeof(buf
), "%d", ldata
->element
);
565 wordlistAdd(&W
, buf
);
573 ACLASN::empty () const
581 CbDataList
<int> **curlist
= &data
;
582 CbDataList
<int> **Tail
;
583 CbDataList
<int> *q
= NULL
;
586 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
));
587 while ((t
= strtokFile())) {
588 q
= new CbDataList
<int> (atoi(t
));
594 ACLData
<Ip::Address
> *
595 ACLASN::clone() const
598 fatal ("cloning of ACLASN not implemented");
600 return new ACLASN(*this);
603 /* explicit template instantiation required for some systems */
605 template class ACLStrategised
<Ip::Address
>;
607 ACL::Prototype
ACLASN::SourceRegistryProtoype(&ACLASN::SourceRegistryEntry_
, "src_as");
609 ACLStrategised
<Ip::Address
> ACLASN::SourceRegistryEntry_(new ACLASN
, ACLSourceASNStrategy::Instance(), "src_as");
611 ACL::Prototype
ACLASN::DestinationRegistryProtoype(&ACLASN::DestinationRegistryEntry_
, "dst_as");
613 ACLStrategised
<Ip::Address
> ACLASN::DestinationRegistryEntry_(new ACLASN
, ACLDestinationASNStrategy::Instance(), "dst_as");
616 ACLSourceASNStrategy::match (ACLData
<Ip::Address
> * &data
, ACLFilledChecklist
*checklist
)
618 return data
->match(checklist
->src_addr
);
621 ACLSourceASNStrategy
*
622 ACLSourceASNStrategy::Instance()
627 ACLSourceASNStrategy
ACLSourceASNStrategy::Instance_
;
630 ACLDestinationASNStrategy::match (ACLData
<MatchType
> * &data
, ACLFilledChecklist
*checklist
)
632 const ipcache_addrs
*ia
= ipcache_gethostbyname(checklist
->request
->GetHost(), IP_LOOKUP_IF_MISS
);
635 for (int k
= 0; k
< (int) ia
->count
; ++k
) {
636 if (data
->match(ia
->in_addrs
[k
]))
642 } else if (!checklist
->request
->flags
.destinationIPLookedUp()) {
643 /* No entry in cache, lookup not attempted */
644 /* XXX FIXME: allow accessing the acl name here */
645 debugs(28, 3, "asnMatchAcl: Can't yet compare '" << "unknown" /*name*/ << "' ACL for '" << checklist
->request
->GetHost() << "'");
646 checklist
->changeState (DestinationIPLookup::Instance());
650 return data
->match(noaddr
);
656 ACLDestinationASNStrategy
*
657 ACLDestinationASNStrategy::Instance()
662 ACLDestinationASNStrategy
ACLDestinationASNStrategy::Instance_
;