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 <stdlib.h>
26#include <sys/stat.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <errno.h>
30#include <string.h>
31
32#include <avahi-common/error.h>
33#include <avahi-common/dbus.h>
34#include "avahi-common/avahi-malloc.h"
35#include <avahi-core/log.h>
36#include <avahi-core/core.h>
37
38#ifdef ENABLE_CHROOT
39#include "chroot.h"
40#endif
41
42#include "main.h"
43#include "dbus-util.h"
44
45DBusHandlerResult avahi_dbus_respond_error(DBusConnection *c, DBusMessage *m, int error, const char *text) {
46    DBusMessage *reply;
47
48    assert(-error > -AVAHI_OK);
49    assert(-error < -AVAHI_ERR_MAX);
50
51    if (!text)
52        text = avahi_strerror(error);
53
54    reply = dbus_message_new_error(m, avahi_error_number_to_dbus(error), text);
55
56    if (!reply) {
57        avahi_log_error("Failed allocate message");
58        return DBUS_HANDLER_RESULT_NEED_MEMORY;
59    }
60
61    dbus_connection_send(c, reply, NULL);
62    dbus_message_unref(reply);
63
64    avahi_log_debug(__FILE__": Responding error '%s' (%i)", text, error);
65
66    return DBUS_HANDLER_RESULT_HANDLED;
67}
68
69DBusHandlerResult avahi_dbus_respond_string(DBusConnection *c, DBusMessage *m, const char *text) {
70    DBusMessage *reply;
71
72    reply = dbus_message_new_method_return(m);
73
74    if (!reply) {
75        avahi_log_error("Failed allocate message");
76        return DBUS_HANDLER_RESULT_NEED_MEMORY;
77    }
78
79    dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
80    dbus_connection_send(c, reply, NULL);
81    dbus_message_unref(reply);
82
83    return DBUS_HANDLER_RESULT_HANDLED;
84}
85
86DBusHandlerResult avahi_dbus_respond_int32(DBusConnection *c, DBusMessage *m, int32_t i) {
87    DBusMessage *reply;
88
89    reply = dbus_message_new_method_return(m);
90
91    if (!reply) {
92        avahi_log_error("Failed allocate message");
93        return DBUS_HANDLER_RESULT_NEED_MEMORY;
94    }
95
96    dbus_message_append_args(reply, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID);
97    dbus_connection_send(c, reply, NULL);
98    dbus_message_unref(reply);
99
100    return DBUS_HANDLER_RESULT_HANDLED;
101}
102
103DBusHandlerResult avahi_dbus_respond_uint32(DBusConnection *c, DBusMessage *m, uint32_t u) {
104    DBusMessage *reply;
105
106    reply = dbus_message_new_method_return(m);
107
108    if (!reply) {
109        avahi_log_error("Failed allocate message");
110        return DBUS_HANDLER_RESULT_NEED_MEMORY;
111    }
112
113    dbus_message_append_args(reply, DBUS_TYPE_UINT32, &u, DBUS_TYPE_INVALID);
114    dbus_connection_send(c, reply, NULL);
115    dbus_message_unref(reply);
116
117    return DBUS_HANDLER_RESULT_HANDLED;
118}
119
120DBusHandlerResult avahi_dbus_respond_boolean(DBusConnection *c, DBusMessage *m, int b) {
121    DBusMessage *reply;
122
123    reply = dbus_message_new_method_return(m);
124
125    if (!reply) {
126        avahi_log_error("Failed allocate message");
127        return DBUS_HANDLER_RESULT_NEED_MEMORY;
128    }
129
130    dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
131    dbus_connection_send(c, reply, NULL);
132    dbus_message_unref(reply);
133
134    return DBUS_HANDLER_RESULT_HANDLED;
135}
136
137DBusHandlerResult avahi_dbus_respond_ok(DBusConnection *c, DBusMessage *m) {
138    DBusMessage *reply;
139
140    reply = dbus_message_new_method_return(m);
141
142    if (!reply) {
143        avahi_log_error("Failed allocate message");
144        return DBUS_HANDLER_RESULT_NEED_MEMORY;
145    }
146
147    dbus_connection_send(c, reply, NULL);
148    dbus_message_unref(reply);
149
150    return DBUS_HANDLER_RESULT_HANDLED;
151}
152
153DBusHandlerResult avahi_dbus_respond_path(DBusConnection *c, DBusMessage *m, const char *path) {
154    DBusMessage *reply;
155
156    reply = dbus_message_new_method_return(m);
157
158    if (!reply) {
159        avahi_log_error("Failed allocate message");
160        return DBUS_HANDLER_RESULT_NEED_MEMORY;
161    }
162
163    dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
164    dbus_connection_send(c, reply, NULL);
165    dbus_message_unref(reply);
166
167    return DBUS_HANDLER_RESULT_HANDLED;
168}
169
170void avahi_dbus_append_server_error(DBusMessage *reply) {
171    const char *t;
172
173    t = avahi_error_number_to_dbus(avahi_server_errno(avahi_server));
174
175    dbus_message_append_args(
176        reply,
177        DBUS_TYPE_STRING, &t,
178        DBUS_TYPE_INVALID);
179}
180
181const char *avahi_dbus_map_browse_signal_name(AvahiBrowserEvent e) {
182    switch (e) {
183        case AVAHI_BROWSER_NEW : return "ItemNew";
184        case AVAHI_BROWSER_REMOVE : return "ItemRemove";
185        case AVAHI_BROWSER_FAILURE : return "Failure";
186        case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CacheExhausted";
187        case AVAHI_BROWSER_ALL_FOR_NOW : return "AllForNow";
188    }
189
190    abort();
191}
192
193const char *avahi_dbus_map_resolve_signal_name(AvahiResolverEvent e) {
194    switch (e) {
195        case AVAHI_RESOLVER_FOUND : return "Found";
196        case AVAHI_RESOLVER_FAILURE : return "Failure";
197    }
198
199    abort();
200}
201
202static char *file_get_contents(const char *fname) {
203    int fd = -1;
204    struct stat st;
205    ssize_t size;
206    char *buf = NULL;
207
208    assert(fname);
209
210#ifdef ENABLE_CHROOT
211    fd = avahi_chroot_helper_get_fd(fname);
212#else
213    fd = open(fname, O_RDONLY);
214#endif
215
216    if (fd < 0) {
217        avahi_log_error("Failed to open %s: %s", fname, strerror(errno));
218        goto fail;
219    }
220
221    if (fstat(fd, &st) < 0) {
222        avahi_log_error("stat(%s) failed: %s", fname, strerror(errno));
223        goto fail;
224    }
225
226    if (!(S_ISREG(st.st_mode))) {
227        avahi_log_error("Invalid file %s", fname);
228        goto fail;
229    }
230
231    if (st.st_size > 1024*1024) { /** 1MB */
232        avahi_log_error("File too large %s", fname);
233        goto fail;
234    }
235
236    buf = avahi_new(char, st.st_size+1);
237
238    if ((size = read(fd, buf, st.st_size)) < 0) {
239        avahi_log_error("read() failed: %s\n", strerror(errno));
240        goto fail;
241    }
242
243    buf[size] = 0;
244
245    close(fd);
246
247    return buf;
248
249fail:
250    if (fd >= 0)
251        close(fd);
252
253    if (buf)
254        avahi_free(buf);
255
256    return NULL;
257
258}
259
260DBusHandlerResult avahi_dbus_handle_introspect(DBusConnection *c, DBusMessage *m, const char *fname) {
261#ifdef AVAHI_DBUS_INTROSPECTION_DIR
262    char *contents, *path;
263    DBusError error;
264
265    assert(c);
266    assert(m);
267    assert(fname);
268
269    dbus_error_init(&error);
270
271    if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
272        avahi_log_error("Error parsing Introspect message: %s", error.message);
273        goto fail;
274    }
275
276    path = avahi_strdup_printf("%s/%s", AVAHI_DBUS_INTROSPECTION_DIR, fname);
277    contents = file_get_contents(path);
278    avahi_free(path);
279
280    if (!contents) {
281        avahi_log_error("Failed to load introspection data.");
282        goto fail;
283    }
284
285    avahi_dbus_respond_string(c, m, contents);
286    avahi_free(contents);
287
288    return DBUS_HANDLER_RESULT_HANDLED;
289
290fail:
291    if (dbus_error_is_set(&error))
292        dbus_error_free(&error);
293#endif
294
295    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
296
297}
298
299void avahi_dbus_append_string_list(DBusMessage *reply, AvahiStringList *txt) {
300    AvahiStringList *p;
301    DBusMessageIter iter, sub;
302
303    assert(reply);
304
305    dbus_message_iter_init_append(reply, &iter);
306    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "ay", &sub);
307
308    for (p = txt; p; p = p->next) {
309        DBusMessageIter sub2;
310        const uint8_t *data = p->text;
311
312        dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "y", &sub2);
313        dbus_message_iter_append_fixed_array(&sub2, DBUS_TYPE_BYTE, &data, p->size);
314        dbus_message_iter_close_container(&sub, &sub2);
315
316    }
317    dbus_message_iter_close_container(&iter, &sub);
318}
319
320int avahi_dbus_read_rdata(DBusMessage *m, int idx, void **rdata, uint32_t *size) {
321    DBusMessageIter iter, sub;
322    int n, j;
323    uint8_t *k;
324
325    assert(m);
326
327    dbus_message_iter_init(m, &iter);
328
329    for (j = 0; j < idx; j++)
330       dbus_message_iter_next(&iter);
331
332    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
333        dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
334        goto fail;
335
336    dbus_message_iter_recurse(&iter, &sub);
337    dbus_message_iter_get_fixed_array(&sub, &k, &n);
338
339    *rdata = k;
340    *size = n;
341
342    return 0;
343
344fail:
345    avahi_log_warn("Error parsing data");
346
347    *rdata = NULL;
348    size = 0;
349    return -1;
350}
351
352int avahi_dbus_read_strlst(DBusMessage *m, int idx, AvahiStringList **l) {
353    DBusMessageIter iter, sub;
354    int j;
355    AvahiStringList *strlst = NULL;
356
357    assert(m);
358    assert(l);
359
360    dbus_message_iter_init(m, &iter);
361
362    for (j = 0; j < idx; j++)
363        dbus_message_iter_next(&iter);
364
365    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
366        dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_ARRAY)
367        goto fail;
368
369    dbus_message_iter_recurse(&iter, &sub);
370
371    for (;;) {
372        int at, n;
373        const uint8_t *k;
374        DBusMessageIter sub2;
375
376        if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
377            break;
378
379        assert(at == DBUS_TYPE_ARRAY);
380
381        if (dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_BYTE)
382            goto fail;
383
384        dbus_message_iter_recurse(&sub, &sub2);
385
386        k = (const uint8_t*) "";
387        n = 0;
388        dbus_message_iter_get_fixed_array(&sub2, &k, &n);
389
390        if (!k)
391            k = (const uint8_t*) "";
392
393        strlst = avahi_string_list_add_arbitrary(strlst, k, n);
394
395        dbus_message_iter_next(&sub);
396    }
397
398    *l = strlst;
399
400    return 0;
401
402fail:
403    avahi_log_warn("Error parsing TXT data");
404
405    avahi_string_list_free(strlst);
406    *l = NULL;
407    return -1;
408}
409
410int avahi_dbus_is_our_own_service(Client *c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
411    AvahiSEntryGroup *g;
412
413    if (avahi_server_get_group_of_service(avahi_server, interface, protocol, name, type, domain, &g) == AVAHI_OK) {
414        EntryGroupInfo *egi;
415
416        for (egi = c->entry_groups; egi; egi = egi->entry_groups_next)
417            if (egi->entry_group == g)
418                return 1;
419    }
420
421    return 0;
422}
423
424int avahi_dbus_append_rdata(DBusMessage *message, const void *rdata, size_t size) {
425    DBusMessageIter iter, sub;
426
427    assert(message);
428
429    dbus_message_iter_init_append(message, &iter);
430
431    if (!(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &sub)) ||
432        !(dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &rdata, size)) ||
433        !(dbus_message_iter_close_container(&iter, &sub)))
434        return -1;
435
436    return 0;
437}
438