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