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