1 |
/* Copyright 1999-2007 The Apache Software Foundation or its licensors, as |
2 |
* applicable. |
3 |
* |
4 |
* Licensed under the Apache License, Version 2.0 (the "License"); |
5 |
* you may not use this file except in compliance with the License. |
6 |
* You may obtain a copy of the License at |
7 |
* |
8 |
* http://www.apache.org/licenses/LICENSE-2.0 |
9 |
* |
10 |
* Unless required by applicable law or agreed to in writing, software |
11 |
* distributed under the License is distributed on an "AS IS" BASIS, |
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
* See the License for the specific language governing permissions and |
14 |
* limitations under the License. |
15 |
*/ |
16 |
|
17 |
/* |
18 |
* Original Copyright (c) Netmask.IT!® 2006-2007 |
19 |
* |
20 |
* DNS Protocol module for Apache 2.x |
21 |
*/ |
22 |
|
23 |
|
24 |
/* TODO LIST: |
25 |
** filters for compression (RFC 1035) |
26 |
** output truncation filter (limit UDP to 512 + set TC bit) |
27 |
** forward queries (*via tcp / * via udp with retransmission policy) |
28 |
** support AXFR queries for slaves to update from us |
29 |
** How should we update the scoreboard? we use many logical reads/writes |
30 |
** query cache via mod_cache with special key_generator |
31 |
** Error support dns_die() |
32 |
** IPV6 Support? |
33 |
** Wildcard virtualhosts will return 0.0.0.0 as the IP. What do we do with this? |
34 |
*/ |
35 |
|
36 |
#include "mod_dns.h" |
37 |
|
38 |
static server_rec *base_server; |
39 |
|
40 |
APR_HOOK_STRUCT( |
41 |
APR_HOOK_LINK(post_read_request) |
42 |
APR_HOOK_LINK(create_request) |
43 |
APR_HOOK_LINK(handler) |
44 |
) |
45 |
|
46 |
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(dns, DNS, int, post_read_request, |
47 |
(dns_message_t *dns), |
48 |
(dns), |
49 |
OK, DECLINED) |
50 |
|
51 |
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(dns, DNS, int, create_request, |
52 |
(request_rec *r, dns_message_t *dns), |
53 |
(r, dns), |
54 |
OK, DECLINED) |
55 |
|
56 |
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dns, DNS, int, handler, |
57 |
(request_rec *r), |
58 |
(r), DECLINED) |
59 |
|
60 |
/** Be nice and export so other modules can remove it if they really want */ |
61 |
DNS_DECLARE_DATA ap_filter_rec_t* dns_buffered_write_response_filter_handle; |
62 |
DNS_DECLARE_DATA ap_filter_rec_t* dns_buffered_write_proto_filter_handle; |
63 |
DNS_DECLARE_DATA ap_filter_rec_t* dns_output_protocol_filter_handle; |
64 |
DNS_DECLARE_DATA ap_filter_rec_t* dns_tcp_length_filter_handle; |
65 |
|
66 |
/** This isn't exported by core, so we need to redo ourselves... */ |
67 |
DNS_DECLARE(int) dns_invoke_filter_init(ap_filter_t *filters) |
68 |
{ |
69 |
while (filters) { |
70 |
if (filters->frec->filter_init_func) { |
71 |
int result = filters->frec->filter_init_func(filters); |
72 |
if (result != OK) { |
73 |
return result; |
74 |
} |
75 |
} |
76 |
filters = filters->next; |
77 |
} |
78 |
return OK; |
79 |
} |
80 |
|
81 |
DNS_DECLARE(request_rec *) dns_create_request(dns_message_t *dns) { |
82 |
request_rec *r; |
83 |
apr_pool_t *p; |
84 |
|
85 |
apr_pool_create(&p, dns->pool); |
86 |
apr_pool_tag(p, "request"); |
87 |
r = apr_pcalloc(p, sizeof(*r)); |
88 |
r->pool = p; |
89 |
r->connection = dns->conn; |
90 |
r->server = dns->conn->base_server; |
91 |
|
92 |
r->user = NULL; |
93 |
r->ap_auth_type = NULL; |
94 |
r->next = NULL; |
95 |
r->prev = NULL; |
96 |
r->main = NULL; |
97 |
|
98 |
r->allowed_methods = ap_make_method_list(p, 2); |
99 |
|
100 |
r->headers_in = apr_table_make(r->pool, 1); |
101 |
r->subprocess_env = apr_table_make(r->pool, 1); |
102 |
r->headers_out = apr_table_make(r->pool, 1); |
103 |
r->err_headers_out = apr_table_make(r->pool, 1); |
104 |
r->notes = apr_table_make(r->pool, 5); |
105 |
|
106 |
r->request_config = ap_create_request_config(r->pool); |
107 |
/* Must be set before we run create request hook */ |
108 |
|
109 |
r->proto_output_filters = dns->filters_out; |
110 |
r->output_filters = r->proto_output_filters; |
111 |
r->proto_input_filters = dns->filters_in; |
112 |
r->input_filters = r->proto_input_filters; |
113 |
r->per_dir_config = r->server->lookup_defaults; |
114 |
|
115 |
r->sent_bodyct = 0; /* bytect isn't for body */ |
116 |
|
117 |
r->read_body = REQUEST_NO_BODY; |
118 |
r->handler = NULL; |
119 |
|
120 |
r->status = HTTP_OK; /* Until we get a request */ |
121 |
r->status_line = NULL; |
122 |
r->the_request = NULL; |
123 |
|
124 |
/* Begin by presuming any module can make its own path_info assumptions, |
125 |
* until some module interjects and changes the value. |
126 |
*/ |
127 |
r->used_path_info = AP_REQ_DEFAULT_PATH_INFO; |
128 |
|
129 |
ap_set_module_config(r->request_config, &dns_module, dns); |
130 |
/** Cheat here and add our buffered write filter before anyone |
131 |
* else gets in the way */ |
132 |
ap_add_output_filter_handle(dns_buffered_write_response_filter_handle, NULL, |
133 |
r, dns->conn); |
134 |
|
135 |
/** Let other modules do what they need now */ |
136 |
ap_run_create_request(r); |
137 |
dns_run_create_request(r, dns); |
138 |
return r; |
139 |
} |
140 |
|
141 |
/** Our buffered output filter */ |
142 |
typedef struct { |
143 |
apr_bucket_brigade *bb; |
144 |
int seen_eos; |
145 |
} dns_buffered_write_filter_ctx; |
146 |
|
147 |
static apr_status_t dns_buffered_write_filter(ap_filter_t *f, |
148 |
apr_bucket_brigade *bb) |
149 |
{ |
150 |
dns_buffered_write_filter_ctx *ctx = f->ctx; |
151 |
apr_bucket *b; |
152 |
apr_off_t len = 0; |
153 |
apr_status_t rv; |
154 |
|
155 |
if (ctx == NULL) { |
156 |
f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx)); |
157 |
ctx->bb = apr_brigade_create(f->r->pool, |
158 |
f->r->connection->bucket_alloc); |
159 |
ctx->seen_eos = 0; |
160 |
} |
161 |
|
162 |
if (ctx->seen_eos) |
163 |
return APR_SUCCESS; |
164 |
|
165 |
rv = APR_SUCCESS; |
166 |
|
167 |
/** Buffer until we get a EOS or FLUSH */ |
168 |
for (b = APR_BRIGADE_FIRST(bb); |
169 |
b != APR_BRIGADE_SENTINEL(bb) && !APR_BRIGADE_EMPTY(bb); |
170 |
b = APR_BUCKET_NEXT(b)) |
171 |
{ |
172 |
APR_BUCKET_REMOVE(b); |
173 |
APR_BRIGADE_INSERT_TAIL(ctx->bb, b); |
174 |
apr_brigade_length(ctx->bb, 1, &len); |
175 |
if ((APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b)) || |
176 |
(len > APR_BUCKET_BUFF_SIZE)) |
177 |
{ |
178 |
rv = ap_pass_brigade(f->next, ctx->bb); |
179 |
apr_brigade_cleanup(ctx->bb); |
180 |
if (APR_BUCKET_IS_EOS(b)) |
181 |
ctx->seen_eos = 1; |
182 |
} |
183 |
} |
184 |
|
185 |
/** Give an empty brigade back */ |
186 |
apr_brigade_cleanup(bb); |
187 |
|
188 |
/** if we got the EOS before the brigade ended, replace the unsent data */ |
189 |
if (ctx->seen_eos) { |
190 |
APR_BRIGADE_CONCAT(bb, ctx->bb); |
191 |
apr_brigade_destroy(ctx->bb); |
192 |
} |
193 |
|
194 |
return rv; |
195 |
} |
196 |
|
197 |
/** Our protocol output filter */ |
198 |
static apr_status_t dns_output_protocol_filter(ap_filter_t *f, |
199 |
apr_bucket_brigade *bb) |
200 |
{ |
201 |
|
202 |
dns_message_t *dns; |
203 |
apr_bucket *b; |
204 |
apr_size_t len = 0; |
205 |
apr_status_t rv; |
206 |
dns_rr_t *rr, *tmp_rr; |
207 |
const char *data; |
208 |
char *response; |
209 |
apr_bucket_brigade *bb_out; |
210 |
|
211 |
/*if (AP_BUCKET_IS_ERROR(e)) { |
212 |
ap_bucket_error *eb = e->data; |
213 |
|
214 |
ap_die(eb->status, r); |
215 |
return AP_FILTER_ERROR; |
216 |
}*/ |
217 |
/** Get our DNS message */ |
218 |
dns = (dns_message_t *)ap_get_module_config(f->r->request_config, |
219 |
&dns_module); |
220 |
|
221 |
if (!(dns)) { |
222 |
ap_remove_output_filter(f); |
223 |
return ap_pass_brigade(f->next, bb); |
224 |
} |
225 |
|
226 |
rv = APR_SUCCESS; |
227 |
|
228 |
/** We can't flush and EOS means end of answer; we send content onward |
229 |
* when we've got all of our answers */ |
230 |
for (b = APR_BRIGADE_FIRST(bb); |
231 |
b != APR_BRIGADE_SENTINEL(bb) && !APR_BRIGADE_EMPTY(bb); |
232 |
b = APR_BUCKET_NEXT(b)) |
233 |
{ |
234 |
if (BUCKET_IS_DNS_RR(b)) { |
235 |
/** Got an RR */ |
236 |
if (BUCKET_IS_DNS_ANSWER(b)) { |
237 |
rr = apr_array_push(dns->answer); |
238 |
dns->header->ancount++; |
239 |
} |
240 |
else if (BUCKET_IS_DNS_AUTHORITY(b)) { |
241 |
rr = apr_array_push(dns->authority); |
242 |
dns->header->nscount++; |
243 |
} |
244 |
else if (BUCKET_IS_DNS_ADDITIONAL(b)) { |
245 |
rr = apr_array_push(dns->additional); |
246 |
dns->header->arcount++; |
247 |
} |
248 |
else |
249 |
rr = NULL; |
250 |
|
251 |
rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); |
252 |
/** This is a waste of memory, but unserialize (correctly) |
253 |
* allocates a new rr, and apr_array_push will lose it's |
254 |
* pointer... Since we can't play with the array's |
255 |
* pointers (why not?) we copy the block of memory */ |
256 |
dns_rr_unserialize(f->r->pool, data, &tmp_rr); |
257 |
memcpy(rr, tmp_rr, sizeof(*rr)); |
258 |
apr_bucket_delete(b); |
259 |
/** Handle errors */ |
260 |
|
261 |
continue; |
262 |
} |
263 |
|
264 |
if (APR_BUCKET_IS_EOS(b)) { |
265 |
/** End of query - ignore everything after this */ |
266 |
apr_brigade_cleanup(bb); |
267 |
dns->answered++; |
268 |
break; |
269 |
} |
270 |
|
271 |
if (APR_BUCKET_IS_FLUSH(b)) { |
272 |
/** We can't flush before we've assembled the response - sorry */ |
273 |
apr_bucket_delete(b); |
274 |
continue; |
275 |
} |
276 |
|
277 |
/** Handle error buckets */ |
278 |
|
279 |
/** We don't know what to do with anything else... */ |
280 |
apr_bucket_delete(b); |
281 |
} |
282 |
|
283 |
if (dns->answered == dns->header->qdcount) { |
284 |
/** We've assembled a response - send it out */ |
285 |
bb_out = apr_brigade_create(dns->pool, dns->conn->bucket_alloc); |
286 |
rv = dns_write_response(dns, &response, &len); |
287 |
b = apr_bucket_heap_create(response, len, free, |
288 |
bb_out->bucket_alloc); |
289 |
APR_BRIGADE_INSERT_TAIL(bb_out, b); |
290 |
/** Add flush (UDP needs this push) */ |
291 |
b = apr_bucket_flush_create(bb_out->bucket_alloc); |
292 |
APR_BRIGADE_INSERT_TAIL(bb_out, b); |
293 |
/** Add EOS */ |
294 |
b = apr_bucket_eos_create(bb_out->bucket_alloc); |
295 |
APR_BRIGADE_INSERT_TAIL(bb_out, b); |
296 |
/** Send it on its merry way */ |
297 |
rv = ap_pass_brigade(f->next, bb_out); |
298 |
/** Core-output will destroy this brigade for us */ |
299 |
} |
300 |
return rv; |
301 |
} |
302 |
|
303 |
/** TCP set-length filter - we cheat now by assuming one brigade holds |
304 |
* everything; it almost certainly does, but that's besides the point */ |
305 |
static apr_status_t dns_tcp_length_filter(ap_filter_t *f, |
306 |
apr_bucket_brigade *bb) |
307 |
{ |
308 |
apr_bucket *b; |
309 |
apr_uint16_t len = 0; |
310 |
apr_status_t rv; |
311 |
int sd_type; |
312 |
|
313 |
/** If we have a TCP connection, we'll need to write length */ |
314 |
apr_socket_type_get(ap_get_module_config(f->r->connection->conn_config, |
315 |
&core_module), |
316 |
&sd_type); |
317 |
if (sd_type == SOCK_STREAM) { |
318 |
apr_brigade_length(bb, 1, (apr_off_t *)&len); |
319 |
len = htons(len); |
320 |
|
321 |
b = apr_bucket_transient_create((const char *)&len, 2, bb->bucket_alloc); |
322 |
APR_BRIGADE_INSERT_HEAD(bb, b); |
323 |
} |
324 |
|
325 |
rv = ap_pass_brigade(f->next, bb); |
326 |
|
327 |
/** We've done our work (and don't want to risk screwing more up) */ |
328 |
ap_remove_output_filter(f); |
329 |
|
330 |
return rv; |
331 |
} |
332 |
|
333 |
static void finalize_response(request_rec *r) { |
334 |
apr_bucket_brigade *bb; |
335 |
apr_bucket *b; |
336 |
|
337 |
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); |
338 |
b = apr_bucket_eos_create(bb->bucket_alloc); |
339 |
APR_BRIGADE_INSERT_TAIL(bb, b); |
340 |
ap_pass_brigade(r->output_filters, bb); |
341 |
apr_brigade_destroy(bb); |
342 |
} |
343 |
|
344 |
static int process_dns_connection(conn_rec *c) |
345 |
{ |
346 |
dns_module_config_t *conf; |
347 |
dns_message_t *dns; |
348 |
dns_query_t *q; |
349 |
request_rec *r; |
350 |
server_rec *s = c->base_server; |
351 |
apr_status_t rv; |
352 |
int access_status; |
353 |
int i; |
354 |
|
355 |
conf = (dns_module_config_t *)ap_get_module_config(s->module_config, |
356 |
&dns_module); |
357 |
if (!conf) { |
358 |
/* We're not configured. Something's very wrong. Abort. */ |
359 |
return DECLINED; |
360 |
} |
361 |
|
362 |
if (!conf->enabled) { |
363 |
/* We're configured, but disabled */ |
364 |
return DECLINED; |
365 |
} |
366 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0,c, "I got a connection!"); |
367 |
|
368 |
dns = dns_read_message_header(c); |
369 |
|
370 |
if (!(dns)) { |
371 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "[mod_dns] Unable to read " |
372 |
"DNS message header. Aborting."); |
373 |
return OK; |
374 |
} |
375 |
|
376 |
dns_run_post_read_request(dns); |
377 |
for (i=0;i<dns->header->qdcount;i++) { |
378 |
q = NULL; |
379 |
r = NULL; |
380 |
rv = dns_read_request(dns, &r, &q); |
381 |
if (rv != APR_SUCCESS) { |
382 |
/* Set header->rcode to error and abort */ |
383 |
/* dns_die should read remaining bytes from socket, |
384 |
* set error in dns_header_t, write response, and return |
385 |
*/ |
386 |
} else { |
387 |
/** Allow mod_cache to act as our resolver cache */ |
388 |
access_status = ap_run_quick_handler(r, 0); |
389 |
if (access_status == DECLINED) { |
390 |
access_status = ap_process_request_internal(r); |
391 |
if (access_status != OK) { |
392 |
/** Handle me */ |
393 |
} |
394 |
/** Invoke handler (return response) */ |
395 |
access_status = dns_invoke_filter_init(r->output_filters); |
396 |
if (access_status != OK) |
397 |
/** Log, but continue */ |
398 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, access_status, |
399 |
r, "[mod_dns] Error initializing " |
400 |
"output filters for %s", |
401 |
r->the_request); |
402 |
|
403 |
/** Run handler - only DNS registered handlers |
404 |
* to avoid default-handler */ |
405 |
access_status = dns_run_handler(r); |
406 |
finalize_response(r); |
407 |
|
408 |
if (access_status == DECLINED) { |
409 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
410 |
"[mod_dns] handler not found for method \"%s\"", |
411 |
r->handler, r->method); |
412 |
/** die */ |
413 |
} |
414 |
} |
415 |
if (access_status == DONE) { |
416 |
/* e.g., something not in storage like TRACE */ |
417 |
access_status = OK; |
418 |
} |
419 |
|
420 |
if (access_status != OK) { |
421 |
/** Return error (dns_die)*/ |
422 |
} else { |
423 |
/** Cleanup */ |
424 |
} |
425 |
|
426 |
/* FIXME: |
427 |
* Suppose that the logging causes a DNS lookup to occur, which may |
428 |
* have a high latency. If we hold off on this packet, then it'll |
429 |
* appear like the link is stalled when really it's the application |
430 |
* that's stalled. |
431 |
* Since we can't send packets until all queries in the message |
432 |
* are answered, we should add the request_rec to a list and |
433 |
* batch-log them later |
434 |
*/ |
435 |
ap_run_log_transaction(r); |
436 |
} |
437 |
} |
438 |
/** Flush response */ |
439 |
/** Log + cleanup */ |
440 |
return OK; |
441 |
} |
442 |
|
443 |
/* -------------------------------------------------------------- */ |
444 |
/* Default handlers */ |
445 |
|
446 |
static server_rec *matches_aliases(server_rec *s, const char *host) |
447 |
{ |
448 |
int i; |
449 |
apr_array_header_t *names; |
450 |
|
451 |
while (s) { |
452 |
/* match ServerName */ |
453 |
if (!strcasecmp(host, s->server_hostname)) { |
454 |
return s; |
455 |
} |
456 |
|
457 |
/* search all the aliases from ServerAlias directive */ |
458 |
names = s->names; |
459 |
if (names) { |
460 |
char **name = (char **) names->elts; |
461 |
for (i = 0; i < names->nelts; ++i) { |
462 |
if(!name[i]) continue; |
463 |
if (!strcasecmp(host, name[i])) |
464 |
return s; |
465 |
} |
466 |
} |
467 |
names = s->wild_names; |
468 |
if (names) { |
469 |
char **name = (char **) names->elts; |
470 |
for (i = 0; i < names->nelts; ++i) { |
471 |
if(!name[i]) continue; |
472 |
if (!ap_strcasecmp_match(host, name[i])) |
473 |
return s; |
474 |
} |
475 |
} |
476 |
s = s->next; |
477 |
} |
478 |
return NULL; |
479 |
} |
480 |
|
481 |
static int dns_default_handler(request_rec *r) { |
482 |
dns_rr_t* rr; |
483 |
dns_rdata_a_t *a; |
484 |
server_rec *s; |
485 |
char *name; |
486 |
|
487 |
if (strcasecmp(r->handler, DNS_MAGIC_TYPE)) |
488 |
return DECLINED; |
489 |
if (strcasecmp(r->method, "A")) |
490 |
return DECLINED; |
491 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
492 |
"Running A/CNAME lookup for host %s", r->hostname); |
493 |
|
494 |
if ((s = matches_aliases(base_server, r->hostname)) == NULL) |
495 |
return DECLINED; |
496 |
|
497 |
name = (char *)r->hostname; |
498 |
if (strcasecmp(s->server_hostname, r->hostname)) { |
499 |
dns_rdata_cname_t *cname; |
500 |
/** Add CNAME */ |
501 |
cname = apr_palloc(r->pool, sizeof(*cname)); |
502 |
cname->cname = apr_pstrdup(r->pool, s->server_hostname); |
503 |
rr = dns_create_rr(r, r->hostname, DNS_TYPE_CNAME, DNS_CLASS_IN, 0); |
504 |
rr->rdata->rdata = cname; |
505 |
dns_add_rr_answer(r, rr); |
506 |
name = s->server_hostname; |
507 |
} |
508 |
/** Add A */ |
509 |
a = apr_pcalloc(r->pool, sizeof(*a)); |
510 |
a->address = inet_addr(inet_ntoa(*((struct in_addr *)s->addrs->host_addr->ipaddr_ptr))); |
511 |
/** We get network address in network order; since serialize expects it in |
512 |
* host order, switch here.*/ |
513 |
a->address = ntohl(a->address); |
514 |
rr = dns_create_rr(r, name, DNS_TYPE_A, DNS_CLASS_IN, 0); |
515 |
rr->rdata->rdata = a; |
516 |
dns_add_rr_answer(r, rr); |
517 |
return OK; |
518 |
} |
519 |
|
520 |
static int dns_mx_handler(request_rec *r) { |
521 |
dns_module_config_t *conf; |
522 |
dns_rr_t* rr; |
523 |
dns_rdata_mx_t *mx; |
524 |
apr_array_header_t *mxs; |
525 |
server_rec *s; |
526 |
char *name; |
527 |
|
528 |
if (strcasecmp(r->handler, DNS_MAGIC_TYPE)) |
529 |
return DECLINED; |
530 |
if (strcasecmp(r->method, "MX")) |
531 |
return DECLINED; |
532 |
|
533 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
534 |
"Running MX lookup for host %s", r->hostname); |
535 |
|
536 |
if ((s = matches_aliases(base_server, r->hostname)) == NULL) |
537 |
return DECLINED; |
538 |
|
539 |
name = (char *)r->hostname; |
540 |
if (strcasecmp(s->server_hostname, r->hostname)) |
541 |
return DECLINED; /* Require exact match here */ |
542 |
|
543 |
conf = |
544 |
(dns_module_config_t *)ap_get_module_config(s->module_config, |
545 |
&dns_module); |
546 |
|
547 |
if (!(conf->mx)) |
548 |
return DECLINED; |
549 |
|
550 |
mxs = apr_array_copy(r->pool, conf->mx); |
551 |
|
552 |
while (!(apr_is_empty_array(mxs))) { |
553 |
mx = apr_array_pop(mxs); |
554 |
rr = dns_create_rr(r, r->hostname, DNS_TYPE_MX, DNS_CLASS_IN, 0); |
555 |
rr->rdata->rdata = mx; |
556 |
dns_add_rr_answer(r, rr); |
557 |
} |
558 |
return OK; |
559 |
} |
560 |
|
561 |
static int ap_create_request_handler(request_rec *r) { |
562 |
dns_message_t *dns; |
563 |
|
564 |
dns = (dns_message_t *)ap_get_module_config(r->request_config, |
565 |
&dns_module); |
566 |
|
567 |
if (!(dns)) |
568 |
return DECLINED; |
569 |
|
570 |
ap_add_output_filter_handle(dns_output_protocol_filter_handle, NULL, |
571 |
r, dns->conn); |
572 |
/* ap_add_output_filter_handle(dns_buffered_write_proto_filter_handle, NULL, |
573 |
r, dns->conn);*/ |
574 |
ap_add_output_filter_handle(dns_tcp_length_filter_handle, NULL, |
575 |
r, dns->conn); |
576 |
|
577 |
/** Don't care what this is, as long as it's not OK, We do it to prevent |
578 |
* default HTTP protocol handlers from registering */ |
579 |
return DECLINED * 2; |
580 |
} |
581 |
|
582 |
static int dns_post_read_request(dns_message_t *dns) { |
583 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, dns->conn, |
584 |
"I got %d queries in the message", dns->header->qdcount); |
585 |
return OK; |
586 |
} |
587 |
|
588 |
static int dns_post_config(apr_pool_t *p, apr_pool_t *plog, |
589 |
apr_pool_t *ptemp, server_rec *s) |
590 |
{ |
591 |
base_server = s; |
592 |
dns_register_rr((dns_rdata_t *)&dns_rdata_a, DNS_TYPE_A); |
593 |
dns_register_rr((dns_rdata_t *)&dns_rdata_cname, DNS_TYPE_CNAME); |
594 |
dns_register_rr((dns_rdata_t *)&dns_rdata_mx, DNS_TYPE_MX); |
595 |
return OK; |
596 |
} |
597 |
|
598 |
|
599 |
|
600 |
/* -------------------------------------------------------------- */ |
601 |
/* Setup configurable data */ |
602 |
|
603 |
static void *create_dns_config(apr_pool_t *p, server_rec *s) |
604 |
{ |
605 |
dns_module_config_t *ps = apr_pcalloc(p, sizeof(*ps)); |
606 |
ps->enabled = 0; |
607 |
ps->enabled_set = 0; |
608 |
ps->default_ttl = DEFAULT_DEFAULT_TTL; |
609 |
ps->default_ttl_set = 0; |
610 |
ps->mx = NULL; |
611 |
return ps; |
612 |
} |
613 |
|
614 |
static void *merge_dns_config(apr_pool_t *p, void *basev, void *overridesv) |
615 |
{ |
616 |
dns_module_config_t *ps = apr_pcalloc(p, sizeof(*ps)); |
617 |
dns_module_config_t *base = (dns_module_config_t *) basev; |
618 |
dns_module_config_t *overrides = (dns_module_config_t *) overridesv; |
619 |
|
620 |
ps->enabled = |
621 |
(overrides->enabled_set == 0) |
622 |
? base->enabled |
623 |
: overrides->enabled; |
624 |
ps->default_ttl = |
625 |
(overrides->default_ttl_set == 0) |
626 |
? base->default_ttl |
627 |
: overrides->default_ttl; |
628 |
|
629 |
if (base->mx || overrides->mx) { |
630 |
if (base->mx) |
631 |
ps->mx = base->mx; |
632 |
if (overrides->mx) |
633 |
if (ps->mx) { |
634 |
apr_array_cat(ps->mx, overrides->mx); |
635 |
} else { |
636 |
ps->mx = overrides->mx; |
637 |
} |
638 |
} |
639 |
|
640 |
|
641 |
return ps; |
642 |
} |
643 |
|
644 |
static const char *set_dns_enabled(cmd_parms *parms, void *dummy, int flag) |
645 |
{ |
646 |
dns_module_config_t *conf; |
647 |
|
648 |
const char *err = ap_check_cmd_context(parms, |
649 |
NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); |
650 |
if (err) { |
651 |
return err; |
652 |
} |
653 |
|
654 |
conf = |
655 |
(dns_module_config_t *)ap_get_module_config(parms->server->module_config, |
656 |
&dns_module); |
657 |
conf->enabled = flag; |
658 |
conf->enabled_set = 1; |
659 |
return NULL; |
660 |
} |
661 |
|
662 |
static const char *set_dns_default_ttl(cmd_parms *parms, void *dummy, |
663 |
const char *arg) |
664 |
{ |
665 |
dns_module_config_t *conf; |
666 |
|
667 |
const char *err = ap_check_cmd_context(parms, GLOBAL_ONLY); |
668 |
if (err) { |
669 |
return err; |
670 |
} |
671 |
|
672 |
conf = |
673 |
(dns_module_config_t *)ap_get_module_config(parms->server->module_config, |
674 |
&dns_module); |
675 |
conf->default_ttl = (apr_size_t) atol(arg); |
676 |
conf->default_ttl_set = 1; |
677 |
return NULL; |
678 |
} |
679 |
|
680 |
static const char *set_dns_add_mx(cmd_parms *parms, void *dummy, |
681 |
const char *preference, const char *exchange) |
682 |
{ |
683 |
dns_module_config_t *conf; |
684 |
dns_rdata_mx_t *mx; |
685 |
|
686 |
const char *err = ap_check_cmd_context(parms, |
687 |
NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); |
688 |
if (err) { |
689 |
return err; |
690 |
} |
691 |
|
692 |
conf = |
693 |
(dns_module_config_t *)ap_get_module_config(parms->server->module_config, |
694 |
&dns_module); |
695 |
|
696 |
if (!(conf->mx)) { |
697 |
conf->mx = apr_array_make(parms->pool, 2, sizeof(dns_rdata_mx_t)); |
698 |
if (!(conf->mx)) |
699 |
return "Could not allocate list of MXs for virtualhost"; |
700 |
} |
701 |
|
702 |
mx = apr_array_push(conf->mx); |
703 |
if (!(mx)) |
704 |
return "Could not allocate MX"; |
705 |
mx->preference = (apr_uint16_t)apr_atoi64(preference); |
706 |
mx->exchange = apr_pstrdup(parms->pool, exchange); |
707 |
|
708 |
return NULL; |
709 |
} |
710 |
|
711 |
static const command_rec dns_cmds[] = |
712 |
{ |
713 |
AP_INIT_FLAG("DNSEnabled", set_dns_enabled, NULL, RSRC_CONF, |
714 |
"Enable the DNS server on this VirtualHost."), |
715 |
AP_INIT_TAKE1("DNSDefaultTTL", set_dns_default_ttl, NULL, RSRC_CONF, |
716 |
"Sets the default TTL used for all DNS replies (defaults" |
717 |
"to 30 days"), |
718 |
AP_INIT_TAKE2("DNSAddMX", set_dns_add_mx, NULL, RSRC_CONF, |
719 |
"Adds an MX record to the current domain"), |
720 |
{NULL} |
721 |
}; |
722 |
|
723 |
static void register_hooks(apr_pool_t *p) |
724 |
{ |
725 |
ap_hook_post_config(dns_post_config, NULL, NULL, APR_HOOK_MIDDLE); |
726 |
ap_hook_process_connection(process_dns_connection, NULL, NULL, APR_HOOK_FIRST); |
727 |
/** FIXME: We hook REALLY_LAST - 1 to prevent default HTTP protocol |
728 |
* handlers to be installed for the request, but there ought to be a |
729 |
* better way... */ |
730 |
ap_hook_create_request(ap_create_request_handler, NULL, NULL, |
731 |
APR_HOOK_REALLY_LAST - 1); |
732 |
dns_hook_post_read_request(dns_post_read_request, NULL, NULL, APR_HOOK_MIDDLE); |
733 |
/** Although we don't take care of everything, hook after last so 3rd party |
734 |
* handlers are always before us */ |
735 |
dns_hook_handler(dns_mx_handler, NULL, NULL, APR_HOOK_LAST + 4); |
736 |
dns_hook_handler(dns_default_handler, NULL, NULL, APR_HOOK_LAST + 5); |
737 |
/* Put buffered output filter as close as possible to front */ |
738 |
dns_buffered_write_response_filter_handle = |
739 |
ap_register_output_filter_protocol("DNS_BUFFERED_WRITE", |
740 |
dns_buffered_write_filter, NULL, |
741 |
AP_FTYPE_RESOURCE, 0); |
742 |
dns_output_protocol_filter_handle = |
743 |
ap_register_output_filter_protocol("DNS_OUTPUT_PROTOCOL", |
744 |
dns_output_protocol_filter, NULL, |
745 |
AP_FTYPE_PROTOCOL, |
746 |
AP_FILTER_PROTO_CHANGE + |
747 |
AP_FILTER_PROTO_CHANGE_LENGTH + |
748 |
AP_FILTER_PROTO_NO_BYTERANGE); |
749 |
dns_buffered_write_proto_filter_handle = |
750 |
ap_register_output_filter_protocol("DNS_BUFFERED_WRITE", |
751 |
dns_buffered_write_filter, NULL, |
752 |
AP_FTYPE_PROTOCOL, 0); |
753 |
dns_tcp_length_filter_handle = |
754 |
ap_register_output_filter_protocol("DNS_TCP_LENGTH", |
755 |
dns_tcp_length_filter, NULL, |
756 |
AP_FTYPE_PROTOCOL, |
757 |
AP_FILTER_PROTO_CHANGE + |
758 |
AP_FILTER_PROTO_CHANGE_LENGTH + |
759 |
AP_FILTER_PROTO_NO_BYTERANGE); |
760 |
} |
761 |
|
762 |
/* Dispatch list for API hooks */ |
763 |
module AP_MODULE_DECLARE_DATA dns_module = { |
764 |
STANDARD20_MODULE_STUFF, |
765 |
NULL, /* create per-dir config structures */ |
766 |
NULL, /* merge per-dir config structures */ |
767 |
create_dns_config, /* create per-server config structures */ |
768 |
merge_dns_config, /* merge per-server config structures */ |
769 |
dns_cmds, /* table of config file commands */ |
770 |
register_hooks /* register hooks */ |
771 |
}; |