"Fossies" - the Fresh Open Source Software Archive 
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 };