]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/Asn.cc
transaction_initiator ACL for detecting various unusual transactions
[thirdparty/squid.git] / src / acl / Asn.cc
CommitLineData
3841dd46 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3841dd46 3 *
bbc27441
AJ
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.
3841dd46 7 */
8
bbc27441
AJ
9/* DEBUG: section 53 AS Number handling */
10
582c2af2 11#include "squid.h"
445d18a4 12#include "acl/Acl.h"
3ad63615
AR
13#include "acl/Asn.h"
14#include "acl/Checklist.h"
445d18a4
AJ
15#include "acl/DestinationAsn.h"
16#include "acl/DestinationIp.h"
f206b652 17#include "acl/SourceAsn.h"
4eac3407 18#include "acl/Strategised.h"
eb13c21e 19#include "FwdState.h"
445d18a4 20#include "HttpReply.h"
f206b652 21#include "HttpRequest.h"
714e68b7 22#include "ipcache.h"
5ceaee75 23#include "MasterXaction.h"
f206b652
FC
24#include "mgr/Registration.h"
25#include "radix.h"
26#include "RequestFlags.h"
4d5904f7 27#include "SquidConfig.h"
f206b652
FC
28#include "Store.h"
29#include "StoreClient.h"
445d18a4
AJ
30
31#define WHOIS_PORT 43
f53969cc 32#define AS_REQBUF_SZ 4096
445d18a4
AJ
33
34/* BEGIN of definitions for radix tree entries */
35
445d18a4
AJ
36/* 32/128 bits address in memory with length */
37class m_ADDR
38{
39public:
40 uint8_t len;
b7ac5457 41 Ip::Address addr;
445d18a4 42
b7ac5457 43 m_ADDR() : len(sizeof(Ip::Address)) {};
445d18a4
AJ
44};
45
46/* END of definitions for radix tree entries */
47
48/* Head for ip to asn radix tree */
49
50struct squid_radix_node_head *AS_tree_head;
51
52/* explicit instantiation required for some systems */
53
d6d0eb11 54/// \cond AUTODOCS_IGNORE
445d18a4
AJ
55template cbdata_type CbDataList<int>::CBDATA_CbDataList;
56/// \endcond
57
58/**
59 * Structure for as number information. it could be simply
60 * a list but it's coded as a structure for future
61 * enhancements (e.g. expires)
62 */
63struct as_info {
64 CbDataList<int> *as_number;
f53969cc 65 time_t expires; /* NOTUSED */
445d18a4
AJ
66};
67
e2e3d39b
AJ
68class ASState
69{
5c2f68b7
AJ
70 CBDATA_CLASS(ASState);
71
e2e3d39b
AJ
72public:
73 ASState();
74 ~ASState();
75
445d18a4
AJ
76 StoreEntry *entry;
77 store_client *sc;
e2e3d39b 78 HttpRequest::Pointer request;
445d18a4
AJ
79 int as_number;
80 int64_t offset;
81 int reqofs;
82 char reqbuf[AS_REQBUF_SZ];
83 bool dataRead;
84};
85
e2e3d39b
AJ
86CBDATA_CLASS_INIT(ASState);
87
88ASState::ASState() :
f53969cc
SM
89 entry(NULL),
90 sc(NULL),
91 request(NULL),
92 as_number(0),
93 offset(0),
94 reqofs(0),
95 dataRead(false)
e2e3d39b
AJ
96{
97 memset(reqbuf, 0, AS_REQBUF_SZ);
98}
99
100ASState::~ASState()
101{
102 debugs(53, 3, entry->url());
103 storeUnregister(sc, entry, this);
dcd84f80 104 entry->unlock("~ASState");
e2e3d39b
AJ
105}
106
445d18a4
AJ
107/** entry into the radix tree */
108struct rtentry_t {
109 struct squid_radix_node e_nodes[2];
110 as_info *e_info;
111 m_ADDR e_addr;
112 m_ADDR e_mask;
113};
114
115static int asnAddNet(char *, int);
116
117static void asnCacheStart(int as);
118
119static STCB asHandleReply;
120
062e0b16
FC
121#if defined(__cplusplus)
122extern "C" {
123#endif
124
f53969cc
SM
125static int destroyRadixNode(struct squid_radix_node *rn, void *w);
126static int printRadixNode(struct squid_radix_node *rn, void *sentry);
445d18a4 127
062e0b16
FC
128#if defined(__cplusplus)
129}
130#endif
131
445d18a4
AJ
132void asnAclInitialize(ACL * acls);
133
445d18a4
AJ
134static void destroyRadixNodeInfo(as_info *);
135
136static OBJH asnStats;
137
138/* PUBLIC */
139
140int
b7ac5457 141asnMatchIp(CbDataList<int> *data, Ip::Address &addr)
445d18a4
AJ
142{
143 struct squid_radix_node *rn;
144 as_info *e;
145 m_ADDR m_addr;
146 CbDataList<int> *a = NULL;
147 CbDataList<int> *b = NULL;
148
149 debugs(53, 3, "asnMatchIp: Called for " << addr );
150
151 if (AS_tree_head == NULL)
152 return 0;
153
4dd643d5 154 if (addr.isNoAddr())
445d18a4
AJ
155 return 0;
156
4dd643d5 157 if (addr.isAnyAddr())
445d18a4
AJ
158 return 0;
159
160 m_addr.addr = addr;
161
162 rn = squid_rn_match(&m_addr, AS_tree_head);
163
164 if (rn == NULL) {
165 debugs(53, 3, "asnMatchIp: Address not in as db.");
166 return 0;
167 }
168
169 debugs(53, 3, "asnMatchIp: Found in db!");
170 e = ((rtentry_t *) rn)->e_info;
171 assert(e);
172
173 for (a = data; a; a = a->next)
174 for (b = e->as_number; b; b = b->next)
175 if (a->element == b->element) {
176 debugs(53, 5, "asnMatchIp: Found a match!");
177 return 1;
178 }
179
180 debugs(53, 5, "asnMatchIp: AS not in as db.");
181 return 0;
182}
183
184void
185ACLASN::prepareForUse()
186{
187 for (CbDataList<int> *i = data; i; i = i->
188 next)
189 asnCacheStart(i->element);
190}
191
192static void
193asnRegisterWithCacheManager(void)
194{
8822ebee 195 Mgr::RegisterAction("asndb", "AS Number Database", asnStats, 0, 1);
445d18a4
AJ
196}
197
198/* initialize the radix tree structure */
199
f53969cc 200SQUIDCEXTERN int squid_max_keylen; /* yuck.. this is in lib/radix.c */
445d18a4 201
445d18a4
AJ
202void
203asnInit(void)
204{
742a021b 205 static bool inited = false;
445d18a4 206 squid_max_keylen = 40;
445d18a4 207
742a021b
FC
208 if (!inited) {
209 inited = true;
445d18a4 210 squid_rn_init();
742a021b 211 }
445d18a4
AJ
212
213 squid_rn_inithead(&AS_tree_head, 8);
214
215 asnRegisterWithCacheManager();
216}
217
218void
219asnFreeMemory(void)
220{
221 squid_rn_walktree(AS_tree_head, destroyRadixNode, AS_tree_head);
222
223 destroyRadixNode((struct squid_radix_node *) 0, (void *) AS_tree_head);
224}
225
226static void
227asnStats(StoreEntry * sentry)
228{
229 storeAppendPrintf(sentry, "Address \tAS Numbers\n");
230 squid_rn_walktree(AS_tree_head, printRadixNode, sentry);
231}
232
233/* PRIVATE */
234
445d18a4
AJ
235static void
236asnCacheStart(int as)
237{
238 LOCAL_ARRAY(char, asres, 4096);
239 StoreEntry *e;
e2e3d39b
AJ
240 ASState *asState = new ASState;
241 debugs(53, 3, "AS " << as);
445d18a4
AJ
242 snprintf(asres, 4096, "whois://%s/!gAS%d", Config.as_whois_server, as);
243 asState->as_number = as;
5ceaee75
CT
244 const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initAsn);
245 asState->request = HttpRequest::FromUrl(asres, mx);
e2e3d39b 246 assert(asState->request != NULL);
445d18a4 247
c2a7cefd
AJ
248 if ((e = storeGetPublic(asres, Http::METHOD_GET)) == NULL) {
249 e = storeCreateEntry(asres, asres, RequestFlags(), Http::METHOD_GET);
445d18a4 250 asState->sc = storeClientListAdd(e, asState);
e2e3d39b 251 FwdState::fwdStart(Comm::ConnectionPointer(), e, asState->request.getRaw());
445d18a4 252 } else {
1bfe9ade 253 e->lock("Asn");
445d18a4
AJ
254 asState->sc = storeClientListAdd(e, asState);
255 }
256
257 asState->entry = e;
445d18a4 258 StoreIOBuffer readBuffer (AS_REQBUF_SZ, asState->offset, asState->reqbuf);
e2e3d39b 259 storeClientCopy(asState->sc, e, readBuffer, asHandleReply, asState);
445d18a4
AJ
260}
261
262static void
263asHandleReply(void *data, StoreIOBuffer result)
264{
265 ASState *asState = (ASState *)data;
266 StoreEntry *e = asState->entry;
267 char *s;
268 char *t;
269 char *buf = asState->reqbuf;
270 int leftoversz = -1;
271
272 debugs(53, 3, "asHandleReply: Called with size=" << (unsigned int)result.length);
273 debugs(53, 3, "asHandleReply: buffer='" << buf << "'");
274
275 /* First figure out whether we should abort the request */
276
277 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
e2e3d39b 278 delete asState;
445d18a4
AJ
279 return;
280 }
281
282 if (result.length == 0 && asState->dataRead) {
e2e3d39b
AJ
283 debugs(53, 3, "asHandleReply: Done: " << e->url());
284 delete asState;
445d18a4
AJ
285 return;
286 } else if (result.flags.error) {
e0236918 287 debugs(53, DBG_IMPORTANT, "asHandleReply: Called with Error set and size=" << (unsigned int) result.length);
e2e3d39b 288 delete asState;
445d18a4 289 return;
9b769c67 290 } else if (e->getReply()->sline.status() != Http::scOkay) {
e0236918 291 debugs(53, DBG_IMPORTANT, "WARNING: AS " << asState->as_number << " whois request failed");
e2e3d39b 292 delete asState;
445d18a4
AJ
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 ((size_t)(s - buf) < result.length + asState->reqofs && *s != '\0') {
303 while (*s && xisspace(*s))
742a021b 304 ++s;
445d18a4 305
742a021b 306 for (t = s; *t; ++t) {
445d18a4
AJ
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 debugs(53, 3, "asHandleReply: AS# " << s << " (" << asState->as_number << ")");
318 asnAddNet(s, asState->as_number);
319 s = t + 1;
e2e3d39b 320 asState->dataRead = true;
445d18a4
AJ
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 */
41d00cd3 336 memmove(buf, s, leftoversz);
445d18a4
AJ
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 debugs(53, 3, "asState->offset = " << asState->offset);
346
347 if (e->store_status == STORE_PENDING) {
348 debugs(53, 3, "asHandleReply: store_status == STORE_PENDING: " << e->url() );
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 debugs(53, 3, "asHandleReply: store complete, but data received " << e->url() );
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
445d18a4
AJ
371/**
372 * add a network (addr, mask) to the radix tree, with matching AS number
373 */
374static int
375asnAddNet(char *as_string, int as_number)
376{
445d18a4
AJ
377 struct squid_radix_node *rn;
378 CbDataList<int> **Tail = NULL;
379 CbDataList<int> *q = NULL;
380 as_info *asinfo = NULL;
381
b7ac5457
AJ
382 Ip::Address mask;
383 Ip::Address addr;
445d18a4
AJ
384 char *t;
385 int bitl;
386
387 t = strchr(as_string, '/');
388
389 if (t == NULL) {
390 debugs(53, 3, "asnAddNet: failed, invalid response from whois server.");
391 return 0;
392 }
393
394 *t = '\0';
395 addr = as_string;
396 bitl = atoi(t + 1);
397
398 if (bitl < 0)
399 bitl = 0;
400
401 // INET6 TODO : find a better way of identifying the base IPA family for mask than this.
402 t = strchr(as_string, '.');
403
404 // generate Netbits Format Mask
4dd643d5
AJ
405 mask.setNoAddr();
406 mask.applyMask(bitl, (t!=NULL?AF_INET:AF_INET6) );
445d18a4
AJ
407
408 debugs(53, 3, "asnAddNet: called for " << addr << "/" << mask );
409
726904ad 410 rtentry_t *e = (rtentry_t *)xcalloc(1, sizeof(rtentry_t));
445d18a4
AJ
411
412 e->e_addr.addr = addr;
413
414 e->e_mask.addr = mask;
415
416 rn = squid_rn_lookup(&e->e_addr, &e->e_mask, AS_tree_head);
417
418 if (rn != NULL) {
419 asinfo = ((rtentry_t *) rn)->e_info;
420
421 if (asinfo->as_number->find(as_number)) {
422 debugs(53, 3, "asnAddNet: Ignoring repeated network '" << addr << "/" << bitl << "' for AS " << as_number);
423 } else {
424 debugs(53, 3, "asnAddNet: Warning: Found a network with multiple AS numbers!");
425
426 for (Tail = &asinfo->as_number; *Tail; Tail = &(*Tail)->next);
427 q = new CbDataList<int> (as_number);
428
429 *(Tail) = q;
430
431 e->e_info = asinfo;
432 }
433 } else {
434 q = new CbDataList<int> (as_number);
435 asinfo = (as_info *)xmalloc(sizeof(as_info));
436 asinfo->as_number = q;
58ff1033 437 squid_rn_addroute(&e->e_addr, &e->e_mask, AS_tree_head, e->e_nodes);
445d18a4
AJ
438 rn = squid_rn_match(&e->e_addr, AS_tree_head);
439 assert(rn != NULL);
440 e->e_info = asinfo;
441 }
442
f53969cc 443 if (rn == 0) { /* assert might expand to nothing */
445d18a4
AJ
444 xfree(asinfo);
445 delete q;
446 xfree(e);
447 debugs(53, 3, "asnAddNet: Could not add entry.");
448 return 0;
449 }
450
451 e->e_info = asinfo;
452 return 1;
453}
454
455static int
456destroyRadixNode(struct squid_radix_node *rn, void *w)
457{
458
459 struct squid_radix_node_head *rnh = (struct squid_radix_node_head *) w;
460
461 if (rn && !(rn->rn_flags & RNF_ROOT)) {
462 rtentry_t *e = (rtentry_t *) rn;
463 rn = squid_rn_delete(rn->rn_key, rn->rn_mask, rnh);
464
465 if (rn == 0)
466 debugs(53, 3, "destroyRadixNode: internal screwup");
467
468 destroyRadixNodeInfo(e->e_info);
469
470 xfree(rn);
471 }
472
473 return 1;
474}
475
476static void
477destroyRadixNodeInfo(as_info * e_info)
478{
479 CbDataList<int> *prev = NULL;
480 CbDataList<int> *data = e_info->as_number;
481
482 while (data) {
483 prev = data;
484 data = data->next;
485 delete prev;
486 }
445d18a4
AJ
487}
488
489static int
490printRadixNode(struct squid_radix_node *rn, void *_sentry)
491{
492 StoreEntry *sentry = (StoreEntry *)_sentry;
493 rtentry_t *e = (rtentry_t *) rn;
494 CbDataList<int> *q;
495 as_info *asinfo;
496 char buf[MAX_IPSTRLEN];
b7ac5457
AJ
497 Ip::Address addr;
498 Ip::Address mask;
445d18a4
AJ
499
500 assert(e);
501 assert(e->e_info);
502 addr = e->e_addr.addr;
503 mask = e->e_mask.addr;
504 storeAppendPrintf(sentry, "%s/%d\t",
4dd643d5
AJ
505 addr.toStr(buf, MAX_IPSTRLEN),
506 mask.cidr() );
445d18a4
AJ
507 asinfo = e->e_info;
508 assert(asinfo->as_number);
509
510 for (q = asinfo->as_number; q; q = q->next)
511 storeAppendPrintf(sentry, " %d", q->element);
512
513 storeAppendPrintf(sentry, "\n");
514
515 return 0;
516}
517
518ACLASN::~ACLASN()
519{
520 if (data)
521 delete data;
522}
523
524bool
525
b7ac5457 526ACLASN::match(Ip::Address toMatch)
445d18a4
AJ
527{
528 return asnMatchIp(data, toMatch);
529}
530
8966008b 531SBufList
4f8ca96e 532ACLASN::dump() const
445d18a4 533{
8966008b
FC
534 SBufList sl;
535
445d18a4
AJ
536 CbDataList<int> *ldata = data;
537
538 while (ldata != NULL) {
8966008b
FC
539 SBuf s;
540 s.Printf("%d", ldata->element);
541 sl.push_back(s);
445d18a4
AJ
542 ldata = ldata->next;
543 }
544
8966008b 545 return sl;
445d18a4
AJ
546}
547
548bool
549ACLASN::empty () const
550{
551 return data == NULL;
552}
553
554void
555ACLASN::parse()
556{
557 CbDataList<int> **curlist = &data;
558 CbDataList<int> **Tail;
559 CbDataList<int> *q = NULL;
560 char *t = NULL;
561
562 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
16c5ad96 563 while ((t = ConfigParser::strtokFile())) {
445d18a4
AJ
564 q = new CbDataList<int> (atoi(t));
565 *(Tail) = q;
566 Tail = &q->next;
567 }
568}
569
b7ac5457 570ACLData<Ip::Address> *
445d18a4
AJ
571ACLASN::clone() const
572{
573 if (data)
574 fatal ("cloning of ACLASN not implemented");
575
576 return new ACLASN(*this);
577}
578
579/* explicit template instantiation required for some systems */
580
b7ac5457 581template class ACLStrategised<Ip::Address>;
445d18a4 582
445d18a4 583int
4eac3407 584ACLSourceASNStrategy::match (ACLData<Ip::Address> * &data, ACLFilledChecklist *checklist)
445d18a4
AJ
585{
586 return data->match(checklist->src_addr);
587}
588
445d18a4 589int
4eac3407 590ACLDestinationASNStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
445d18a4 591{
5c51bffb 592 const ipcache_addrs *ia = ipcache_gethostbyname(checklist->request->url.host(), IP_LOOKUP_IF_MISS);
445d18a4
AJ
593
594 if (ia) {
742a021b 595 for (int k = 0; k < (int) ia->count; ++k) {
445d18a4
AJ
596 if (data->match(ia->in_addrs[k]))
597 return 1;
598 }
599
600 return 0;
601
450fe1cb 602 } else if (!checklist->request->flags.destinationIpLookedUp) {
445d18a4 603 /* No entry in cache, lookup not attempted */
5c51bffb 604 debugs(28, 3, "can't yet compare '" << AclMatchedName << "' ACL for " << checklist->request->url.host());
6f58d7d7
AR
605 if (checklist->goAsync(DestinationIPLookup::Instance()))
606 return -1;
607 // else fall through to noaddr match, hiding the lookup failure (XXX)
445d18a4 608 }
e936c41c 609 Ip::Address noaddr;
4dd643d5 610 noaddr.setNoAddr();
e936c41c 611 return data->match(noaddr);
445d18a4
AJ
612}
613