• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/avahi-0.6.31/avahi-daemon/
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 <assert.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/socket.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <sys/un.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <sys/stat.h>
34
35#include <avahi-common/llist.h>
36#include <avahi-common/malloc.h>
37#include <avahi-common/error.h>
38
39#include <avahi-core/log.h>
40#include <avahi-core/lookup.h>
41#include <avahi-core/dns-srv-rr.h>
42
43#include "simple-protocol.h"
44#include "main.h"
45#include "sd-daemon.h"
46
47#ifdef ENABLE_CHROOT
48#include "chroot.h"
49#endif
50
51#ifndef AF_LOCAL
52#define AF_LOCAL AF_UNIX
53#endif
54#ifndef PF_LOCAL
55#define PF_LOCAL PF_UNIX
56#endif
57
58#define BUFFER_SIZE (20*1024)
59
60#define CLIENTS_MAX 50
61
62typedef struct Client Client;
63typedef struct Server Server;
64
65typedef enum {
66    CLIENT_IDLE,
67    CLIENT_RESOLVE_HOSTNAME,
68    CLIENT_RESOLVE_ADDRESS,
69    CLIENT_BROWSE_DNS_SERVERS,
70    CLIENT_DEAD
71} ClientState;
72
73struct Client {
74    Server *server;
75
76    ClientState state;
77
78    int fd;
79    AvahiWatch *watch;
80
81    char inbuf[BUFFER_SIZE], outbuf[BUFFER_SIZE];
82    size_t inbuf_length, outbuf_length;
83
84    AvahiSHostNameResolver *host_name_resolver;
85    AvahiSAddressResolver *address_resolver;
86    AvahiSDNSServerBrowser *dns_server_browser;
87
88    AvahiProtocol afquery;
89
90    AVAHI_LLIST_FIELDS(Client, clients);
91};
92
93struct Server {
94    const AvahiPoll *poll_api;
95    int fd;
96    AvahiWatch *watch;
97    AVAHI_LLIST_HEAD(Client, clients);
98
99    unsigned n_clients;
100    int remove_socket;
101};
102
103static Server *server = NULL;
104
105static void client_work(AvahiWatch *watch, int fd, AvahiWatchEvent events, void *userdata);
106
107static void client_free(Client *c) {
108    assert(c);
109
110    assert(c->server->n_clients >= 1);
111    c->server->n_clients--;
112
113    if (c->host_name_resolver)
114        avahi_s_host_name_resolver_free(c->host_name_resolver);
115
116    if (c->address_resolver)
117        avahi_s_address_resolver_free(c->address_resolver);
118
119    if (c->dns_server_browser)
120        avahi_s_dns_server_browser_free(c->dns_server_browser);
121
122    c->server->poll_api->watch_free(c->watch);
123    close(c->fd);
124
125    AVAHI_LLIST_REMOVE(Client, clients, c->server->clients, c);
126    avahi_free(c);
127}
128
129static void client_new(Server *s, int fd) {
130    Client *c;
131
132    assert(fd >= 0);
133
134    c = avahi_new(Client, 1);
135    c->server = s;
136    c->fd = fd;
137    c->state = CLIENT_IDLE;
138
139    c->inbuf_length = c->outbuf_length = 0;
140
141    c->host_name_resolver = NULL;
142    c->address_resolver = NULL;
143    c->dns_server_browser = NULL;
144
145    c->watch = s->poll_api->watch_new(s->poll_api, fd, AVAHI_WATCH_IN, client_work, c);
146
147    AVAHI_LLIST_PREPEND(Client, clients, s->clients, c);
148    s->n_clients++;
149}
150
151static void client_output(Client *c, const uint8_t*data, size_t size) {
152    size_t k, m;
153
154    assert(c);
155    assert(data);
156
157    if (!size)
158        return;
159
160    k = sizeof(c->outbuf) - c->outbuf_length;
161    m = size > k ? k : size;
162
163    memcpy(c->outbuf + c->outbuf_length, data, m);
164    c->outbuf_length += m;
165
166    server->poll_api->watch_update(c->watch, AVAHI_WATCH_OUT);
167}
168
169static void client_output_printf(Client *c, const char *format, ...) {
170    char *t;
171    va_list ap;
172
173    va_start(ap, format);
174    t = avahi_strdup_vprintf(format, ap);
175    va_end(ap);
176
177    client_output(c, (uint8_t*) t, strlen(t));
178    avahi_free(t);
179}
180
181static void host_name_resolver_callback(
182    AVAHI_GCC_UNUSED AvahiSHostNameResolver *r,
183    AvahiIfIndex iface,
184    AvahiProtocol protocol,
185    AvahiResolverEvent event,
186    const char *hostname,
187    const AvahiAddress *a,
188    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
189    void* userdata) {
190
191    Client *c = userdata;
192
193    assert(c);
194
195    if (event == AVAHI_RESOLVER_FAILURE)
196        client_output_printf(c, "%+i %s\n", avahi_server_errno(avahi_server), avahi_strerror(avahi_server_errno(avahi_server)));
197    else if (event == AVAHI_RESOLVER_FOUND) {
198        char t[AVAHI_ADDRESS_STR_MAX];
199        avahi_address_snprint(t, sizeof(t), a);
200        client_output_printf(c, "+ %i %u %s %s\n", iface, protocol, hostname, t);
201    }
202
203    c->state = CLIENT_DEAD;
204}
205
206static void address_resolver_callback(
207    AVAHI_GCC_UNUSED AvahiSAddressResolver *r,
208    AvahiIfIndex iface,
209    AvahiProtocol protocol,
210    AvahiResolverEvent event,
211    AVAHI_GCC_UNUSED const AvahiAddress *a,
212    const char *hostname,
213    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
214    void* userdata) {
215
216    Client *c = userdata;
217
218    assert(c);
219
220    if (event == AVAHI_RESOLVER_FAILURE)
221        client_output_printf(c, "%+i %s\n", avahi_server_errno(avahi_server), avahi_strerror(avahi_server_errno(avahi_server)));
222    else if (event == AVAHI_RESOLVER_FOUND)
223        client_output_printf(c, "+ %i %u %s\n", iface, protocol, hostname);
224
225    c->state = CLIENT_DEAD;
226}
227
228static void dns_server_browser_callback(
229    AVAHI_GCC_UNUSED AvahiSDNSServerBrowser *b,
230    AvahiIfIndex interface,
231    AvahiProtocol protocol,
232    AvahiBrowserEvent event,
233    AVAHI_GCC_UNUSED const char *host_name,
234    const AvahiAddress *a,
235    uint16_t port,
236    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
237    void* userdata) {
238
239    Client *c = userdata;
240    char t[AVAHI_ADDRESS_STR_MAX];
241
242    assert(c);
243
244    if (!a)
245        return;
246
247    switch (event) {
248        case AVAHI_BROWSER_FAILURE:
249            client_output_printf(c, "%+i %s\n", avahi_server_errno(avahi_server), avahi_strerror(avahi_server_errno(avahi_server)));
250            c->state = CLIENT_DEAD;
251            break;
252
253        case AVAHI_BROWSER_ALL_FOR_NOW:
254        case AVAHI_BROWSER_CACHE_EXHAUSTED:
255            break;
256
257        case AVAHI_BROWSER_NEW:
258        case AVAHI_BROWSER_REMOVE:
259
260            avahi_address_snprint(t, sizeof(t), a);
261            client_output_printf(c, "%c %i %u %s %u\n", event == AVAHI_BROWSER_NEW ? '>' : '<',  interface, protocol, t, port);
262            break;
263    }
264}
265
266static void handle_line(Client *c, const char *s) {
267    char cmd[64], arg[64];
268    int n_args;
269
270    assert(c);
271    assert(s);
272
273    if (c->state != CLIENT_IDLE)
274        return;
275
276    if ((n_args = sscanf(s, "%63s %63s", cmd, arg)) < 1 ) {
277        client_output_printf(c, "%+i Failed to parse command, try \"HELP\".\n", AVAHI_ERR_INVALID_OPERATION);
278        c->state = CLIENT_DEAD;
279        return;
280    }
281
282    if (strcmp(cmd, "HELP") == 0) {
283        client_output_printf(c,
284                             "+ Available commands are:\n"
285                             "+      RESOLVE-HOSTNAME <hostname>\n"
286                             "+      RESOLVE-HOSTNAME-IPV6 <hostname>\n"
287                             "+      RESOLVE-HOSTNAME-IPV4 <hostname>\n"
288                             "+      RESOLVE-ADDRESS <address>\n"
289                             "+      BROWSE-DNS-SERVERS\n"
290                             "+      BROWSE-DNS-SERVERS-IPV4\n"
291                             "+      BROWSE-DNS-SERVERS-IPV6\n");
292        c->state = CLIENT_DEAD; }
293    else if (strcmp(cmd, "FUCK") == 0 && n_args == 1) {
294        client_output_printf(c, "+ FUCK: Go fuck yourself!\n");
295        c->state = CLIENT_DEAD;
296    } else if (strcmp(cmd, "RESOLVE-HOSTNAME-IPV4") == 0 && n_args == 2) {
297        c->state = CLIENT_RESOLVE_HOSTNAME;
298        if (!(c->host_name_resolver = avahi_s_host_name_resolver_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, arg, c->afquery = AVAHI_PROTO_INET, AVAHI_LOOKUP_USE_MULTICAST, host_name_resolver_callback, c)))
299            goto fail;
300
301        avahi_log_debug(__FILE__": Got %s request for '%s'.", cmd, arg);
302    } else if (strcmp(cmd, "RESOLVE-HOSTNAME-IPV6") == 0 && n_args == 2) {
303        c->state = CLIENT_RESOLVE_HOSTNAME;
304        if (!(c->host_name_resolver = avahi_s_host_name_resolver_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, arg, c->afquery = AVAHI_PROTO_INET6, AVAHI_LOOKUP_USE_MULTICAST, host_name_resolver_callback, c)))
305            goto fail;
306
307        avahi_log_debug(__FILE__": Got %s request for '%s'.", cmd, arg);
308    } else if (strcmp(cmd, "RESOLVE-HOSTNAME") == 0 && n_args == 2) {
309        c->state = CLIENT_RESOLVE_HOSTNAME;
310        if (!(c->host_name_resolver = avahi_s_host_name_resolver_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, arg, c->afquery = AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_MULTICAST, host_name_resolver_callback, c)))
311            goto fail;
312
313        avahi_log_debug(__FILE__": Got %s request for '%s'.", cmd, arg);
314    } else if (strcmp(cmd, "RESOLVE-ADDRESS") == 0 && n_args == 2) {
315        AvahiAddress addr;
316
317        if (!(avahi_address_parse(arg, AVAHI_PROTO_UNSPEC, &addr))) {
318            client_output_printf(c, "%+i Failed to parse address \"%s\".\n", AVAHI_ERR_INVALID_ADDRESS, arg);
319            c->state = CLIENT_DEAD;
320        } else {
321            c->state = CLIENT_RESOLVE_ADDRESS;
322            if (!(c->address_resolver = avahi_s_address_resolver_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, &addr, AVAHI_LOOKUP_USE_MULTICAST, address_resolver_callback, c)))
323                goto fail;
324        }
325
326        avahi_log_debug(__FILE__": Got %s request for '%s'.", cmd, arg);
327
328    } else if (strcmp(cmd, "BROWSE-DNS-SERVERS-IPV4") == 0 && n_args == 1) {
329        c->state = CLIENT_BROWSE_DNS_SERVERS;
330        if (!(c->dns_server_browser = avahi_s_dns_server_browser_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery = AVAHI_PROTO_INET, AVAHI_LOOKUP_USE_MULTICAST, dns_server_browser_callback, c)))
331            goto fail;
332        client_output_printf(c, "+ Browsing ...\n");
333
334        avahi_log_debug(__FILE__": Got %s request.", cmd);
335
336    } else if (strcmp(cmd, "BROWSE-DNS-SERVERS-IPV6") == 0 && n_args == 1) {
337        c->state = CLIENT_BROWSE_DNS_SERVERS;
338        if (!(c->dns_server_browser = avahi_s_dns_server_browser_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery = AVAHI_PROTO_INET6, AVAHI_LOOKUP_USE_MULTICAST, dns_server_browser_callback, c)))
339            goto fail;
340        client_output_printf(c, "+ Browsing ...\n");
341
342        avahi_log_debug(__FILE__": Got %s request.", cmd);
343
344    } else if (strcmp(cmd, "BROWSE-DNS-SERVERS") == 0 && n_args == 1) {
345        c->state = CLIENT_BROWSE_DNS_SERVERS;
346        if (!(c->dns_server_browser = avahi_s_dns_server_browser_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery = AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_MULTICAST, dns_server_browser_callback, c)))
347            goto fail;
348        client_output_printf(c, "+ Browsing ...\n");
349
350        avahi_log_debug(__FILE__": Got %s request.", cmd);
351
352    } else {
353        client_output_printf(c, "%+i Invalid command \"%s\", try \"HELP\".\n", AVAHI_ERR_INVALID_OPERATION, cmd);
354        c->state = CLIENT_DEAD;
355
356        avahi_log_debug(__FILE__": Got invalid request '%s'.", cmd);
357    }
358
359    return;
360
361fail:
362    client_output_printf(c, "%+i %s\n", avahi_server_errno(avahi_server), avahi_strerror(avahi_server_errno(avahi_server)));
363    c->state = CLIENT_DEAD;
364}
365
366static void handle_input(Client *c) {
367    assert(c);
368
369    for (;;) {
370        char *e;
371        size_t k;
372
373        if (!(e = memchr(c->inbuf, '\n', c->inbuf_length)))
374            break;
375
376        k = e - (char*) c->inbuf;
377        *e = 0;
378
379        handle_line(c, c->inbuf);
380        c->inbuf_length -= k + 1;
381        memmove(c->inbuf, e+1, c->inbuf_length);
382    }
383}
384
385static void client_work(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AvahiWatchEvent events, void *userdata) {
386    Client *c = userdata;
387
388    assert(c);
389
390    if ((events & AVAHI_WATCH_IN) && c->inbuf_length < sizeof(c->inbuf)) {
391        ssize_t r;
392
393        if ((r = read(c->fd, c->inbuf + c->inbuf_length, sizeof(c->inbuf) - c->inbuf_length)) <= 0) {
394            if (r < 0)
395                avahi_log_warn("read(): %s", strerror(errno));
396            client_free(c);
397            return;
398        }
399
400        c->inbuf_length += r;
401        assert(c->inbuf_length <= sizeof(c->inbuf));
402
403        handle_input(c);
404    }
405
406    if ((events & AVAHI_WATCH_OUT) && c->outbuf_length > 0) {
407        ssize_t r;
408
409        if ((r = write(c->fd, c->outbuf, c->outbuf_length)) < 0) {
410            avahi_log_warn("write(): %s", strerror(errno));
411            client_free(c);
412            return;
413        }
414
415        assert((size_t) r <= c->outbuf_length);
416        c->outbuf_length -= r;
417
418        if (c->outbuf_length)
419            memmove(c->outbuf, c->outbuf + r, c->outbuf_length - r);
420
421        if (c->outbuf_length == 0 && c->state == CLIENT_DEAD) {
422            client_free(c);
423            return;
424        }
425    }
426
427    c->server->poll_api->watch_update(
428        watch,
429        (c->outbuf_length > 0 ? AVAHI_WATCH_OUT : 0) |
430        (c->inbuf_length < sizeof(c->inbuf) ? AVAHI_WATCH_IN : 0));
431}
432
433static void server_work(AVAHI_GCC_UNUSED AvahiWatch *watch, int fd, AvahiWatchEvent events, void *userdata) {
434    Server *s = userdata;
435
436    assert(s);
437
438    if (events & AVAHI_WATCH_IN) {
439        int cfd;
440
441        if ((cfd = accept(fd, NULL, NULL)) < 0)
442            avahi_log_error("accept(): %s", strerror(errno));
443        else
444            client_new(s, cfd);
445    }
446}
447
448int simple_protocol_setup(const AvahiPoll *poll_api) {
449    struct sockaddr_un sa;
450    mode_t u;
451    int n;
452
453    assert(!server);
454
455    server = avahi_new(Server, 1);
456    server->poll_api = poll_api;
457    server->remove_socket = 0;
458    server->fd = -1;
459    server->n_clients = 0;
460    AVAHI_LLIST_HEAD_INIT(Client, server->clients);
461    server->watch = NULL;
462
463    u = umask(0000);
464
465    if ((n = sd_listen_fds(1)) < 0) {
466        avahi_log_warn("Failed to acquire systemd file descriptors: %s", strerror(-n));
467        goto fail;
468    }
469
470    if (n > 1) {
471        avahi_log_warn("Too many systemd file descriptors passed.");
472        goto fail;
473    }
474
475    if (n == 1) {
476        int r;
477
478        if ((r = sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_STREAM, 1)) < 0) {
479            avahi_log_warn("Passed systemd file descriptor is of wrong type: %s", strerror(-r));
480            goto fail;
481        }
482
483        server->fd = SD_LISTEN_FDS_START;
484
485    } else {
486
487        if ((server->fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
488            avahi_log_warn("socket(AF_LOCAL, SOCK_STREAM, 0): %s", strerror(errno));
489            goto fail;
490        }
491
492        memset(&sa, 0, sizeof(sa));
493        sa.sun_family = AF_LOCAL;
494        strncpy(sa.sun_path, AVAHI_SOCKET, sizeof(sa.sun_path)-1);
495
496        /* We simply remove existing UNIX sockets under this name. The
497           Avahi daemon makes sure that it runs only once on a host,
498           therefore sockets that already exist are stale and may be
499           removed without any ill effects */
500
501        unlink(AVAHI_SOCKET);
502
503        if (bind(server->fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
504            avahi_log_warn("bind(): %s", strerror(errno));
505            goto fail;
506        }
507
508        server->remove_socket = 1;
509
510        if (listen(server->fd, SOMAXCONN) < 0) {
511            avahi_log_warn("listen(): %s", strerror(errno));
512            goto fail;
513        }
514    }
515
516    umask(u);
517
518    server->watch = poll_api->watch_new(poll_api, server->fd, AVAHI_WATCH_IN, server_work, server);
519
520    return 0;
521
522fail:
523
524    umask(u);
525    simple_protocol_shutdown();
526
527    return -1;
528}
529
530void simple_protocol_shutdown(void) {
531
532    if (server) {
533
534        if (server->remove_socket)
535#ifdef ENABLE_CHROOT
536            avahi_chroot_helper_unlink(AVAHI_SOCKET);
537#else
538            unlink(AVAHI_SOCKET);
539#endif
540
541        while (server->clients)
542            client_free(server->clients);
543
544        if (server->watch)
545            server->poll_api->watch_free(server->watch);
546
547        if (server->fd >= 0)
548            close(server->fd);
549
550        avahi_free(server);
551
552        server = NULL;
553    }
554}
555
556void simple_protocol_restart_queries(void) {
557    Client *c;
558
559    /* Restart queries in case of local domain name changes */
560
561    assert(server);
562
563    for (c = server->clients; c; c = c->clients_next)
564        if (c->state == CLIENT_BROWSE_DNS_SERVERS && c->dns_server_browser) {
565            avahi_s_dns_server_browser_free(c->dns_server_browser);
566            c->dns_server_browser = avahi_s_dns_server_browser_new(avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, c->afquery, AVAHI_LOOKUP_USE_MULTICAST, dns_server_browser_callback, c);
567        }
568}
569