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/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 
avahi_dbus_respond_error(DBusConnection * c,DBusMessage * m,int error,const char * text)45 DBusHandlerResult 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 
avahi_dbus_respond_string(DBusConnection * c,DBusMessage * m,const char * text)69 DBusHandlerResult 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 
avahi_dbus_respond_int32(DBusConnection * c,DBusMessage * m,int32_t i)86 DBusHandlerResult 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 
avahi_dbus_respond_uint32(DBusConnection * c,DBusMessage * m,uint32_t u)103 DBusHandlerResult 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 
avahi_dbus_respond_boolean(DBusConnection * c,DBusMessage * m,int b)120 DBusHandlerResult 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 
avahi_dbus_respond_ok(DBusConnection * c,DBusMessage * m)137 DBusHandlerResult avahi_dbus_respond_ok(DBusConnection *c, DBusMessage *m) {
138     DBusMessage *reply;
139 
140     if (dbus_message_get_no_reply(m))
141             return DBUS_HANDLER_RESULT_HANDLED;
142 
143     reply = dbus_message_new_method_return(m);
144 
145     if (!reply) {
146         avahi_log_error("Failed allocate message");
147         return DBUS_HANDLER_RESULT_NEED_MEMORY;
148     }
149 
150     dbus_connection_send(c, reply, NULL);
151     dbus_message_unref(reply);
152 
153     return DBUS_HANDLER_RESULT_HANDLED;
154 }
155 
avahi_dbus_respond_path(DBusConnection * c,DBusMessage * m,const char * path)156 DBusHandlerResult avahi_dbus_respond_path(DBusConnection *c, DBusMessage *m, const char *path) {
157     DBusMessage *reply;
158 
159     reply = dbus_message_new_method_return(m);
160 
161     if (!reply) {
162         avahi_log_error("Failed allocate message");
163         return DBUS_HANDLER_RESULT_NEED_MEMORY;
164     }
165 
166     dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
167     dbus_connection_send(c, reply, NULL);
168     dbus_message_unref(reply);
169 
170     return DBUS_HANDLER_RESULT_HANDLED;
171 }
172 
avahi_dbus_append_server_error(DBusMessage * reply)173 void avahi_dbus_append_server_error(DBusMessage *reply) {
174     const char *t;
175 
176     t = avahi_error_number_to_dbus(avahi_server_errno(avahi_server));
177 
178     dbus_message_append_args(
179         reply,
180         DBUS_TYPE_STRING, &t,
181         DBUS_TYPE_INVALID);
182 }
183 
avahi_dbus_map_browse_signal_name(AvahiBrowserEvent e)184 const char *avahi_dbus_map_browse_signal_name(AvahiBrowserEvent e) {
185     switch (e) {
186         case AVAHI_BROWSER_NEW : return "ItemNew";
187         case AVAHI_BROWSER_REMOVE : return "ItemRemove";
188         case AVAHI_BROWSER_FAILURE : return "Failure";
189         case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CacheExhausted";
190         case AVAHI_BROWSER_ALL_FOR_NOW : return "AllForNow";
191     }
192 
193     abort();
194 }
195 
avahi_dbus_map_resolve_signal_name(AvahiResolverEvent e)196 const char *avahi_dbus_map_resolve_signal_name(AvahiResolverEvent e) {
197     switch (e) {
198         case AVAHI_RESOLVER_FOUND : return "Found";
199         case AVAHI_RESOLVER_FAILURE : return "Failure";
200     }
201 
202     abort();
203 }
204 
file_get_contents(const char * fname)205 static char *file_get_contents(const char *fname) {
206     int fd = -1;
207     struct stat st;
208     ssize_t size;
209     char *buf = NULL;
210 
211     assert(fname);
212 
213 #ifdef ENABLE_CHROOT
214     fd = avahi_chroot_helper_get_fd(fname);
215 #else
216     fd = open(fname, O_RDONLY);
217 #endif
218 
219     if (fd < 0) {
220         avahi_log_error("Failed to open %s: %s", fname, strerror(errno));
221         goto fail;
222     }
223 
224     if (fstat(fd, &st) < 0) {
225         avahi_log_error("stat(%s) failed: %s", fname, strerror(errno));
226         goto fail;
227     }
228 
229     if (!(S_ISREG(st.st_mode))) {
230         avahi_log_error("Invalid file %s", fname);
231         goto fail;
232     }
233 
234     if (st.st_size > 1024*1024) { /** 1MB */
235         avahi_log_error("File too large %s", fname);
236         goto fail;
237     }
238 
239     buf = avahi_new(char, st.st_size+1);
240 
241     if ((size = read(fd, buf, st.st_size)) < 0) {
242         avahi_log_error("read() failed: %s\n", strerror(errno));
243         goto fail;
244     }
245 
246     buf[size] = 0;
247 
248     close(fd);
249 
250     return buf;
251 
252 fail:
253     if (fd >= 0)
254         close(fd);
255 
256     if (buf)
257         avahi_free(buf);
258 
259     return NULL;
260 
261 }
262 
avahi_dbus_handle_introspect(DBusConnection * c,DBusMessage * m,const char * fname)263 DBusHandlerResult avahi_dbus_handle_introspect(DBusConnection *c, DBusMessage *m, const char *fname) {
264     char *contents, *path;
265     DBusError error;
266 
267     assert(c);
268     assert(m);
269     assert(fname);
270 
271     dbus_error_init(&error);
272 
273     if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
274         avahi_log_error("Error parsing Introspect message: %s", error.message);
275         goto fail;
276     }
277 
278     path = avahi_strdup_printf("%s/%s", AVAHI_DBUS_INTROSPECTION_DIR, fname);
279     contents = file_get_contents(path);
280     avahi_free(path);
281 
282     if (!contents) {
283         avahi_log_error("Failed to load introspection data.");
284         goto fail;
285     }
286 
287     avahi_dbus_respond_string(c, m, contents);
288     avahi_free(contents);
289 
290     return DBUS_HANDLER_RESULT_HANDLED;
291 
292 fail:
293     if (dbus_error_is_set(&error))
294         dbus_error_free(&error);
295 
296     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
297 
298 }
299 
avahi_dbus_append_string_list(DBusMessage * reply,AvahiStringList * txt)300 void avahi_dbus_append_string_list(DBusMessage *reply, AvahiStringList *txt) {
301     AvahiStringList *p;
302     DBusMessageIter iter, sub;
303 
304     assert(reply);
305 
306     dbus_message_iter_init_append(reply, &iter);
307     dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "ay", &sub);
308 
309     for (p = txt; p; p = p->next) {
310         DBusMessageIter sub2;
311         const uint8_t *data = p->text;
312 
313         dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "y", &sub2);
314         dbus_message_iter_append_fixed_array(&sub2, DBUS_TYPE_BYTE, &data, p->size);
315         dbus_message_iter_close_container(&sub, &sub2);
316 
317     }
318     dbus_message_iter_close_container(&iter, &sub);
319 }
320 
avahi_dbus_read_rdata(DBusMessage * m,int idx,void ** rdata,uint32_t * size)321 int avahi_dbus_read_rdata(DBusMessage *m, int idx, void **rdata, uint32_t *size) {
322     DBusMessageIter iter, sub;
323     int n, j;
324     uint8_t *k;
325 
326     assert(m);
327 
328     dbus_message_iter_init(m, &iter);
329 
330     for (j = 0; j < idx; j++)
331        dbus_message_iter_next(&iter);
332 
333     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
334         dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
335         goto fail;
336 
337     dbus_message_iter_recurse(&iter, &sub);
338     dbus_message_iter_get_fixed_array(&sub, &k, &n);
339 
340     *rdata = k;
341     *size = n;
342 
343     return 0;
344 
345 fail:
346     avahi_log_warn("Error parsing data");
347 
348     *rdata = NULL;
349     size = 0;
350     return -1;
351 }
352 
avahi_dbus_read_strlst(DBusMessage * m,int idx,AvahiStringList ** l)353 int avahi_dbus_read_strlst(DBusMessage *m, int idx, AvahiStringList **l) {
354     DBusMessageIter iter, sub;
355     int j;
356     AvahiStringList *strlst = NULL;
357 
358     assert(m);
359     assert(l);
360 
361     dbus_message_iter_init(m, &iter);
362 
363     for (j = 0; j < idx; j++)
364         dbus_message_iter_next(&iter);
365 
366     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
367         dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_ARRAY)
368         goto fail;
369 
370     dbus_message_iter_recurse(&iter, &sub);
371 
372     for (;;) {
373         int at, n;
374         const uint8_t *k;
375         DBusMessageIter sub2;
376 
377         if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
378             break;
379 
380         assert(at == DBUS_TYPE_ARRAY);
381 
382         if (dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_BYTE)
383             goto fail;
384 
385         dbus_message_iter_recurse(&sub, &sub2);
386 
387         k = (const uint8_t*) "";
388         n = 0;
389         dbus_message_iter_get_fixed_array(&sub2, &k, &n);
390 
391         if (!k)
392             k = (const uint8_t*) "";
393 
394         strlst = avahi_string_list_add_arbitrary(strlst, k, n);
395 
396         dbus_message_iter_next(&sub);
397     }
398 
399     *l = strlst;
400 
401     return 0;
402 
403 fail:
404     avahi_log_warn("Error parsing TXT data");
405 
406     avahi_string_list_free(strlst);
407     *l = NULL;
408     return -1;
409 }
410 
avahi_dbus_is_our_own_service(Client * c,AvahiIfIndex interface,AvahiProtocol protocol,const char * name,const char * type,const char * domain)411 int avahi_dbus_is_our_own_service(Client *c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
412     AvahiSEntryGroup *g;
413 
414     if (avahi_server_get_group_of_service(avahi_server, interface, protocol, name, type, domain, &g) == AVAHI_OK) {
415         EntryGroupInfo *egi;
416 
417         for (egi = c->entry_groups; egi; egi = egi->entry_groups_next)
418             if (egi->entry_group == g)
419                 return 1;
420     }
421 
422     return 0;
423 }
424 
avahi_dbus_append_rdata(DBusMessage * message,const void * rdata,size_t size)425 int avahi_dbus_append_rdata(DBusMessage *message, const void *rdata, size_t size) {
426     DBusMessageIter iter, sub;
427 
428     assert(message);
429 
430     dbus_message_iter_init_append(message, &iter);
431 
432     if (!(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &sub)) ||
433         !(dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &rdata, size)) ||
434         !(dbus_message_iter_close_container(&iter, &sub)))
435         return -1;
436 
437     return 0;
438 }
439