• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/avahi-0.6.25/avahi-dnsconfd/
1/* $Id$ */
2
3/***
4  This file is part of avahi.
5
6  avahi is free software; you can redistribute it and/or modify it
7  under the terms of the GNU Lesser General Public License as
8  published by the Free Software Foundation; either version 2.1 of the
9  License, or (at your option) any later version.
10
11  avahi is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14  Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with avahi; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  USA.
20***/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <fcntl.h>
27#include <unistd.h>
28#include <sys/select.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <errno.h>
32#include <string.h>
33#include <stdio.h>
34#include <sys/ioctl.h>
35#include <net/if.h>
36#include <signal.h>
37#include <sys/wait.h>
38#include <getopt.h>
39#include <assert.h>
40#include <inttypes.h>
41#include <stdlib.h>
42
43#include <avahi-common/llist.h>
44#include <avahi-common/malloc.h>
45#include <avahi-common/address.h>
46
47#include <libdaemon/dfork.h>
48#include <libdaemon/dsignal.h>
49#include <libdaemon/dlog.h>
50#include <libdaemon/dpid.h>
51#include <libdaemon/dexec.h>
52
53#define BROWSE_DNS_SERVERS "BROWSE-DNS-SERVERS\n"
54
55#define ENV_INTERFACE_DNS_SERVERS  "AVAHI_INTERFACE_DNS_SERVERS"
56#define ENV_DNS_SERVERS "AVAHI_DNS_SERVERS"
57#define ENV_INTERFACE "AVAHI_INTERFACE"
58
59static enum {
60    ACKWAIT,
61    BROWSING
62} state = ACKWAIT;
63
64static enum {
65    DAEMON_RUN,
66    DAEMON_KILL,
67    DAEMON_REFRESH,
68    DAEMON_VERSION,
69    DAEMON_HELP,
70    DAEMON_CHECK
71} command = DAEMON_RUN;
72
73static int quit = 0;
74static int daemonize = 0;
75
76#if !HAVE_DECL_ENVIRON
77extern char **environ;
78#endif
79
80typedef struct DNSServerInfo DNSServerInfo;
81
82struct DNSServerInfo {
83    AvahiIfIndex interface;
84    AvahiProtocol protocol;
85    char *address;
86    AVAHI_LLIST_FIELDS(DNSServerInfo, servers);
87};
88
89static AVAHI_LLIST_HEAD(DNSServerInfo, servers);
90
91static void server_info_free(DNSServerInfo *i) {
92    assert(i);
93
94    avahi_free(i->address);
95
96    AVAHI_LLIST_REMOVE(DNSServerInfo, servers, servers, i);
97    avahi_free(i);
98}
99
100static DNSServerInfo* get_server_info(AvahiIfIndex interface, AvahiProtocol protocol, const char *address) {
101    DNSServerInfo *i;
102    assert(address);
103
104    for (i = servers; i; i = i->servers_next)
105        if (i->interface == interface &&
106            i->protocol == protocol &&
107            strcmp(i->address, address) == 0)
108            return i;
109
110    return NULL;
111}
112
113static DNSServerInfo* new_server_info(AvahiIfIndex interface, AvahiProtocol protocol, const char *address) {
114    DNSServerInfo *i;
115
116    assert(address);
117
118    i = avahi_new(DNSServerInfo, 1);
119    i->interface = interface;
120    i->protocol = protocol;
121    i->address = avahi_strdup(address);
122
123    AVAHI_LLIST_PREPEND(DNSServerInfo, servers, servers, i);
124
125    return i;
126}
127
128static int set_cloexec(int fd) {
129    int n;
130
131    assert(fd >= 0);
132
133    if ((n = fcntl(fd, F_GETFD)) < 0)
134        return -1;
135
136    if (n & FD_CLOEXEC)
137        return 0;
138
139    return fcntl(fd, F_SETFD, n|FD_CLOEXEC);
140}
141
142static int open_socket(void) {
143    int fd = -1;
144    struct sockaddr_un sa;
145
146    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
147        daemon_log(LOG_ERR, "socket(): %s", strerror(errno));
148        goto fail;
149    }
150
151    if (set_cloexec(fd) < 0) {
152        daemon_log(LOG_ERR, "fcntl(): %s", strerror(errno));
153        goto fail;
154    }
155
156    memset(&sa, 0, sizeof(sa));
157    sa.sun_family = AF_UNIX;
158    strncpy(sa.sun_path, AVAHI_SOCKET, sizeof(sa.sun_path)-1);
159    sa.sun_path[sizeof(sa.sun_path)-1] = 0;
160
161    if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
162        daemon_log(LOG_ERR, "connect(): %s", strerror(errno));
163	daemon_log(LOG_INFO, "Failed to connect to the daemon. This probably means that you");
164	daemon_log(LOG_INFO, "didn't start avahi-daemon before avahi-dnsconfd.");
165        goto fail;
166    }
167
168    return fd;
169
170fail:
171    if (fd >= 0)
172        close(fd);
173
174    return -1;
175}
176
177static ssize_t loop_write(int fd, const void*data, size_t size) {
178
179    ssize_t ret = 0;
180    assert(fd >= 0 && data && size);
181
182    while (size > 0) {
183        ssize_t r;
184
185        if ((r = write(fd, data, size)) < 0)
186            return r;
187
188        if (r == 0)
189            break;
190
191        ret += r;
192        data = (const uint8_t*) data + r;
193        size -= r;
194    }
195
196    return ret;
197}
198
199static char *concat_dns_servers(AvahiIfIndex interface) {
200    DNSServerInfo *i;
201    char *r = NULL;
202
203    for (i = servers; i; i = i->servers_next)
204        if (i->interface == interface || interface <= 0) {
205            DNSServerInfo *j;
206            char *t;
207
208            /* Filter out double entries */
209            for (j = servers; j != i; j = j->servers_next)
210                if (j->interface == interface || interface <= 0)
211                    if (strcmp(i->address, j->address) == 0)
212                        break;
213
214            if (j != i)
215                continue;
216
217            if (!r)
218                t = avahi_strdup(i->address);
219            else
220                t = avahi_strdup_printf("%s %s", r, i->address);
221
222            avahi_free(r);
223            r = t;
224        }
225
226    return r;
227}
228
229static void set_env(const char *name, const char *value) {
230    char **e;
231    size_t l;
232
233    assert(name);
234    assert(value);
235
236    l = strlen(name);
237
238    for (e = environ; *e; e++) {
239        /* Search for the variable */
240        if (strlen(*e) < l+1)
241            continue;
242
243        if (strncmp(*e, name, l) != 0 || (*e)[l] != '=')
244            continue;
245
246        /* We simply free the record, sicne we know that we created it previously */
247        avahi_free(*e);
248        *e = avahi_strdup_printf("%s=%s", name, value);
249        return;
250    }
251
252    assert(0);
253}
254
255static void run_script(int new, AvahiIfIndex interface, AvahiProtocol protocol, const char *address) {
256    char *p;
257    int ret;
258    char ia[16], pa[16];
259    char name[IF_NAMESIZE];
260
261    assert(interface > 0);
262
263    if (!if_indextoname(interface, name))
264        return;
265
266    p = concat_dns_servers(interface);
267    set_env(ENV_INTERFACE_DNS_SERVERS, p ? p : "");
268    avahi_free(p);
269
270    p = concat_dns_servers(-1);
271    set_env(ENV_DNS_SERVERS, p ? p : "");
272    avahi_free(p);
273
274    set_env(ENV_INTERFACE, name);
275
276    snprintf(ia, sizeof(ia), "%i", (int) interface);
277    snprintf(pa, sizeof(pa), "%i", (int) protocol);
278
279    if (daemon_exec("/", &ret, AVAHI_DNSCONF_SCRIPT, AVAHI_DNSCONF_SCRIPT, new ? "+" : "-", address, ia, pa, avahi_proto_to_string(protocol), NULL) < 0)
280        daemon_log(LOG_WARNING, "Failed to run script");
281    else if (ret != 0)
282        daemon_log(LOG_WARNING, "Script returned with non-zero exit code %i", ret);
283}
284
285static int new_line(const char *l) {
286    assert(l);
287
288    if (state == ACKWAIT) {
289        if (*l != '+') {
290            daemon_log(LOG_ERR, "Avahi command failed: %s", l);
291            return -1;
292        }
293
294        daemon_log(LOG_INFO, "Successfully connected to Avahi daemon.");
295        state = BROWSING;
296    } else {
297        AvahiIfIndex interface;
298        AvahiProtocol protocol;
299        int i_interface, i_protocol, port;
300        char a[AVAHI_ADDRESS_STR_MAX];
301
302        assert(state == BROWSING);
303
304        if (*l != '<' && *l != '>') {
305            daemon_log(LOG_ERR, "Avahi sent us an invalid browsing line: %s", l);
306            return -1;
307        }
308
309        if (sscanf(l+1, "%i %i %39s %i", &i_interface, &i_protocol, a, &port) != 4) {
310            daemon_log(LOG_ERR, "Failed to parse browsing line: %s", l);
311            return -1;
312        }
313
314        interface = (AvahiIfIndex) i_interface;
315        protocol = (AvahiProtocol) i_protocol;
316
317        if (*l == '>') {
318            if (port != 53)
319                daemon_log(LOG_WARNING, "DNS server with port address != 53 found, ignoring");
320            else {
321                daemon_log(LOG_INFO, "New DNS Server %s (interface: %i.%s)", a, interface, avahi_proto_to_string(protocol));
322                new_server_info(interface, protocol, a);
323                run_script(1, interface, protocol, a);
324            }
325        } else {
326            DNSServerInfo *i;
327
328            if (port == 53)
329                if ((i = get_server_info(interface, protocol, a))) {
330                    daemon_log(LOG_INFO, "DNS Server %s removed (interface: %i.%s)", a, interface, avahi_proto_to_string(protocol));
331                    server_info_free(i);
332                    run_script(0, interface, protocol, a);
333                }
334        }
335
336    }
337
338    return 0;
339}
340
341static int do_connect(void) {
342    int fd = -1;
343
344    if ((fd = open_socket()) < 0)
345        goto fail;
346
347    if (loop_write(fd, BROWSE_DNS_SERVERS, sizeof(BROWSE_DNS_SERVERS)-1) < 0) {
348        daemon_log(LOG_ERR, "write(): %s", strerror(errno));
349        goto fail;
350    }
351
352    state = ACKWAIT;
353    return fd;
354
355fail:
356    if (fd >= 0)
357        close(fd);
358
359    return -1;
360}
361
362static void free_dns_server_info_list(void) {
363    while (servers) {
364        AvahiIfIndex interface = servers->interface;
365        AvahiProtocol protocol = servers->protocol;
366        char *address = avahi_strdup(servers->address);
367        server_info_free(servers);
368
369        run_script(0, interface, protocol, address);
370        avahi_free(address);
371    }
372}
373
374static void help(FILE *f, const char *argv0) {
375    fprintf(f,
376            "%s [options]\n"
377            "    -h --help        Show this help\n"
378            "    -D --daemonize   Daemonize after startup\n"
379            "    -k --kill        Kill a running daemon\n"
380            "    -r --refresh     Request a running daemon to refresh DNS server data\n"
381            "    -c --check       Return 0 if a daemon is already running\n"
382            "    -V --version     Show version\n",
383            argv0);
384}
385
386static int parse_command_line(int argc, char *argv[]) {
387    int c;
388
389    static const struct option long_options[] = {
390        { "help",      no_argument,       NULL, 'h' },
391        { "daemonize", no_argument,       NULL, 'D' },
392        { "kill",      no_argument,       NULL, 'k' },
393        { "version",   no_argument,       NULL, 'V' },
394        { "refresh",   no_argument,       NULL, 'r' },
395        { "check",     no_argument,       NULL, 'c' },
396        { NULL, 0, NULL, 0 }
397    };
398
399    while ((c = getopt_long(argc, argv, "hDkVrc", long_options, NULL)) >= 0) {
400
401        switch(c) {
402            case 'h':
403                command = DAEMON_HELP;
404                break;
405            case 'D':
406                daemonize = 1;
407                break;
408            case 'k':
409                command = DAEMON_KILL;
410                break;
411            case 'V':
412                command = DAEMON_VERSION;
413                break;
414            case 'r':
415                command = DAEMON_REFRESH;
416                break;
417            case 'c':
418                command = DAEMON_CHECK;
419                break;
420            default:
421                return -1;
422        }
423    }
424
425    if (optind < argc) {
426        fprintf(stderr, "Too many arguments\n");
427        return -1;
428    }
429
430    return 0;
431}
432
433static int run_daemon(void) {
434    int fd = -1, ret = -1;
435    char buf[1024];
436    size_t buflen = 0;
437
438    AVAHI_LLIST_HEAD_INIT(DNSServerInfo, servers);
439
440    daemon_signal_init(SIGINT, SIGTERM, SIGCHLD, SIGHUP, 0);
441
442    /* Allocate some memory for our environment variables */
443    putenv(avahi_strdup(ENV_INTERFACE"="));
444    putenv(avahi_strdup(ENV_DNS_SERVERS"="));
445    putenv(avahi_strdup(ENV_INTERFACE_DNS_SERVERS"="));
446
447    if ((fd = do_connect()) < 0)
448        goto finish;
449
450    if (daemonize)
451        daemon_retval_send(0);
452
453    ret = 0;
454
455    while (!quit) {
456        fd_set rfds, wfds;
457
458        FD_ZERO(&rfds);
459        FD_ZERO(&wfds);
460
461        FD_SET(fd, &rfds);
462        FD_SET(daemon_signal_fd(), &rfds);
463
464        for (;;) {
465            if (select(fd+1, &rfds, NULL, NULL, NULL) < 0) {
466                if (errno == EINTR)
467                    continue;
468
469                daemon_log(LOG_ERR, "select(): %s", strerror(errno));
470                goto finish;
471            }
472
473            break;
474        }
475
476        if (FD_ISSET(daemon_signal_fd(), &rfds)) {
477
478            int sig;
479
480            if ((sig = daemon_signal_next()) <= 0) {
481                daemon_log(LOG_ERR, "daemon_signal_next() failed");
482                goto finish;
483            }
484
485            switch(sig) {
486                case SIGINT:
487                case SIGTERM:
488                    daemon_log(LOG_INFO, "Got %s, quitting.", sig == SIGINT ? "SIGINT" : "SIGTERM");
489                    ret = 0;
490                    goto finish;
491
492                case SIGCHLD:
493                    waitpid(-1, NULL, WNOHANG);
494                    break;
495
496                case SIGHUP:
497                    daemon_log(LOG_INFO, "Refreshing DNS Server list");
498
499                    close(fd);
500                    free_dns_server_info_list();
501
502                    if ((fd = do_connect()) < 0)
503                        goto finish;
504
505                    break;
506            }
507
508        } else if (FD_ISSET(fd, &rfds)) {
509            ssize_t r;
510            char *n;
511
512            if ((r = read(fd, buf, sizeof(buf) - buflen - 1)) <= 0) {
513                daemon_log(LOG_ERR, "read(): %s", r < 0 ? strerror(errno) : "EOF");
514                goto finish;
515            }
516
517            buflen += r;
518            assert(buflen <= sizeof(buf)-1);
519
520            while ((n = memchr(buf, '\n', buflen))) {
521                *(n++) = 0;
522
523                if (new_line(buf) < 0)
524                    goto finish;
525
526                buflen -= (n - buf);
527                memmove(buf, n, buflen);
528            }
529
530            if (buflen >= sizeof(buf)-1) {
531                /* The incoming line is horribly long */
532                buf[sizeof(buf)-1] = 0;
533
534                if (new_line(buf) < 0)
535                    goto finish;
536
537                buflen = 0;
538            }
539        }
540    }
541
542finish:
543
544    free_dns_server_info_list();
545
546    if (fd >= 0)
547        close(fd);
548
549    daemon_signal_done();
550
551    if (ret != 0 && daemonize)
552        daemon_retval_send(1);
553
554    return ret;
555}
556
557static const char* pid_file_proc(void) {
558    return AVAHI_RUNTIME_DIR"/avahi-dnsconfd.pid";
559}
560
561int main(int argc, char *argv[]) {
562    char *argv0;
563    int r = 1;
564    int wrote_pid_file = 0;
565
566    if ((argv0 = strrchr(argv[0], '/')))
567        argv0++;
568    else
569        argv0 = argv[0];
570
571    daemon_pid_file_ident = daemon_log_ident = argv0;
572    daemon_pid_file_proc = pid_file_proc;
573
574    if (parse_command_line(argc, argv) < 0)
575        goto finish;
576
577    if (command == DAEMON_RUN) {
578        pid_t pid;
579
580        if (getuid() != 0) {
581            daemon_log(LOG_ERR, "This program is intended to be run as root.");
582            goto finish;
583        }
584
585        if ((pid = daemon_pid_file_is_running()) >= 0) {
586            daemon_log(LOG_ERR, "Daemon already running on PID %u", pid);
587            goto finish;
588        }
589
590        if (daemonize) {
591            daemon_retval_init();
592
593            if ((pid = daemon_fork()) < 0)
594                goto finish;
595            else if (pid != 0) {
596                int ret;
597                /** Parent **/
598
599                if ((ret = daemon_retval_wait(20)) < 0) {
600                    daemon_log(LOG_ERR, "Could not receive return value from daemon process.");
601                    goto finish;
602                }
603
604                r = ret;
605                goto finish;
606            }
607
608            /* Child */
609        }
610
611        chdir("/");
612
613        if (daemon_pid_file_create() < 0) {
614            daemon_log(LOG_ERR, "Failed to create PID file: %s", strerror(errno));
615
616            if (daemonize)
617                daemon_retval_send(1);
618            goto finish;
619        } else
620            wrote_pid_file = 1;
621
622        if (run_daemon() < 0)
623            goto finish;
624
625        r = 0;
626    } else if (command == DAEMON_HELP) {
627        help(stdout, argv0);
628
629        r = 0;
630    } else if (command == DAEMON_VERSION) {
631        printf("%s "PACKAGE_VERSION"\n", argv0);
632
633        r = 0;
634    } else if (command == DAEMON_KILL) {
635        if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
636            daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
637            goto finish;
638        }
639
640        r = 0;
641    } else if (command == DAEMON_REFRESH) {
642        if (daemon_pid_file_kill(SIGHUP) < 0) {
643            daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
644            goto finish;
645        }
646
647        r = 0;
648    } else if (command == DAEMON_CHECK)
649        r = (daemon_pid_file_is_running() >= 0) ? 0 : 1;
650
651
652finish:
653
654    if (daemonize)
655        daemon_retval_done();
656
657    if (wrote_pid_file)
658        daemon_pid_file_remove();
659
660    return r;
661}
662