"Fossies" - the Fresh Open Source Software Archive

Member "mod_defensible-1.5/mod_defensible.c" (3 Apr 2012, 12289 Bytes) of package /linux/www/apache_httpd_modules/old/mod_defensible-1.5.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 "mod_defensible.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * mod_defensible.c - Forbid page access using DNSBL
    3  *
    4  * Copyright © 2007-2012 Julien Danjou <julien@danjou.info>
    5  *
    6  * This program is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License as published by
    8  * the Free Software Foundation; either version 2 of the License, or
    9  * (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License along
   17  * with this program; if not, write to the Free Software Foundation, Inc.,
   18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   19  *
   20  */
   21 
   22 #include <config.h>
   23 
   24 #include "apr_strings.h"
   25 
   26 #include "httpd.h"
   27 #include "http_core.h"
   28 #include "http_protocol.h"
   29 #include "http_config.h"
   30 #include "http_log.h"
   31 #include "http_request.h"
   32 
   33 #ifdef HAVE_LIBUDNS
   34 #include <udns.h>
   35 #include <sys/socket.h>
   36 #include <netinet/in.h>
   37 #include <arpa/inet.h>
   38 #include <poll.h>
   39 #else
   40 #include <netinet/in.h>
   41 #include <netdb.h>
   42 #endif
   43 
   44 #define DEFENSIBLE_HEADER_STRING "mod_defensible/" VERSION
   45 
   46 /* enum used for config */
   47 enum use_dnsbl_type
   48 {
   49     T_YES,
   50     T_NO
   51 };
   52 
   53 /*
   54  * module configuration structure
   55  * use_dnsbl is T_YES or T_NO if we use it or not
   56  * dnsbl_servers is an array containing DNSBL servers
   57  */
   58 typedef struct
   59 {
   60     enum use_dnsbl_type use_dnsbl;
   61     apr_array_header_t *dnsbl_servers;
   62 #ifdef HAVE_LIBUDNS
   63     char * nameserver;
   64 #endif
   65 } dnsbl_config;
   66 
   67 module AP_MODULE_DECLARE_DATA defensible_module;
   68 
   69 /* Callback function called when we get DnsblUse option */
   70 static const char *use_dnsbl(cmd_parms *parms __attribute__ ((unused)),
   71                              void *mconfig,
   72                              const char *arg)
   73 {
   74     dnsbl_config *s_cfg = (dnsbl_config *) mconfig;
   75     
   76     /* Repeat after me: DNSBL is good for your web server */
   77     if(!strcasecmp(arg, "On"))
   78         s_cfg->use_dnsbl = T_YES;
   79     /* Oh, no ! © Lemmings */
   80     else
   81         s_cfg->use_dnsbl = T_NO;
   82 
   83     return NULL;
   84 }
   85 
   86 #ifdef HAVE_LIBUDNS
   87 /* Callback function called when we get DnsblNameserver option */
   88 static const char *set_dnsbl_nameserver(cmd_parms *parms,
   89                              void *mconfig,
   90                              const char *arg)
   91 {
   92     dnsbl_config *s_cfg = (dnsbl_config *) mconfig;
   93     
   94     s_cfg->nameserver = apr_pstrdup(parms->pool, arg);
   95 
   96     return NULL;
   97 }
   98 #endif
   99 
  100 /* Callback function called when we get DnsblServers option */
  101 static const char *set_dnsbl_server(cmd_parms *parms,
  102                                     void *mconfig,
  103                                     const char *server_o)
  104 {
  105     char *server = apr_pstrdup(parms->pool, server_o);
  106     char ** cfg;
  107     dnsbl_config *s_cfg = (dnsbl_config *) mconfig;
  108 
  109     /* We add the DNSBL server to the array */
  110     cfg = (char **) apr_array_push(s_cfg->dnsbl_servers);
  111     *cfg = server;
  112 
  113     return NULL;
  114 }
  115 
  116 /* Configuration directive declaration for our module */
  117 static const command_rec defensible_cmds[] =
  118 {
  119     AP_INIT_TAKE1("DnsblUse", use_dnsbl, NULL, RSRC_CONF|ACCESS_CONF|OR_LIMIT,
  120                   "Set to 'On' to use DNSBL"),
  121     AP_INIT_ITERATE("DnsblServers", set_dnsbl_server, NULL, RSRC_CONF|ACCESS_CONF|OR_LIMIT,
  122                      "DNS suffix to use for lookup in DNSBL server"),
  123 #ifdef HAVE_LIBUDNS
  124     AP_INIT_TAKE1("DnsblNameserver", set_dnsbl_nameserver, NULL, RSRC_CONF|ACCESS_CONF|OR_LIMIT,
  125                   "IP address of the nameserver to use for DNSBL lookup"),
  126 #endif
  127     {NULL, {NULL}, NULL, 0, RAW_ARGS, NULL}
  128 };
  129 
  130 /* Create initial configuration */
  131 static void *create_defensible_config(apr_pool_t *p,
  132                                  char *dummy __attribute__ ((unused)))
  133 {
  134     dnsbl_config *conf = (dnsbl_config *) apr_pcalloc(p, sizeof(dnsbl_config)); 
  135 
  136     conf->use_dnsbl = T_NO;
  137     conf->dnsbl_servers = apr_array_make(p, 1, sizeof(char *)); 
  138 
  139 #ifdef HAVE_LIBUDNS
  140     conf->nameserver = NULL;
  141 #endif
  142 
  143     return (void *) conf;
  144 }
  145 
  146 #ifdef HAVE_LIBUDNS
  147 /* Struct used as data for the udns callback function */
  148 struct udns_cb_data
  149 {
  150     request_rec *r;
  151     char * dnsbl;
  152     int blacklist;
  153 };
  154 
  155 /* udns callback function called by udns after each query resolution */
  156 static void udns_cb(struct dns_ctx *ctx __attribute__ ((unused)),
  157                     struct dns_rr_a4 *r,
  158                     void *data)
  159 {
  160     struct udns_cb_data * info = (struct udns_cb_data *) data;
  161 
  162     /* If we get a record */
  163     if(r)
  164     {
  165         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, info->r,
  166                       "client denied by DNSBL: %s for: %s",
  167                       info->dnsbl, info->r->uri);
  168         free(r);
  169         info->blacklist = 1;
  170     }
  171     else
  172     {
  173         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, info->r,
  174                       "client not listed on %s",
  175                       info->dnsbl);
  176     }
  177 
  178     return;
  179 }
  180 #endif
  181 
  182 static void generate_page(request_rec *r, char * dnsbl)
  183 {
  184     ap_set_content_type(r, "text/html"); 
  185     ap_rputs(DOCTYPE_HTML_2_0, r);
  186     ap_rputs("<html><head>\n<title>403 Forbidden</title></head><body><h1>Forbidden</h1>\n", r);
  187     ap_rprintf(r, "<p>You don't have permission to access %s\n", ap_escape_html(r->pool, r->uri));
  188     ap_rprintf(r, "on this server because you are currently blacklisted by a DNSBL server at: <b>%s</b></p>\n", dnsbl);
  189     ap_rputs("<hr>\n", r);
  190     ap_rprintf(r, "<address>%s</address>\n", ap_get_server_banner());
  191     ap_rputs("</body></html>\n", r);
  192 }
  193 
  194 /* 
  195  * Callback function called on each HTTP request
  196  * Check an IP in a DNSBL
  197  */
  198 static int check_dnsbl_access(request_rec *r)
  199 {
  200     char **srv_elts;
  201     char *ip = r->useragent_ip;
  202     int i;
  203 
  204     dnsbl_config *conf = (dnsbl_config *)
  205         ap_get_module_config(r->per_dir_config, &defensible_module);
  206 
  207     /* Return right now if we don't use DNSBL */
  208     if(conf->use_dnsbl == T_NO)
  209         return DECLINED;
  210 
  211     /* Return if IPv6 client */
  212     if (ap_strchr_c(ip, ':'))
  213        return DECLINED;
  214 
  215     srv_elts = (char **) conf->dnsbl_servers->elts;
  216 
  217 #ifdef HAVE_LIBUDNS
  218     apr_array_header_t *data_array;
  219     data_array = apr_array_make(r->pool, 1, sizeof(struct udns_cb_data *)); 
  220 
  221     /* Initialize udns lib */
  222     if(dns_init(&dns_defctx, 0) < 0)
  223     {
  224         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
  225                       "error initializing udns library for DNSBL, can't check client");
  226         return DECLINED;
  227     }
  228 
  229     /* Add configured nameserver if available */
  230     if(conf->nameserver)
  231         dns_add_serv(&dns_defctx, NULL);
  232         if(dns_add_serv(&dns_defctx, conf->nameserver) < 0)
  233         {
  234             ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
  235                           "error adding DNSBL nameserver, can't check client");
  236             return DECLINED;
  237         }
  238 
  239     if(dns_open(&dns_defctx) < 0)
  240     {
  241         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
  242                       "error open connection from udns library for DNSBL, can't check client");
  243         return DECLINED;
  244     }
  245 #else
  246     int old_i, j, k = 0; 
  247     ssize_t len, len_dnsbl;
  248     char *revip = NULL, *hostdnsbl = NULL;
  249     len = strlen(ip); 
  250 
  251     revip  = (char *) apr_pcalloc(r->pool, sizeof(char) * (len + 1)); 
  252 
  253     /* reverse IP from a.b.c.d to d.c.b.a */
  254     old_i = len; 
  255     for(i = len - 1; i >= 0; i--) 
  256         if(ip[i] == '.' || i == 0) 
  257         { 
  258             for(j = i ? i + 1 : 0; j < old_i; j++) 
  259                 revip[k++] = ip[j]; 
  260             revip[k++] = '.'; 
  261             old_i = i; 
  262         }
  263 #endif
  264 
  265     /* check in each dnsbl */
  266     for(i = 0; i < conf->dnsbl_servers->nelts; i++)
  267     {
  268 #ifdef HAVE_LIBUDNS
  269         struct in_addr client_addr;
  270         struct udns_cb_data *data, **tmp;
  271         
  272         /* First, allocate space for udns_cb_data in data */
  273         data = (struct udns_cb_data *) apr_pcalloc (r->pool, sizeof(struct udns_cb_data));
  274 
  275         /* Copy connection_req and our DNSBL server in data */
  276         data->r = r;
  277         data->dnsbl = srv_elts[i];
  278         data->blacklist = 0;
  279 
  280         /* Finally push data in our array */
  281         tmp = (struct udns_cb_data **) apr_array_push(data_array);
  282         *tmp = data;
  283 
  284         /*
  285          * Submit a DNSBL query to udns with:
  286          * Client address, DNSBL server, udns_cb as callback function
  287          * and data as data for the callback function
  288          */
  289         inet_aton(ip, &client_addr);
  290         dns_submit_a4dnsbl(&dns_defctx, &client_addr, srv_elts[i], udns_cb, data);
  291 
  292         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
  293                       "looking up in DNSBL: %s for: %s", srv_elts[i], r->uri);
  294 #else
  295         /* 
  296          * Here we build the host to lookup:
  297          * revip.dnsblserver
  298          */
  299         len_dnsbl = strlen(srv_elts[i]);
  300 
  301         hostdnsbl = (char *) apr_pcalloc(r->pool, sizeof(char) * (len_dnsbl + len + 2)); 
  302 
  303         strncpy(hostdnsbl, revip, len);
  304         strncat(hostdnsbl, ".", 1);
  305         strncat(hostdnsbl, srv_elts[i], len_dnsbl);
  306 
  307         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
  308                       "looking up in DNSBL: %s for: %s", srv_elts[i], r->uri);
  309 
  310         /* If it resolve, the IP is blacklisted */
  311         if(gethostbyname(hostdnsbl))
  312         {
  313             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  314                           "denied by DNSBL: %s for: %s", srv_elts[i], r->uri);
  315             r->status = 403;
  316             generate_page(r, srv_elts[i]);
  317             return DONE;
  318         }
  319         else
  320         {
  321             /* Log some interesting stuff if we don't have any record */
  322             switch(h_errno)
  323             {
  324                 case HOST_NOT_FOUND:
  325                 case NO_ADDRESS:
  326                     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
  327                                   "client not listed on %s",
  328                                   srv_elts[i]);
  329                     break;
  330                 case NO_RECOVERY:
  331                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  332                                   "non-recoverable DNS error while checking DNSBL on %s for %s",
  333                                   srv_elts[i], r->uri);
  334                     break;
  335                 case TRY_AGAIN:
  336                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  337                                   "temporary DNS error while checking DNSBL on %s for %s",
  338                                   srv_elts[i], r->uri);
  339                     break;
  340             }
  341         }
  342 #endif
  343     }
  344 
  345 #ifdef HAVE_LIBUDNS
  346     struct pollfd pfd;
  347     struct udns_cb_data **data_array_elts;
  348 
  349     pfd.fd = dns_sock(0);
  350     pfd.events = POLLIN;
  351 
  352     data_array_elts = (struct udns_cb_data **) data_array->elts;
  353 
  354     /* While we have a queue active */
  355     while(dns_active(&dns_defctx))
  356         if(poll(&pfd, 1, dns_timeouts(0, -1, 0) * 1000))
  357             dns_ioevent(0, 0);
  358 
  359     dns_close(&dns_defctx);
  360 
  361     /* Check if one of the DNSBL server has blacklisted */
  362     for(i = 0; i < data_array->nelts; i++)
  363         if(data_array_elts[i]->blacklist)
  364         {
  365             r->status = 403;
  366             generate_page(r, data_array_elts[i]->dnsbl);
  367             return DONE;
  368         }
  369 #endif
  370 
  371     return OK;
  372 }
  373 
  374 /* Callback function used for initialization */
  375 static int defensible_init(apr_pool_t *p,
  376                        apr_pool_t *plog __attribute__ ((unused)),
  377                        apr_pool_t *ptemp __attribute__ ((unused)),
  378                        server_rec *s __attribute__ ((unused)))
  379 {
  380     ap_add_version_component(p, DEFENSIBLE_HEADER_STRING);
  381 
  382     return OK;
  383 }
  384 
  385 /* Register hooks */
  386 static void register_hooks(apr_pool_t *p __attribute__ ((unused)))
  387 {
  388     ap_hook_access_checker(check_dnsbl_access, NULL, NULL, APR_HOOK_MIDDLE);
  389     ap_hook_post_config(defensible_init, NULL, NULL, APR_HOOK_LAST);
  390 }
  391 
  392 /* Declare our module to apache2 */
  393 module AP_MODULE_DECLARE_DATA defensible_module =
  394 {
  395     STANDARD20_MODULE_STUFF,
  396     create_defensible_config,   /* dir config creater */
  397     NULL,                       /* dir merger --- default is to override */
  398     NULL,                       /* server config */
  399     NULL,                       /* merge server config */
  400     defensible_cmds,
  401     register_hooks              /* register hooks */
  402 };