/* * mod_blockinator for Apache v2.2 * * Author: Scott Wallace * Date: March 2012 * * Written for European Directories to integrate traffic * blocking based upon recommendations from a Sentor * device. * * The SQLite DB is populated with a Python script that * can be found at, * http://svn.eurodir.eu/svn/common/trunk/scripts/blockinator/trunk/ * */ #include "httpd.h" #include "http_config.h" #include "http_log.h" #include #include module AP_MODULE_DECLARE_DATA blockinator_module; /* * Global variables for use everywhere */ sqlite3 *db; typedef struct { const char *basepath; const char *db; } blockinator_cfg_t; /* * Function to build the configuration for the module */ static void *create_blockinator_config(apr_pool_t *p, server_rec *s) { /* Allocate some memory using the proper Apache function */ blockinator_cfg_t *mod_config = apr_pcalloc(p, sizeof(blockinator_cfg_t)); return mod_config; } /* * Function to set the configuration item, 'basepath' */ static const char *blockinator_set_config_basepath(cmd_parms *parms, void *mconfig, const char *arg) { blockinator_cfg_t *cfg = ap_get_module_config(parms->server->module_config, &blockinator_module); cfg->basepath = (char *)arg; return NULL; } /* * Function to set the configuration item, 'db' */ static const char *blockinator_set_config_db(cmd_parms *parms, void *mconfig, const char *arg) { blockinator_cfg_t *cfg = ap_get_module_config(parms->server->module_config, &blockinator_module); cfg->db = (char *)arg; return NULL; } /* * Main HTTP request handler */ static int mod_blockinator_method_handler(request_rec *r) { const char *remote_ip, *forwarded_ip, *useragent, *cookie; char *statement; char *sqlite3_error; sqlite3_stmt *sqlite3_statement; /* Capture the relevant information from the inbound request */ remote_ip = r->connection->remote_ip; forwarded_ip = apr_table_get(r->headers_in, "X-Forwarded-For"); useragent = apr_table_get(r->headers_in, "User-Agent"); cookie = apr_table_get(r->headers_in, "Cookie"); /* Build the SQL statement */ statement = sqlite3_mprintf("SELECT * FROM blocklist WHERE remote_ip = '%q' AND (forwarded_ip = 'ANY' OR forwarded_ip = '%q') AND (useragent = 'ANY' OR useragent = '%q') AND (cookie = 'ANY' OR instr('%q', cookie))", remote_ip, forwarded_ip, useragent, cookie); /* Prepare the statement */ if (sqlite3_prepare_v2(db, statement, BUFSIZ, &sqlite3_statement, NULL) != SQLITE_OK) { /* SQLite error. Allow. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "SQLite error (%s). Allow traffic from %s by default.", sqlite3_error, remote_ip); sqlite3_free(sqlite3_error); return DECLINED; } /* Check for any results. */ if (sqlite3_step(sqlite3_statement) == SQLITE_ROW) { /* SQLite results. Time to block. */ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Blocklist match. (Forwarded_IP: %s, User-Agent: %s, Cookie: %s)", forwarded_ip, useragent, cookie); apr_table_set(r->headers_in, "X-Block", "1"); } /* Tidy-up the SQLite way. */ if (sqlite3_finalize(sqlite3_statement) != SQLITE_OK) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "SQLite error freeing the SQLite compile statement (%s). Possible memory leak.", sqlite3_error); sqlite3_free(sqlite3_error); } return DECLINED; } /* * Module initialiser */ static void mod_blockinator_init_handler(apr_pool_t *p, server_rec *s) { char *sqlite3_error; char sqlite3_instr_extension[BUFSIZ]; /* Read config from module */ blockinator_cfg_t *cfg = ap_get_module_config(s->module_config, &blockinator_module); /* Build the full path to the SQLite instr() extension */ sprintf(sqlite3_instr_extension, "%s/sqlite_instr/instr.sqlext", cfg->basepath); /* Open the SQLite DB */ if (sqlite3_open_v2(cfg->db, &db, SQLITE_OPEN_READONLY, "unix-none")) { /* Error. */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_blockinator: SQLite error (%s). Could not open database.", sqlite3_errmsg(db)); } /* Load the EDSA SQLite extension for instr() */ if ((sqlite3_enable_load_extension(db, 1) != SQLITE_OK) || (sqlite3_load_extension(db, sqlite3_instr_extension, 0, &sqlite3_error) != SQLITE_OK) ) { /* FAIL */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_blockinator: SQLite error (%s). Failed to load the instr() extension.", sqlite3_error); sqlite3_free(sqlite3_error); } else { /* SQLite module successfully loaded. */ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_blockinator: SQLite loaded the instr() extension successfully."); } } /* * Register hooks */ static void register_hooks(apr_pool_t *p) { /* Register the module initialiser */ ap_hook_child_init(mod_blockinator_init_handler, NULL, NULL, APR_HOOK_LAST); /* Register the module header parser in the post_read_request phase of Apache */ ap_hook_post_read_request(mod_blockinator_method_handler, NULL, NULL, APR_HOOK_FIRST); } /* * Apache configuration directives */ static const command_rec mod_blockinator_directives[] = { AP_INIT_TAKE1( "BlockinatorHome", blockinator_set_config_basepath, NULL, RSRC_CONF, "BlockinatorHome (filepath). The base directory of Blockinator." ), AP_INIT_TAKE1( "BlockinatorBlocklistDB", blockinator_set_config_db, NULL, RSRC_CONF, "BlockinatorBlocklistDB (filepath). The absolute path of Blockinator blocklist SQLite DB." ), {NULL} }; /* * Main module code */ module AP_MODULE_DECLARE_DATA blockinator_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-directory config structures */ NULL, /* merge per-directory config structures */ create_blockinator_config, /* create per-server config structures */ NULL, /* merge per-server config structures */ mod_blockinator_directives, /* command handlers */ register_hooks /* register hooks */ };