"Fossies" - the Fresh Open Source Software Archive

Member "rbldnsd-0.998/rbldnsd_acl.c" (6 Apr 2013, 6039 Bytes) of package /linux/misc/dns/rbldnsd-0.998.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rbldnsd_acl.c" see the Fossies "Dox" file reference documentation.

    1 /* ACL (Access Control Lists) implementation for rbldnsd.
    2  */
    3 
    4 #include <stdio.h>
    5 #include <string.h>
    6 #include <stdlib.h>
    7 #include <syslog.h>
    8 #include <sys/types.h>
    9 #include <sys/socket.h>
   10 #include <netinet/in.h>
   11 #include "rbldnsd.h"
   12 #include "btrie.h"
   13 
   14 struct dsdata {
   15   struct btrie *ip4_trie;
   16 #ifndef NO_IPv6
   17   struct btrie *ip6_trie;
   18 #endif
   19   const char *def_rr;
   20   const char *def_action;
   21 };
   22 
   23 /* special cases for pseudo-RRs */
   24 static const struct {
   25   const char *name;
   26   unsigned long rr;
   27 } keywords[] = {
   28   /* ignore (don't answer) queries from this IP */
   29 #define RR_IGNORE   1
   30  { "ignore", RR_IGNORE },
   31  { "blackhole", RR_IGNORE },
   32  /* refuse *data* queries from this IP (but not metadata) */
   33 #define RR_REFUSE   2
   34  { "refuse", RR_REFUSE },
   35  /* pretend the zone is completely empty */
   36 #define RR_EMPTY    3
   37  { "empty", RR_EMPTY },
   38  /* a 'whitelist' entry: pretend this netrange isn't here */
   39 #define RR_PASS     4
   40  { "pass", RR_PASS },
   41 };
   42 
   43 static void ds_acl_reset(struct dsdata *dsd, int UNUSED unused_freeall) {
   44   memset(dsd, 0, sizeof(*dsd));
   45 }
   46 
   47 static void ds_acl_start(struct dataset *ds) {
   48   struct dsdata *dsd = ds->ds_dsd;
   49 
   50   dsd->def_rr = def_rr;
   51   dsd->def_action = (char*)RR_IGNORE;
   52   if (!dsd->ip4_trie) {
   53     dsd->ip4_trie = btrie_init(ds->ds_mp);
   54 #ifndef NO_IPv6
   55     dsd->ip6_trie = btrie_init(ds->ds_mp);
   56 #endif
   57   }
   58 }
   59 
   60 static const char *keyword(const char *s) {
   61   const char *k, *p;
   62   unsigned i;
   63   if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z')))
   64     return NULL;
   65   for (i = 0; i < sizeof(keywords)/sizeof(keywords[0]); ++i)
   66     for (k = keywords[i].name, p = s;;)
   67       if ((*p >= 'A' && *p <= 'Z' ? *p - 'A' + 'a' : *p) != *k++)
   68         break;
   69       else if (!*++p || *p == ':' || ISSPACE(*p) || ISCOMMENT(*p))
   70         return (const char *)(keywords[i].rr);
   71   return NULL;
   72 }
   73 
   74 static int
   75 ds_acl_parse_val(char *s, const char **rr_p, struct dsdata *dsd,
   76                  struct dsctx *dsc) {
   77   int r;
   78   if (*s == '=') {
   79     if ((*rr_p = keyword(s+1)))
   80       return 0;
   81     dswarn(dsc, "invalid keyword");
   82     return -1;
   83   }
   84   if (*s == ':' && (*rr_p = keyword(s+1)))
   85     return 0;
   86   r = parse_a_txt(s, rr_p, dsd->def_rr, dsc);
   87   return r ? r : -1;
   88 }
   89 
   90 #define VALID_TAIL(c) ((c) == '\0' || ISSPACE(c) ||  ISCOMMENT(c) || (c) == ':')
   91 
   92 static int
   93 ds_acl_line(struct dataset *ds, char *s, struct dsctx *dsc) {
   94   struct dsdata *dsd = ds->ds_dsd;
   95   char *tail;
   96   ip4addr_t ip4addr;
   97   ip6oct_t addr[IP6ADDR_FULL];
   98   const char *ipstring;
   99   struct btrie *trie;
  100   int bits;
  101   const char *rr;
  102   int rrl;
  103 
  104   /* "::" can not be a valid start to a default RR setting ("invalid A
  105    * RR") but it can be a valid beginning to an ip6 address
  106    * (e.g. "::1")
  107    */
  108   if ((*s == ':' && s[1] != ':') || *s == '=') {
  109     if ((rrl = ds_acl_parse_val(s, &rr, dsd, dsc)) < 0)
  110       return 1;
  111     else if (!rrl)
  112       dsd->def_action = rr;
  113     else if (!(rr = mp_dmemdup(ds->ds_mp, rr, rrl)))
  114       return 0;
  115     dsd->def_rr = dsd->def_action = rr;
  116     return 1;
  117   }
  118 
  119   if ((bits = ip4cidr(s, &ip4addr, &tail)) >= 0 && VALID_TAIL(tail[0])) {
  120     if (accept_in_cidr)
  121       ip4addr &= ip4mask(bits);
  122     else if (ip4addr & ~ip4mask(bits)) {
  123       dswarn(dsc, "invalid range (non-zero host part)");
  124       return 1;
  125     }
  126     if (dsc->dsc_ip4maxrange && dsc->dsc_ip4maxrange <= ~ip4mask(bits)) {
  127       dswarn(dsc, "too large range (%u) ignored (%u max)",
  128              ~ip4mask(bits) + 1, dsc->dsc_ip4maxrange);
  129       return 1;
  130     }
  131     trie = dsd->ip4_trie;
  132     ip4unpack(addr, ip4addr);
  133     ipstring = ip4atos(ip4addr);
  134     s = tail;
  135   }
  136 #ifndef NO_IPv6
  137   else if ((bits = ip6cidr(s, addr, &tail)) >= 0 && VALID_TAIL(tail[0])) {
  138     int non_zero_host = ip6mask(addr, addr, IP6ADDR_FULL, bits);
  139     if (non_zero_host && !accept_in_cidr) {
  140       dswarn(dsc, "invalid range (non-zero host part)");
  141       return 1;
  142     }
  143     trie = dsd->ip6_trie;
  144     ipstring = ip6atos(addr, IP6ADDR_FULL);
  145     s = tail;
  146   }
  147 #endif
  148   else {
  149     dswarn(dsc, "invalid address");
  150     return 1;
  151   }
  152 
  153   SKIPSPACE(s);
  154   if (!*s || ISCOMMENT(*s))
  155     rr = dsd->def_action;
  156   else if ((rrl = ds_acl_parse_val(s, &rr, dsd, dsc)) < 0)
  157     return 1;
  158   else if (rrl && !(rr = mp_dmemdup(ds->ds_mp, rr, rrl)))
  159     return 0;
  160 
  161   switch(btrie_add_prefix(trie, addr, bits, rr)) {
  162   case BTRIE_OKAY:
  163     return 1;
  164   case BTRIE_DUPLICATE_PREFIX:
  165     dswarn(dsc, "duplicated entry for %s/%d", ipstring, bits);
  166     return 1;
  167   case BTRIE_ALLOC_FAILED:
  168   default:
  169     return 0;
  170   }
  171 }
  172 
  173 static void ds_acl_finish(struct dataset *ds, struct dsctx *dsc) {
  174   dsloaded(dsc, "loaded");
  175   dslog(LOG_INFO, dsc, "ip4 trie: %s", btrie_stats(ds->ds_dsd->ip4_trie));
  176 #ifndef NO_IPv6
  177   dslog(LOG_INFO, dsc, "ip6 trie: %s", btrie_stats(ds->ds_dsd->ip6_trie));
  178 #endif
  179 }
  180 
  181 int ds_acl_query(const struct dataset *ds, struct dnspacket *pkt) {
  182   const struct sockaddr *sa = pkt->p_peer;
  183   const char *rr;
  184 
  185   if (sa->sa_family == AF_INET) {
  186     const struct sockaddr_in *sin = (const struct sockaddr_in *)pkt->p_peer;
  187     if (pkt->p_peerlen < sizeof(*sin))
  188       return 0;
  189     rr = btrie_lookup(ds->ds_dsd->ip4_trie,
  190                       (const btrie_oct_t *)&sin->sin_addr.s_addr, 32);
  191   }
  192 #ifndef NO_IPv6
  193   else if (sa->sa_family == AF_INET6) {
  194     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)pkt->p_peer;
  195     if (pkt->p_peerlen < sizeof(*sin6))
  196       return 0;
  197     rr = btrie_lookup(ds->ds_dsd->ip6_trie,
  198                       sin6->sin6_addr.s6_addr, 8 * IP6ADDR_FULL);
  199   }
  200 #endif
  201   else {
  202     return 0;
  203   }
  204 
  205   switch((unsigned long)rr) {
  206   case 0: return 0;
  207   case RR_IGNORE:   return NSQUERY_IGNORE;
  208   case RR_REFUSE:   return NSQUERY_REFUSE;
  209   case RR_EMPTY:    return NSQUERY_EMPTY;
  210   case RR_PASS:     return 0;
  211   }
  212   if (!pkt->p_substrr) {
  213     pkt->p_substrr = rr;
  214     pkt->p_substds = ds;
  215   }
  216   return NSQUERY_ALWAYS;
  217 }
  218 
  219 /*definedstype(acl, DSTF_SPECIAL, "Access Control List dataset");*/
  220 const struct dstype dataset_acl_type = {
  221   "acl", DSTF_SPECIAL, sizeof(struct dsdata),
  222   ds_acl_reset, ds_acl_start, ds_acl_line, ds_acl_finish,
  223   NULL, NULL, "Access Control List dataset"
  224 };