]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Asn.cc
59037e2a391a66cebf4be809c70d1fab230267db
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 53 AS Number handling */
14 #include "acl/Checklist.h"
15 #include "acl/DestinationAsn.h"
16 #include "acl/DestinationIp.h"
17 #include "acl/SourceAsn.h"
18 #include "acl/Strategised.h"
20 #include "HttpReply.h"
21 #include "HttpRequest.h"
23 #include "MasterXaction.h"
24 #include "mgr/Registration.h"
26 #include "RequestFlags.h"
27 #include "SquidConfig.h"
29 #include "StoreClient.h"
32 #define AS_REQBUF_SZ 4096
35 /* BEGIN of definitions for radix tree entries */
37 /* 32/128 bits address in memory with length */
44 m_ADDR() : len(sizeof(Ip::Address
)) {};
47 /* END of definitions for radix tree entries */
49 /* Head for ip to asn radix tree */
51 struct squid_radix_node_head
*AS_tree_head
;
53 /* explicit instantiation required for some systems */
55 /// \cond AUTODOCS_IGNORE
56 template cbdata_type CbDataList
<int>::CBDATA_CbDataList
;
60 * Structure for as number information. it could be simply
61 * a list but it's coded as a structure for future
62 * enhancements (e.g. expires)
65 CbDataList
<int> *as_number
;
66 time_t expires
; /* NOTUSED */
71 CBDATA_CLASS(ASState
);
75 memset(reqbuf
, 0, sizeof(reqbuf
));
79 debugs(53, 3, entry
->url());
80 storeUnregister(sc
, entry
, this);
81 entry
->unlock("~ASState");
86 StoreEntry
*entry
= nullptr;
87 store_client
*sc
= nullptr;
88 HttpRequest::Pointer request
;
92 char reqbuf
[AS_REQBUF_SZ
];
93 bool dataRead
= false;
96 CBDATA_CLASS_INIT(ASState
);
98 /** entry into the radix tree */
100 struct squid_radix_node e_nodes
[2];
106 static int asnAddNet(char *, int);
108 static void asnCacheStart(int as
);
110 static STCB asHandleReply
;
112 #if defined(__cplusplus)
116 static int destroyRadixNode(struct squid_radix_node
*rn
, void *w
);
117 static int printRadixNode(struct squid_radix_node
*rn
, void *sentry
);
119 #if defined(__cplusplus)
123 void asnAclInitialize(ACL
* acls
);
125 static void destroyRadixNodeInfo(as_info
*);
127 static OBJH asnStats
;
132 asnMatchIp(CbDataList
<int> *data
, Ip::Address
&addr
)
134 struct squid_radix_node
*rn
;
137 CbDataList
<int> *a
= nullptr;
138 CbDataList
<int> *b
= nullptr;
140 debugs(53, 3, "asnMatchIp: Called for " << addr
);
142 if (AS_tree_head
== nullptr)
148 if (addr
.isAnyAddr())
153 rn
= squid_rn_match(&m_addr
, AS_tree_head
);
156 debugs(53, 3, "asnMatchIp: Address not in as db.");
160 debugs(53, 3, "asnMatchIp: Found in db!");
161 e
= ((rtentry_t
*) rn
)->e_info
;
164 for (a
= data
; a
; a
= a
->next
)
165 for (b
= e
->as_number
; b
; b
= b
->next
)
166 if (a
->element
== b
->element
) {
167 debugs(53, 5, "asnMatchIp: Found a match!");
171 debugs(53, 5, "asnMatchIp: AS not in as db.");
176 ACLASN::prepareForUse()
178 for (CbDataList
<int> *i
= data
; i
; i
= i
->
180 asnCacheStart(i
->element
);
184 asnRegisterWithCacheManager(void)
186 Mgr::RegisterAction("asndb", "AS Number Database", asnStats
, 0, 1);
189 /* initialize the radix tree structure */
191 SQUIDCEXTERN
int squid_max_keylen
; /* yuck.. this is in lib/radix.c */
196 static bool inited
= false;
197 squid_max_keylen
= 40;
204 squid_rn_inithead(&AS_tree_head
, 8);
206 asnRegisterWithCacheManager();
212 squid_rn_walktree(AS_tree_head
, destroyRadixNode
, AS_tree_head
);
214 destroyRadixNode((struct squid_radix_node
*) nullptr, (void *) AS_tree_head
);
218 asnStats(StoreEntry
* sentry
)
220 storeAppendPrintf(sentry
, "Address \tAS Numbers\n");
221 squid_rn_walktree(AS_tree_head
, printRadixNode
, sentry
);
227 asnCacheStart(int as
)
229 AnyP::Uri
whoisUrl(AnyP::PROTO_WHOIS
);
230 whoisUrl
.host(Config
.as_whois_server
);
231 whoisUrl
.defaultPort();
233 SBuf
asPath("/!gAS");
234 asPath
.appendf("%d", as
);
235 whoisUrl
.path(asPath
);
237 debugs(53, 3, "AS " << as
);
238 ASState
*asState
= new ASState
;
239 asState
->as_number
= as
;
240 const auto mx
= MasterXaction::MakePortless
<XactionInitiator::initAsn
>();
241 asState
->request
= new HttpRequest(mx
);
242 asState
->request
->url
= whoisUrl
;
243 asState
->request
->method
= Http::METHOD_GET
;
245 // XXX: performance regression, c_str() reallocates
246 const auto asres
= xstrdup(whoisUrl
.absolute().c_str());
248 // XXX: Missing a hittingRequiresCollapsing() && startCollapsingOn() check.
249 auto e
= storeGetPublic(asres
, Http::METHOD_GET
);
251 e
= storeCreateEntry(asres
, asres
, RequestFlags(), Http::METHOD_GET
);
252 asState
->sc
= storeClientListAdd(e
, asState
);
253 FwdState::fwdStart(Comm::ConnectionPointer(), e
, asState
->request
.getRaw());
256 asState
->sc
= storeClientListAdd(e
, asState
);
261 StoreIOBuffer
readBuffer (AS_REQBUF_SZ
, asState
->offset
, asState
->reqbuf
);
262 storeClientCopy(asState
->sc
, e
, readBuffer
, asHandleReply
, asState
);
266 asHandleReply(void *data
, StoreIOBuffer result
)
268 ASState
*asState
= (ASState
*)data
;
269 StoreEntry
*e
= asState
->entry
;
272 char *buf
= asState
->reqbuf
;
275 debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result
.length
);
276 debugs(53, 3, "asHandleReply: buffer='" << buf
<< "'");
278 /* First figure out whether we should abort the request */
280 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
)) {
285 if (result
.length
== 0 && asState
->dataRead
) {
286 debugs(53, 3, "asHandleReply: Done: " << e
->url());
289 } else if (result
.flags
.error
) {
290 debugs(53, DBG_IMPORTANT
, "ERROR: asHandleReply: Called with Error set and size=" << (unsigned int) result
.length
);
293 } else if (e
->mem().baseReply().sline
.status() != Http::scOkay
) {
294 debugs(53, DBG_IMPORTANT
, "WARNING: AS " << asState
->as_number
<< " whois request failed");
300 * Next, attempt to parse our request
301 * Remembering that the actual buffer size is retsize + reqofs!
305 while ((size_t)(s
- buf
) < result
.length
+ asState
->reqofs
&& *s
!= '\0') {
306 while (*s
&& xisspace(*s
))
309 for (t
= s
; *t
; ++t
) {
315 /* oof, word should continue on next block */
320 debugs(53, 3, "asHandleReply: AS# " << s
<< " (" << asState
->as_number
<< ")");
321 asnAddNet(s
, asState
->as_number
);
323 asState
->dataRead
= true;
327 * Next, grab the end of the 'valid data' in the buffer, and figure
328 * out how much data is left in our buffer, which we need to keep
329 * around for the next request
331 leftoversz
= (asState
->reqofs
+ result
.length
) - (s
- buf
);
333 assert(leftoversz
>= 0);
336 * Next, copy the left over data, from s to s + leftoversz to the
337 * beginning of the buffer
339 memmove(buf
, s
, leftoversz
);
342 * Next, update our offset and reqofs, and kick off a copy if required
344 asState
->offset
+= result
.length
;
346 asState
->reqofs
= leftoversz
;
348 debugs(53, 3, "asState->offset = " << asState
->offset
);
350 if (e
->store_status
== STORE_PENDING
) {
351 debugs(53, 3, "asHandleReply: store_status == STORE_PENDING: " << e
->url() );
352 StoreIOBuffer
tempBuffer (AS_REQBUF_SZ
- asState
->reqofs
,
354 asState
->reqbuf
+ asState
->reqofs
);
355 storeClientCopy(asState
->sc
,
361 StoreIOBuffer tempBuffer
;
362 debugs(53, 3, "asHandleReply: store complete, but data received " << e
->url() );
363 tempBuffer
.offset
= asState
->offset
;
364 tempBuffer
.length
= AS_REQBUF_SZ
- asState
->reqofs
;
365 tempBuffer
.data
= asState
->reqbuf
+ asState
->reqofs
;
366 storeClientCopy(asState
->sc
,
375 * add a network (addr, mask) to the radix tree, with matching AS number
378 asnAddNet(char *as_string
, int as_number
)
380 struct squid_radix_node
*rn
;
381 CbDataList
<int> **Tail
= nullptr;
382 CbDataList
<int> *q
= nullptr;
383 as_info
*asinfo
= nullptr;
390 t
= strchr(as_string
, '/');
393 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
404 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
405 t
= strchr(as_string
, '.');
407 // generate Netbits Format Mask
409 mask
.applyMask(bitl
, (t
!=nullptr?AF_INET
:AF_INET6
) );
411 debugs(53, 3, "asnAddNet: called for " << addr
<< "/" << mask
);
413 rtentry_t
*e
= (rtentry_t
*)xcalloc(1, sizeof(rtentry_t
));
415 e
->e_addr
.addr
= addr
;
417 e
->e_mask
.addr
= mask
;
419 rn
= squid_rn_lookup(&e
->e_addr
, &e
->e_mask
, AS_tree_head
);
422 asinfo
= ((rtentry_t
*) rn
)->e_info
;
424 if (asinfo
->as_number
->find(as_number
)) {
425 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr
<< "/" << bitl
<< "' for AS " << as_number
);
427 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
429 for (Tail
= &asinfo
->as_number
; *Tail
; Tail
= &(*Tail
)->next
);
430 q
= new CbDataList
<int> (as_number
);
437 q
= new CbDataList
<int> (as_number
);
438 asinfo
= (as_info
*)xmalloc(sizeof(as_info
));
439 asinfo
->as_number
= q
;
440 squid_rn_addroute(&e
->e_addr
, &e
->e_mask
, AS_tree_head
, e
->e_nodes
);
441 rn
= squid_rn_match(&e
->e_addr
, AS_tree_head
);
442 assert(rn
!= nullptr);
446 if (rn
== nullptr) { /* assert might expand to nothing */
450 debugs(53, 3, "asnAddNet: Could not add entry.");
459 destroyRadixNode(struct squid_radix_node
*rn
, void *w
)
462 struct squid_radix_node_head
*rnh
= (struct squid_radix_node_head
*) w
;
464 if (rn
&& !(rn
->rn_flags
& RNF_ROOT
)) {
465 rtentry_t
*e
= (rtentry_t
*) rn
;
466 rn
= squid_rn_delete(rn
->rn_key
, rn
->rn_mask
, rnh
);
469 debugs(53, 3, "destroyRadixNode: internal screwup");
471 destroyRadixNodeInfo(e
->e_info
);
480 destroyRadixNodeInfo(as_info
* e_info
)
482 CbDataList
<int> *prev
= nullptr;
483 CbDataList
<int> *data
= e_info
->as_number
;
493 printRadixNode(struct squid_radix_node
*rn
, void *_sentry
)
495 StoreEntry
*sentry
= (StoreEntry
*)_sentry
;
496 rtentry_t
*e
= (rtentry_t
*) rn
;
499 char buf
[MAX_IPSTRLEN
];
505 addr
= e
->e_addr
.addr
;
506 mask
= e
->e_mask
.addr
;
507 storeAppendPrintf(sentry
, "%s/%d\t",
508 addr
.toStr(buf
, MAX_IPSTRLEN
),
511 assert(asinfo
->as_number
);
513 for (q
= asinfo
->as_number
; q
; q
= q
->next
)
514 storeAppendPrintf(sentry
, " %d", q
->element
);
516 storeAppendPrintf(sentry
, "\n");
529 ACLASN::match(Ip::Address toMatch
)
531 return asnMatchIp(data
, toMatch
);
539 CbDataList
<int> *ldata
= data
;
541 while (ldata
!= nullptr) {
543 s
.Printf("%d", ldata
->element
);
552 ACLASN::empty () const
554 return data
== nullptr;
560 CbDataList
<int> **curlist
= &data
;
561 CbDataList
<int> **Tail
;
562 CbDataList
<int> *q
= nullptr;
565 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
));
566 while ((t
= ConfigParser::strtokFile())) {
567 q
= new CbDataList
<int> (atoi(t
));
573 /* explicit template instantiation required for some systems */
575 template class ACLStrategised
<Ip::Address
>;
578 ACLSourceASNStrategy::match (ACLData
<Ip::Address
> * &data
, ACLFilledChecklist
*checklist
)
580 return data
->match(checklist
->src_addr
);
584 ACLDestinationASNStrategy::match (ACLData
<MatchType
> * &data
, ACLFilledChecklist
*checklist
)
586 const ipcache_addrs
*ia
= ipcache_gethostbyname(checklist
->request
->url
.host(), IP_LOOKUP_IF_MISS
);
589 for (const auto &ip
: ia
->goodAndBad()) {
596 } else if (!checklist
->request
->flags
.destinationIpLookedUp
) {
597 /* No entry in cache, lookup not attempted */
598 debugs(28, 3, "can't yet compare '" << AclMatchedName
<< "' ACL for " << checklist
->request
->url
.host());
599 if (checklist
->goAsync(DestinationIPLookup::Instance()))
601 // else fall through to noaddr match, hiding the lookup failure (XXX)
605 return data
->match(noaddr
);