1 /* $Id$ */
2
3 /***
4 Copyright 2006 Lennart Poettering
5
6 Licensed under the Apache License, Version 2.0 (the "License"); you
7 may not use this file except in compliance with the License. You
8 may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15 implied. See the License for the specific language governing
16 permissions and limitations under the License.
17 ***/
18
19 #include <httpd.h>
20 #include <http_config.h>
21 #include <http_protocol.h>
22 #include <http_log.h>
23 #include <apr_lib.h>
24 #include <ap_config.h>
25 #include <apr_strings.h>
26 #include <unixd.h>
27 #if MODULE_MAGIC_COOKIE >= 0x41503234UL /* "AP24" */
28 #include <mod_unixd.h>
29 #endif
30 #include <apr_signal.h>
31 #include <mpm_common.h>
32
33 #if MODULE_MAGIC_NUMBER_MAJOR >= 20090130
34 #define unixd_setup_child ap_unixd_setup_child
35 #endif
36
37 #include <unistd.h>
38
39 #include <avahi-common/simple-watch.h>
40 #include <avahi-common/domain.h>
41 #include <avahi-common/error.h>
42 #include <avahi-common/alternative.h>
43 #include <avahi-common/gccmacro.h>
44 #include <avahi-client/publish.h>
45
46 #define MOD_DNSSD_USERDATA_KEY "mod-dnssd"
47
48 struct runtime_data;
49
50 struct service_data {
51 struct runtime_data *runtime;
52 apr_pool_t *pool;
53
54 char *host_name;
55 uint16_t port;
56 char *location;
57 char *name;
58 apr_array_header_t *txt_record;
59 apr_array_header_t *types;
60 int append_host_name;
61 char *chosen_name;
62
63 AvahiEntryGroup *group;
64
65 struct service_data *next;
66 };
67
68 struct runtime_data {
69 server_rec *main_server;
70 AvahiClient *client;
71 AvahiSimplePoll *simple_poll;
72 struct global_config_data *global_config_data;
73 apr_pool_t* pool;
74 struct service_data *services;
75 };
76
77 struct global_config_data {
78 int enabled;
79 int user_dir;
80 int vhost;
81 const char *user_dir_path;
82 };
83
84 static int sigterm_pipe_fds[2] = { -1, -1 };
85
86 module AP_MODULE_DECLARE_DATA dnssd_module;
87
88 #define GET_CONFIG_DATA(s) ap_get_module_config((s)->module_config, &dnssd_module)
89
set_nonblock(int fd)90 static int set_nonblock(int fd) {
91 int n;
92
93 ap_assert(fd >= 0);
94
95 if ((n = fcntl(fd, F_GETFL)) < 0)
96 return -1;
97
98 if (n & O_NONBLOCK)
99 return 0;
100
101 return fcntl(fd, F_SETFL, n|O_NONBLOCK);
102 }
103
add_service(struct runtime_data * r,const char * host_name,uint16_t port,const char * location,const char * name,const char * types,int append_host_name,const char * txt_record)104 static void add_service(struct runtime_data *r, const char *host_name, uint16_t port, const char *location, const char *name, const char *types, int append_host_name, const char *txt_record) {
105 struct service_data *d;
106 char *w;
107 ap_assert(r);
108
109 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "add_service: %s %s %s %s", host_name, location, name, txt_record); */
110
111 d = apr_palloc(r->pool, sizeof(struct service_data));
112 ap_assert(d);
113
114 d->pool = NULL;
115 d->runtime = r;
116 d->host_name = apr_pstrdup(r->pool, host_name);
117 d->port = port;
118 d->location = apr_pstrdup(r->pool, location);
119 d->name = apr_pstrdup(r->pool, name);
120 d->append_host_name = append_host_name;
121 d->chosen_name = NULL;
122
123 d->types = apr_array_make(r->pool, 4, sizeof(char*));
124
125 if (types)
126 while (*(w = ap_getword_conf(r->pool, &types)) != 0)
127 *(char**) apr_array_push(d->types) = w;
128
129 d->txt_record = apr_array_make(r->pool, 4, sizeof(char*));
130
131 if (txt_record)
132 while (*(w = ap_getword_conf(r->pool, &txt_record)) != 0)
133 *(char**) apr_array_push(d->txt_record) = w;
134
135 d->group = NULL;
136
137 d->next = r->services;
138 r->services = d;
139
140 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "done"); */
141
142 }
143
assemble_services(struct runtime_data * r)144 static void assemble_services(struct runtime_data *r) {
145 ap_directive_t *v;
146 const char *default_host_name = NULL;
147 uint16_t default_port = 0;
148 struct service_data *j;
149 apr_pool_t *t;
150
151 ap_assert(r);
152
153 apr_pool_create(&t, r->pool);
154
155 for (v = ap_conftree; v; v = v->next) {
156 const char *a = v->args;
157
158 if (strcasecmp(v->directive, "ServerName") == 0) {
159 const char *tdhn = NULL;
160 char *colon;
161 tdhn = ap_getword_conf(t, &a);
162 colon = strrchr(tdhn, ':');
163 if (colon) {
164 apr_size_t sz;
165 if (!default_port) {
166 default_port = (uint16_t) atoi(colon+1);
167 }
168 sz = colon - tdhn;
169 default_host_name = apr_pstrndup(t, tdhn, sz);
170 } else {
171 default_host_name = tdhn;
172 }
173 } else if (strcasecmp(v->directive, "Listen") == 0) {
174 char *sp;
175
176 if (!default_port) {
177 char *colon;
178
179 sp = ap_getword_conf(t, &a);
180 if ((colon = strrchr(sp, ':')))
181 sp = colon + 1;
182
183 default_port = (uint16_t) atoi(sp);
184 }
185 } else if (strcasecmp(v->directive, "DNSSDServicePort") == 0)
186
187 default_port = (uint16_t) atoi(a);
188
189 else if (strcasecmp(v->directive, "<VirtualHost") == 0) {
190 const char *host_name = NULL;
191 uint16_t vport = 0;
192 const char *vname = NULL, *vtypes = NULL, *txt_record = NULL;
193 ap_directive_t *l;
194 char *colon;
195 struct service_data *marker = r->services;
196
197 if ((colon = strrchr(v->args, ':')))
198 vport = (uint16_t) atoi(colon+1);
199
200 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "VHOST: %s ", v->directive); */
201
202 for (l = v->first_child; l; l = l->next) {
203 a = l->args;
204
205 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "VHOST_INTERNAL %s | %s | %s | %s", l->directive, l->args, vname, vtypes); */
206
207 if (strcasecmp(l->directive, "ServerName") == 0) {
208 const char *thn = NULL;
209 thn = ap_getword_conf(t, &a);
210 colon = strrchr(thn, ':');
211 if (colon) {
212 apr_size_t sz;
213 if (!vport)
214 vport = (uint16_t) atoi(colon+1);
215 sz = colon - thn;
216 host_name = apr_pstrndup(t, thn, sz);
217 } else {
218 host_name = thn;
219 }
220 }
221 else if (strcasecmp(l->directive, "DNSSDServiceName") == 0)
222 vname = ap_getword_conf(t, &a);
223 else if (strcasecmp(l->directive, "DNSSDServiceTypes") == 0)
224 vtypes = a;
225 else if (strcasecmp(l->directive, "DNSSDServicePort") == 0)
226 vport = (uint16_t) atoi(a);
227 else if (strcasecmp(l->directive, "DNSSDServiceTxtRecord") == 0)
228 txt_record = a;
229 else if (strcasecmp(l->directive, "<Location") == 0) {
230 ap_directive_t *s;
231 const char *sname = NULL, *stypes = NULL;
232 char *path;
233 size_t i;
234 uint16_t sport = 0;
235
236 path = apr_pstrdup(t, l->args);
237
238 if (*path != 0 && (path[(i = strlen(path) - 1)] == '>'))
239 path[i] = 0;
240
241 for (s = l->first_child; s; s = s->next) {
242 a = s->args;
243
244 if (strcasecmp(s->directive, "DNSSDServiceName") == 0)
245 sname = ap_getword_conf(t, &a);
246 else if (strcasecmp(s->directive, "DNSSDServiceTypes") == 0)
247 stypes = a;
248 else if (strcasecmp(s->directive, "DNSSDServiceTxtRecord") == 0)
249 txt_record = a;
250 else if (strcasecmp(s->directive, "DNSSDServicePort") == 0)
251 sport = (uint16_t) atoi(a);
252 }
253
254 if (sname)
255 add_service(r, NULL, sport, path, sname, stypes, 0, txt_record);
256 }
257 }
258
259 /* Fill in missing data in <Location> based services */
260 for (j = r->services; j && j != marker; j = j->next) {
261 if (!j->pool)
262 j->port = vport;
263 j->host_name = apr_pstrdup(r->pool, host_name);
264 }
265
266 if (r->global_config_data->vhost || vname || vtypes || txt_record)
267 add_service(r, host_name, vport, NULL, vname ? vname : host_name, vtypes, 0, txt_record);
268 }
269 }
270
271 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "ping"); */
272
273 if (r->global_config_data->user_dir) {
274 struct passwd *pw;
275 apr_pool_t *p_loop;
276
277 apr_pool_create(&p_loop, t);
278
279 while ((pw = getpwent())) {
280 apr_finfo_t finfo;
281 char *path;
282 const char *u;
283
284 apr_pool_clear(p_loop);
285
286 if (pw->pw_uid < 500)
287 continue;
288
289 if (*pw->pw_dir == 0 || strcmp(pw->pw_dir, "/") == 0)
290 continue;
291
292 path = apr_pstrcat(p_loop, pw->pw_dir, "/", r->global_config_data->user_dir_path, NULL);
293
294 if (apr_stat(&finfo, path, APR_FINFO_TYPE, p_loop) != APR_SUCCESS)
295 continue;
296
297 if (finfo.filetype != APR_DIR)
298 continue;
299
300 if (access(path, X_OK) != 0)
301 continue;
302
303 if (pw->pw_gecos && *pw->pw_gecos) {
304 char *comma;
305 u = apr_pstrdup(p_loop, pw->pw_gecos);
306 if ((comma = strchr(u, ',')))
307 *comma = 0;
308 } else
309 u = pw->pw_name;
310
311 add_service(r, NULL, 0, apr_pstrcat(p_loop, "/~", pw->pw_name, NULL), apr_pstrcat(p_loop, u, " on ", NULL), NULL, 1, NULL);
312 }
313
314 endpwent();
315
316 apr_pool_destroy(p_loop);
317 }
318
319 if (!default_port)
320 default_port = 80;
321
322 /* Fill in missing data in all services */
323 for (j = r->services; j; j = j->next) {
324 if (!j->port)
325 j->port = default_port;
326
327 if (!j->host_name)
328 j->host_name = apr_pstrdup(r->pool, default_host_name);
329
330 if (!j->name)
331 j->name = apr_pstrdup(r->pool, j->host_name);
332 }
333
334 apr_pool_destroy(t);
335 }
336
337 static void create_service(struct service_data *j);
338
service_callback(AVAHI_GCC_UNUSED AvahiEntryGroup * g,AvahiEntryGroupState state,void * userdata)339 static void service_callback(AVAHI_GCC_UNUSED AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
340 struct service_data *j = userdata;
341
342 switch (state) {
343 case AVAHI_ENTRY_GROUP_UNCOMMITED:
344 case AVAHI_ENTRY_GROUP_REGISTERING:
345 case AVAHI_ENTRY_GROUP_ESTABLISHED:
346 break;
347
348 case AVAHI_ENTRY_GROUP_COLLISION: {
349
350 char *n;
351 ap_assert(j->chosen_name);
352
353 n = avahi_alternative_service_name(j->chosen_name);
354 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, j->runtime->main_server, "Name collision on '%s', changing to '%s'", j->chosen_name, n);
355
356 apr_pool_clear(j->pool);
357 j->chosen_name = apr_pstrdup(j->pool, n);
358
359 create_service(j);
360
361 break;
362 }
363
364 case AVAHI_ENTRY_GROUP_FAILURE:
365 ap_log_error(APLOG_MARK, APLOG_ERR, 0, j->runtime->main_server, "Failed to register service: %s", avahi_strerror(avahi_client_errno(j->runtime->client)));
366 break;
367 }
368 }
369
create_service(struct service_data * j)370 static void create_service(struct service_data *j) {
371 apr_pool_t *t;
372 const char *n;
373 char *p;
374 struct runtime_data *r = j->runtime;
375 char **type, **txt_record;
376 AvahiStringList *strlist = NULL;
377
378
379 if (!j->group)
380 if (!(j->group = avahi_entry_group_new(r->client, service_callback, j))) {
381 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(r->client)));
382 return;
383 }
384
385 ap_assert(j->group);
386 ap_assert(avahi_entry_group_is_empty(j->group));
387
388 apr_pool_create(&t, r->pool);
389
390 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "Service <%s>, host <%s>, port <%u>, location <%s>", j->name, j->host_name, j->port, j->location); */
391
392 if (j->chosen_name)
393 n = j->chosen_name;
394 else if (!j->name)
395 n = avahi_client_get_host_name(r->client);
396 else if (j->append_host_name)
397 n = apr_pstrcat(t, j->name, avahi_client_get_host_name(r->client), NULL);
398 else
399 n = j->name;
400
401 if (!j->pool)
402 apr_pool_create(&j->pool, r->pool);
403
404 if (n != j->chosen_name) {
405 apr_pool_clear(j->pool);
406 j->chosen_name = apr_pstrdup(j->pool, n);
407 }
408
409 p = j->location ? apr_pstrcat(t, "path=", j->location, NULL) : NULL;
410
411 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "%s, %s", p, n); */
412
413 txt_record = (char **) j->txt_record->elts;
414
415 for ( ; *txt_record ; txt_record++)
416 strlist = avahi_string_list_add(strlist, *txt_record);
417
418 if (p)
419 strlist = avahi_string_list_add(strlist, p);
420
421 if (apr_is_empty_array(j->types)) {
422
423 if (avahi_entry_group_add_service_strlst(
424 j->group,
425 AVAHI_IF_UNSPEC,
426 AVAHI_PROTO_UNSPEC,
427 0,
428 n,
429 j->port == 443 ? "_https._tcp" : "_http._tcp",
430 NULL,
431 j->host_name,
432 j->port,
433 strlist) < 0) {
434
435 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "avahi_entry_group_add_service_strlst(\"%s\") failed: %s", n, avahi_strerror(avahi_client_errno(r->client)));
436 }
437
438 } else {
439
440 for (type = (char**) j->types->elts; *type; type++) {
441
442 if (avahi_entry_group_add_service_strlst(
443 j->group,
444 AVAHI_IF_UNSPEC,
445 AVAHI_PROTO_UNSPEC,
446 0,
447 n,
448 *type,
449 NULL,
450 j->host_name,
451 j->port,
452 strlist) < 0) {
453
454 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "avahi_entry_group_add_service_strlst(\"%s\") failed: %s", n, avahi_strerror(avahi_client_errno(r->client)));
455 }
456 }
457 }
458
459 avahi_string_list_free(strlist);
460
461 if (avahi_entry_group_is_empty(j->group)) {
462 avahi_entry_group_free(j->group);
463 j->group = NULL;
464 } else
465 avahi_entry_group_commit(j->group);
466
467 apr_pool_destroy(t);
468 }
469
create_all_services(struct runtime_data * r)470 static void create_all_services(struct runtime_data *r) {
471 struct service_data *j;
472 ap_assert(r);
473
474 for (j = r->services; j; j = j->next)
475 create_service(j);
476 }
477
reset_services(struct runtime_data * r)478 static void reset_services(struct runtime_data *r) {
479 struct service_data *j;
480
481 ap_assert(r);
482
483 for (j = r->services; j; j = j->next) {
484 if (j->group)
485 avahi_entry_group_reset(j->group);
486
487 if (j->pool)
488 apr_pool_clear(j->pool);
489
490 j->chosen_name = NULL;
491 }
492 }
493
free_services(struct runtime_data * r)494 static void free_services(struct runtime_data *r) {
495 struct service_data *j;
496
497 ap_assert(r);
498
499 for (j = r->services; j; j = j->next) {
500
501 if (j->group) {
502 avahi_entry_group_free(j->group);
503 j->group = NULL;
504 }
505
506 if (j->pool)
507 apr_pool_clear(j->pool);
508
509 j->chosen_name = NULL;
510 }
511 }
512
client_callback(AvahiClient * c,AvahiClientState state,void * userdata)513 static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
514 struct runtime_data *r = userdata;
515
516 ap_assert(r);
517
518 r->client = c;
519
520 /* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->main_server, "client_callback(%u)", state); */
521
522 switch (state) {
523 case AVAHI_CLIENT_S_RUNNING:
524 create_all_services(r);
525 break;
526
527 case AVAHI_CLIENT_S_COLLISION:
528 reset_services(r);
529 break;
530
531 case AVAHI_CLIENT_FAILURE:
532
533 if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
534 int error;
535
536 free_services(r);
537 avahi_client_free(r->client);
538
539 if ((r->client = avahi_client_new(avahi_simple_poll_get(r->simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, r, &error)))
540 break;
541
542 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "avahi_client_new() failed: %s", avahi_strerror(error));
543 } else
544 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "Client failure: %s", avahi_strerror(avahi_client_errno(c)));
545
546 avahi_simple_poll_quit(r->simple_poll);
547
548 break;
549
550 case AVAHI_CLIENT_CONNECTING:
551 case AVAHI_CLIENT_S_REGISTERING:
552 break;
553 }
554
555 }
556
sigterm(AVAHI_GCC_UNUSED int s)557 static void sigterm(AVAHI_GCC_UNUSED int s) {
558 const char c = 'x';
559 write(sigterm_pipe_fds[1], &c, sizeof(c));
560 }
561
watch_callback(AvahiWatch * w,int fd,AvahiWatchEvent event,void * userdata)562 static void watch_callback(AvahiWatch *w, int fd, AvahiWatchEvent event, void *userdata) {
563 char c;
564 ssize_t l;
565 struct runtime_data *r = userdata;
566
567 ap_assert(w);
568 ap_assert(fd == sigterm_pipe_fds[0]);
569 ap_assert(event == AVAHI_WATCH_IN);
570 ap_assert(r);
571
572 l = read(fd, &c, sizeof(c));
573 ap_assert(l == sizeof(c));
574
575 avahi_simple_poll_quit(r->simple_poll);
576 }
577
child_process(apr_pool_t * p,server_rec * server,struct global_config_data * d)578 static void child_process(apr_pool_t *p, server_rec *server, struct global_config_data *d) {
579 struct runtime_data r;
580 int error;
581 const AvahiPoll *api;
582 AvahiWatch *w;
583
584 ap_assert(d);
585
586 unixd_setup_child();
587
588 if (pipe(sigterm_pipe_fds) < 0) {
589 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r.main_server, "pipe() failed: %s", strerror(errno));
590 goto quit;
591 }
592
593 set_nonblock(sigterm_pipe_fds[0]);
594 set_nonblock(sigterm_pipe_fds[1]);
595
596 apr_signal(SIGTERM, sigterm);
597 apr_signal(SIGHUP, sigterm);
598 apr_signal(AP_SIG_GRACEFUL, SIG_IGN);
599
600 r.main_server = server;
601 r.global_config_data = d;
602 r.client = NULL;
603 r.simple_poll = NULL;
604 r.services = NULL;
605 apr_pool_create(&r.pool, p);
606
607 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r.main_server, "Child process startup pid=%lu", (unsigned long) getpid()); */
608
609 assemble_services(&r);
610
611 if (!r.services) {
612 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r.main_server, __FILE__": No services found to register");
613 goto quit;
614 }
615
616 if (!(r.simple_poll = avahi_simple_poll_new())) {
617 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r.main_server, "avahi_simple_poll_new() failed: %s", strerror(errno));
618 goto quit;
619 }
620
621 api = avahi_simple_poll_get(r.simple_poll);
622 w = api->watch_new(api, sigterm_pipe_fds[0], AVAHI_WATCH_IN, watch_callback, &r);
623 ap_assert(w);
624
625 if (!(r.client = avahi_client_new(avahi_simple_poll_get(r.simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, &r, &error))) {
626 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r.main_server, "avahi_client_new() failed: %s", avahi_strerror(error));
627 goto quit;
628 }
629
630 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r.main_server, "Child process running"); */
631
632 avahi_simple_poll_loop(r.simple_poll);
633
634 quit:
635
636 if (r.client)
637 avahi_client_free(r.client);
638
639 if (r.simple_poll)
640 avahi_simple_poll_free(r.simple_poll);
641
642 if (r.pool)
643 apr_pool_destroy(r.pool);
644
645 if (sigterm_pipe_fds[0] >= 0)
646 close(sigterm_pipe_fds[0]);
647
648 if (sigterm_pipe_fds[1] >= 0)
649 close(sigterm_pipe_fds[1]);
650
651 sigterm_pipe_fds[0] = sigterm_pipe_fds[1] = -1;
652
653 /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r.main_server, "Child process ending"); */
654 }
655
start_child_process(apr_pool_t * p,server_rec * server,struct global_config_data * d)656 static int start_child_process(apr_pool_t *p, server_rec *server, struct global_config_data *d) {
657 apr_proc_t* proc;
658 apr_status_t status;
659
660 /* ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "Spawning child pid=%lu", (unsigned long) getpid()); */
661
662 proc = apr_palloc(p, sizeof(apr_proc_t));
663 ap_assert(proc);
664
665 switch (status = apr_proc_fork(proc, p)) {
666
667 case APR_INCHILD:
668 child_process(p, server, d);
669 exit(1);
670 /* never reached */
671 break;
672
673 case APR_INPARENT:
674 apr_pool_note_subprocess(p, proc, APR_KILL_ONLY_ONCE);
675 /* ap_log_error(APLOG_MARK, APLOG_NOTICE, status, server, "Child process %lu", (unsigned long) proc->pid); */
676
677 break;
678
679 default:
680 ap_log_error(APLOG_MARK, APLOG_ERR, status, server, "apr_proc_fork() failed");
681 return HTTP_INTERNAL_SERVER_ERROR;
682 }
683
684 return OK;
685 }
686
post_config(apr_pool_t * pconf,AVAHI_GCC_UNUSED apr_pool_t * plog,AVAHI_GCC_UNUSED apr_pool_t * ptemp,server_rec * s)687 static int post_config(
688 apr_pool_t *pconf,
689 AVAHI_GCC_UNUSED apr_pool_t *plog,
690 AVAHI_GCC_UNUSED apr_pool_t *ptemp,
691 server_rec *s) {
692
693 void *flag;
694 struct global_config_data *d = GET_CONFIG_DATA(s);
695
696 /* All post_config hooks are called twice, we're only interested in the second call. */
697
698 apr_pool_userdata_get(&flag, MOD_DNSSD_USERDATA_KEY, s->process->pool);
699 if (!flag) {
700 apr_pool_userdata_set((void*) 1, MOD_DNSSD_USERDATA_KEY, apr_pool_cleanup_null, s->process->pool);
701 return OK;
702 }
703
704 if (d->enabled)
705 return start_child_process(pconf, s, d);
706
707 return OK;
708 }
709
register_hooks(AVAHI_GCC_UNUSED apr_pool_t * p)710 static void register_hooks(AVAHI_GCC_UNUSED apr_pool_t *p){
711 ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
712 }
713
create_server_config(apr_pool_t * p,AVAHI_GCC_UNUSED server_rec * s)714 static void *create_server_config(apr_pool_t *p, AVAHI_GCC_UNUSED server_rec *s) {
715 struct global_config_data *d;
716
717 d = apr_palloc(p, sizeof(struct global_config_data));
718 ap_assert(d);
719
720 d->enabled = 0;
721 d->user_dir = 1;
722 d->vhost = 1;
723 d->user_dir_path = "public_html";
724
725 return d;
726 }
727
cmd_dnssd_enable(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,int enable)728 static const char *cmd_dnssd_enable(
729 cmd_parms *cmd,
730 AVAHI_GCC_UNUSED void *mconfig,
731 int enable) {
732
733 struct global_config_data *d = GET_CONFIG_DATA(cmd->server);
734 const char *err;
735
736 if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)))
737 return err;
738
739 d->enabled = enable;
740 return NULL;
741 }
742
cmd_dnssd_enable_user_dir(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,int enable)743 static const char *cmd_dnssd_enable_user_dir(
744 cmd_parms *cmd,
745 AVAHI_GCC_UNUSED void *mconfig,
746 int enable) {
747
748 struct global_config_data *d = GET_CONFIG_DATA(cmd->server);
749 const char *err;
750
751 if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)))
752 return err;
753
754 d->user_dir = enable;
755 return NULL;
756 }
757
cmd_dnssd_enable_vhost(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,int enable)758 static const char *cmd_dnssd_enable_vhost(
759 cmd_parms *cmd,
760 AVAHI_GCC_UNUSED void *mconfig,
761 int enable) {
762
763 struct global_config_data *d = GET_CONFIG_DATA(cmd->server);
764 const char *err;
765
766 if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)))
767 return err;
768
769 d->vhost = enable;
770 return NULL;
771 }
772
cmd_dnssd_user_dir_path(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,const char * value)773 static const char *cmd_dnssd_user_dir_path(
774 cmd_parms *cmd,
775 AVAHI_GCC_UNUSED void *mconfig,
776 const char *value) {
777
778 struct global_config_data *d = GET_CONFIG_DATA(cmd->server);
779 const char *err;
780
781 if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)))
782 return err;
783
784 if (value[0] == '~')
785 return "Bad syntax";
786
787 d->user_dir_path = value;
788
789 return NULL;
790 }
791
cmd_dnssd_service_name(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,const char * value)792 static const char *cmd_dnssd_service_name(
793 cmd_parms *cmd,
794 AVAHI_GCC_UNUSED void *mconfig,
795 const char *value) {
796
797 const char *err;
798
799 if ((err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|NOT_IN_LIMIT)))
800 return err;
801
802 if (!avahi_is_valid_service_name(value))
803 return "Invalid service name";
804
805 return NULL;
806 }
807
cmd_dnssd_service_type(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,const char * value)808 static const char *cmd_dnssd_service_type(
809 cmd_parms *cmd,
810 AVAHI_GCC_UNUSED void *mconfig,
811 const char *value) {
812
813 const char *err;
814
815 if ((err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|NOT_IN_LIMIT)))
816 return err;
817
818 if (!avahi_is_valid_service_type_strict(value))
819 return "Invalid service type";
820
821 return NULL;
822 }
823
cmd_dnssd_service_port(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,const char * value)824 static const char *cmd_dnssd_service_port(
825 cmd_parms *cmd,
826 AVAHI_GCC_UNUSED void *mconfig,
827 const char *value) {
828
829 const char *err;
830 int i;
831
832 if ((err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|NOT_IN_LIMIT)))
833 return err;
834
835 i = atoi(value);
836 if (i <= 0 || i > 0xFFFF)
837 return "Invalid port number";
838
839 return NULL;
840 }
841
cmd_dnssd_service_txt_record(cmd_parms * cmd,AVAHI_GCC_UNUSED void * mconfig,AVAHI_GCC_UNUSED const char * value)842 static const char *cmd_dnssd_service_txt_record(
843 cmd_parms *cmd,
844 AVAHI_GCC_UNUSED void *mconfig,
845 AVAHI_GCC_UNUSED const char *value) {
846
847 const char *err;
848
849 if ((err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|NOT_IN_LIMIT)))
850 return err;
851
852 return NULL;
853 }
854
855 static const command_rec commands[] = {
856
857 AP_INIT_FLAG(
858 "DNSSDEnable",
859 cmd_dnssd_enable,
860 NULL,
861 RSRC_CONF,
862 "Enable/disable DNS-SD registration entirely (default: no)"),
863
864 AP_INIT_FLAG(
865 "DNSSDAutoRegisterUserDir",
866 cmd_dnssd_enable_user_dir,
867 NULL,
868 RSRC_CONF,
869 "Enable/disable DNS-SD registration of ~/public_html (default: yes)"),
870
871 AP_INIT_FLAG(
872 "DNSSDAutoRegisterVHosts",
873 cmd_dnssd_enable_vhost,
874 NULL,
875 RSRC_CONF,
876 "Enable/disable DNS-SD registration of all virtual hosts (default: yes)"),
877
878 AP_INIT_TAKE1(
879 "DNSSDUserDir",
880 cmd_dnssd_user_dir_path,
881 NULL,
882 RSRC_CONF,
883 "Set the user directory to use instead of public_html"),
884
885 AP_INIT_TAKE1(
886 "DNSSDServiceName",
887 cmd_dnssd_service_name,
888 NULL,
889 OR_OPTIONS,
890 "Set the DNS-SD service name"),
891
892 AP_INIT_ITERATE(
893 "DNSSDServiceTypes",
894 cmd_dnssd_service_type,
895 NULL,
896 OR_OPTIONS,
897 "Set one or more DNS-SD service types"),
898
899 AP_INIT_ITERATE(
900 "DNSSDServicePort",
901 cmd_dnssd_service_port,
902 NULL,
903 OR_OPTIONS,
904 "Set the IP port this service should be accessed with."),
905
906 AP_INIT_ITERATE(
907 "DNSSDServiceTxtRecord",
908 cmd_dnssd_service_txt_record,
909 NULL,
910 OR_OPTIONS,
911 "Set one or more DNS-SD TXT records"),
912
913 { NULL }
914 };
915
916 module AP_MODULE_DECLARE_DATA dnssd_module = {
917 STANDARD20_MODULE_STUFF,
918 NULL, /* create per-dir config structures */
919 NULL, /* merge per-dir config structures */
920 create_server_config, /* create per-server config structures */
921 NULL, /* merge per-server config structures */
922 commands, /* table of config file commands */
923 register_hooks /* register hooks */
924 };
925
926 /* vim:set expandtab: */
927