#pragma once
#include "common.hpp"
#include "sock.hpp"
#include "pool.hpp"
#include "aio.hpp"
#include "callout.hpp"
#include "test.hpp"
#include <vector>
bool domain_match(const char*, const char*); ///< matches domain against given globbing pattern w/ special subdomain handling
class AclMatch {
private:
typedef struct addr_range_s {
sockaddr_t from, to;
bool parse(const sockaddr_t& a, unsigned m);
bool parse(char* s);
bool match(const sockaddr_t& a) const;
} addr_range_t;
addr_range_t src;
addr_range_t dst;
char host[MAX_SNI_LEN+1];
public:
char* parse(char*); ///< returns end ptr upon success
INLINE bool match(const char* h, const sockaddr_t& s, const sockaddr_t& d) const {
return domain_match(h, host) && src.match(s) && dst.match(d);
}
static bool parser(char*, bool&); ///< helper for use with @see Acl::parser_t
static bool parser(char*, tristate_t&); ///< helper for use with @see Acl::parser_t
};
template <class T> class Acl {
public:
typedef bool (*parser_t)(char*, T&);
private:
typedef struct {
AclMatch match;
T val;
} match_t;
static TPool<match_t> pool;
std::vector<match_t*> acls;
AioFileIn::Result* aio;
tristate_t parsed;
const parser_t parser;
static bool poller(void*); ///< poll for aio to be finished using @see Poller
static void parse(void*); ///< #aio data available now
static bool parse_line(char*, size_t, intptr_t);
public:
Acl(const char* fn, parser_t p); ///< parse filename w/ acl syntax
Acl(parser_t p);
~Acl();
bool parse_line(char*); ///< parse a single line and add it to #acls
tristate_t match(const char*, const sockaddr_t&, const sockaddr_t&, T&) const; ///< TRI_FALSE: no acls parsed (yet), TRI_TRUE: match found, TRI_NONE: no match found
};
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
template <class T> TPool<typename Acl<T>::match_t> Acl<T>::pool;
template <class T> Acl<T>::Acl(const char* fn, parser_t p): parser(p) {
aio = AioFileIn::enqueue(fn);
if (!aio) {
parsed = TRI_FALSE;
} else if (aio->len > 0) {
parsed = TRI_NONE;
parse(this);
} else if (aio->len == 0) {
parsed = TRI_NONE;
(void)Poller::getInst()->add(poller, this);
} else if (aio->len < 0) {
parsed = TRI_FALSE;
aio->delInst();
aio = NULL;
}
}
template <class T> Acl<T>::Acl(parser_t p): aio(NULL), parsed(TRI_TRUE), parser(p) {
}
template <class T> Acl<T>::~Acl() {
for (typename std::vector<match_t*>::iterator it=acls.begin(); it!=acls.end(); ++it) {
pool.push(*it);
}
if (aio) {
if (parsed == TRI_NONE) {
(void)Poller::getInst()->del(poller, this);
}
aio->delInst();
}
}
template <class T> bool Acl<T>::poller(void* i) {
Acl* inst = (Acl*)i;
if (inst->aio->len > 0) {
inst->parse(i);
return false;
} else if (inst->aio->len == 0) {
return true;
} else { // inst->aio->len < 0
inst->parsed = TRI_FALSE;
inst->aio->delInst();
inst->aio = NULL;
return false;
}
}
template <class T> void Acl<T>::parse(void* i) {
Acl* inst = (Acl*)i;
assert(inst->aio && inst->aio->len > 0);
assert(inst->parsed == TRI_NONE);
if (readline_r(inst->aio->buf, inst->aio->len, '\n', &parse_line, (intptr_t)i)) {
inst->parsed = TRI_TRUE;
} else {
inst->parsed = TRI_FALSE;
log(notice, "acl parsing failed");
}
}
template <class T> bool Acl<T>::parse_line(char* p, size_t, intptr_t i) {
return ((Acl*)i)->parse_line(p);
}
template <class T> bool Acl<T>::parse_line(char* p) {
while (*p == ' ' || *p == '\t') ++p;
if (*p == '#') return true;
match_t* m = pool.pop();
p = m->match.parse(p);
if (!p) {
pool.push(m);
return false;
}
if (!parser(p, m->val)) {
pool.push(m);
return false;
}
acls.push_back(m);
return true;
}
template <class T> tristate_t Acl<T>::match(const char* host, const sockaddr_t& src, const sockaddr_t& dst, T& rv) const {
if (parsed != TRI_TRUE) {
return TRI_FALSE;
}
for (typename std::vector<match_t*>::const_iterator it=acls.begin(); it!=acls.end(); ++it) {
if ((*it)->match.match(host, src, dst)) {
rv = (*it)->val;
return TRI_TRUE;
}
}
return TRI_NONE;
}