]> git.meshlink.io Git - catta/blob - avahi-client/browser.c
CVE-2009-0758: Reflector creates packet storm on legacy unicast traffic
[catta] / avahi-client / browser.c
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 <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include <dbus/dbus.h>
31
32 #include <avahi-client/client.h>
33 #include <avahi-common/dbus.h>
34 #include <avahi-common/llist.h>
35 #include <avahi-common/error.h>
36 #include <avahi-common/malloc.h>
37 #include <avahi-common/domain.h>
38
39 #include "client.h"
40 #include "internal.h"
41 #include "xdg-config.h"
42
43 static void parse_environment(AvahiDomainBrowser *b) {
44     char buf[AVAHI_DOMAIN_NAME_MAX*3], *e, *t, *p;
45
46     assert(b);
47     
48     if (!(e = getenv("AVAHI_BROWSE_DOMAINS")))
49         return;
50
51     snprintf(buf, sizeof(buf), "%s", e);
52
53     for (t = strtok_r(buf, ":", &p); t; t = strtok_r(NULL, ":", &p)) {
54         char domain[AVAHI_DOMAIN_NAME_MAX];
55         if (avahi_normalize_name(t, domain, sizeof(domain)))
56             b->static_browse_domains = avahi_string_list_add(b->static_browse_domains, domain);
57     }
58 }
59
60 static void parse_domain_file(AvahiDomainBrowser *b) {
61     FILE *f;
62     char buf[AVAHI_DOMAIN_NAME_MAX];
63     
64     assert(b);
65
66     if (!(f = avahi_xdg_config_open("avahi/browse-domains")))
67         return;
68     
69     
70     while (fgets(buf, sizeof(buf)-1, f)) {
71         char domain[AVAHI_DOMAIN_NAME_MAX];
72         buf[strcspn(buf, "\n\r")] = 0;
73         
74         if (avahi_normalize_name(buf, domain, sizeof(domain)))
75             b->static_browse_domains = avahi_string_list_add(b->static_browse_domains, domain);
76     }
77 }
78
79 static void domain_browser_ref(AvahiDomainBrowser *db) {
80     assert(db);
81     assert(db->ref >= 1);
82     db->ref++;
83 }
84
85 static void defer_timeout_callback(AvahiTimeout *t, void *userdata) {
86     AvahiDomainBrowser *db = userdata;
87     AvahiStringList *l;
88     assert(t);
89
90     db->client->poll_api->timeout_free(db->defer_timeout);
91     db->defer_timeout = NULL;
92
93     domain_browser_ref(db);
94
95     for (l = db->static_browse_domains; l; l = l->next) {
96
97         if (db->ref <= 1)
98             break;
99
100         db->callback(db, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, db->userdata);
101     }
102
103     avahi_domain_browser_free(db);
104 }
105
106 AvahiDomainBrowser* avahi_domain_browser_new(
107     AvahiClient *client,
108     AvahiIfIndex interface,
109     AvahiProtocol protocol,
110     const char *domain,
111     AvahiDomainBrowserType btype,
112     AvahiLookupFlags flags,
113     AvahiDomainBrowserCallback callback,
114     void *userdata) {
115     
116     AvahiDomainBrowser *db = NULL;
117     DBusMessage *message = NULL, *reply = NULL;
118     DBusError error;
119     char *path;
120     int32_t i_interface, i_protocol, bt;
121     uint32_t u_flags;
122
123     assert(client);
124     assert(callback);
125
126     dbus_error_init (&error);
127
128     if (!avahi_client_is_connected(client)) {
129         avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
130         goto fail;
131     }
132
133     if (!domain)
134         domain = "";
135
136     if (!(db = avahi_new (AvahiDomainBrowser, 1))) {
137         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
138         goto fail;
139     }
140
141     db->ref = 1;
142     db->client = client;
143     db->callback = callback;
144     db->userdata = userdata;
145     db->path = NULL;
146     db->interface = interface;
147     db->protocol = protocol;
148     db->static_browse_domains = NULL;
149     db->defer_timeout = NULL;
150
151     AVAHI_LLIST_PREPEND(AvahiDomainBrowser, domain_browsers, client->domain_browsers, db);
152
153     if (!(client->flags & AVAHI_CLIENT_IGNORE_USER_CONFIG)) {
154         parse_environment(db);
155         parse_domain_file(db);
156     }
157
158     db->static_browse_domains = avahi_string_list_reverse(db->static_browse_domains);
159     
160     if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "DomainBrowserNew"))) {
161         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
162         goto fail;
163     }
164
165     i_interface = (int32_t) interface;
166     i_protocol = (int32_t) protocol;
167     u_flags = (uint32_t) flags;
168     bt = btype;
169
170     if (!(dbus_message_append_args(
171               message,
172               DBUS_TYPE_INT32, &i_interface,
173               DBUS_TYPE_INT32, &i_protocol,
174               DBUS_TYPE_STRING, &domain,
175               DBUS_TYPE_INT32, &bt,
176               DBUS_TYPE_UINT32, &flags,
177               DBUS_TYPE_INVALID))) {
178         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
179         goto fail;
180     }
181
182     if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
183         dbus_error_is_set(&error)) {
184         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
185         goto fail;
186     }
187
188     if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
189         dbus_error_is_set(&error) ||
190         !path) {
191         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
192         goto fail;
193     }
194
195     if (!(db->path = avahi_strdup(path))) {
196
197         /* FIXME: We don't remove the object on the server side */
198
199         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
200         goto fail;
201     }
202
203     if (db->static_browse_domains && btype == AVAHI_DOMAIN_BROWSER_BROWSE) {
204         struct timeval tv = { 0, 0 };
205
206         if (!(db->defer_timeout = client->poll_api->timeout_new(client->poll_api, &tv, defer_timeout_callback, db))) {
207             avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
208             goto fail;
209         }
210     }
211     
212     dbus_message_unref(message);
213     dbus_message_unref(reply);
214     
215     return db;
216
217 fail:
218
219     if (dbus_error_is_set(&error)) {
220         avahi_client_set_dbus_error(client, &error);
221         dbus_error_free(&error);
222     }
223
224     if (db)
225         avahi_domain_browser_free(db);
226     
227     if (message)
228         dbus_message_unref(message);
229
230     if (reply)
231         dbus_message_unref(reply);
232
233     return NULL;
234 }
235
236 AvahiClient* avahi_domain_browser_get_client (AvahiDomainBrowser *b) {
237     assert(b);
238     return b->client;
239 }
240
241 int avahi_domain_browser_free (AvahiDomainBrowser *b) {
242     AvahiClient *client;
243     int r = AVAHI_OK;
244     
245     assert(b);
246     assert(b->ref >= 1);
247
248     if (--(b->ref) >= 1)
249         return AVAHI_OK;
250     
251     client = b->client;
252
253     if (b->path && avahi_client_is_connected(b->client))
254         r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "Free");
255
256     AVAHI_LLIST_REMOVE(AvahiDomainBrowser, domain_browsers, client->domain_browsers, b);
257
258     if (b->defer_timeout)
259         b->client->poll_api->timeout_free(b->defer_timeout);
260     
261     avahi_string_list_free(b->static_browse_domains);
262     avahi_free(b->path);
263     avahi_free(b);
264
265     return r;
266 }
267
268 DBusHandlerResult avahi_domain_browser_event (AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
269     AvahiDomainBrowser *db = NULL;
270     DBusError error;
271     const char *path;
272     char *domain = NULL;
273     int32_t interface, protocol;
274     uint32_t flags = 0;
275     AvahiStringList *l;
276
277     assert(client);
278     assert(message);
279     
280     dbus_error_init (&error);
281
282     if (!(path = dbus_message_get_path(message)))
283         goto fail;
284
285     for (db = client->domain_browsers; db; db = db->domain_browsers_next)
286         if (strcmp (db->path, path) == 0)
287             break;
288
289     if (!db)
290         goto fail;
291
292     interface = db->interface;
293     protocol = db->protocol;
294
295     switch (event) {
296         case AVAHI_BROWSER_NEW:
297         case AVAHI_BROWSER_REMOVE:
298             
299             if (!dbus_message_get_args(
300                     message, &error,
301                     DBUS_TYPE_INT32, &interface,
302                     DBUS_TYPE_INT32, &protocol,
303                     DBUS_TYPE_STRING, &domain,
304                     DBUS_TYPE_UINT32, &flags,
305                     DBUS_TYPE_INVALID) ||
306                 dbus_error_is_set (&error)) {
307                 fprintf(stderr, "Failed to parse browser event.\n");
308                 goto fail;
309             }
310
311             break;
312             
313         case AVAHI_BROWSER_CACHE_EXHAUSTED:
314         case AVAHI_BROWSER_ALL_FOR_NOW:
315             break;
316
317         case AVAHI_BROWSER_FAILURE: {
318             char *etxt;
319             
320             if (!dbus_message_get_args(
321                     message, &error,
322                     DBUS_TYPE_STRING, &etxt,
323                     DBUS_TYPE_INVALID) ||
324                 dbus_error_is_set (&error)) {
325                 fprintf(stderr, "Failed to parse browser event.\n");
326                 goto fail;
327             }
328             
329             avahi_client_set_errno(db->client, avahi_error_dbus_to_number(etxt));
330             break;
331         }
332     }
333
334     if (domain)
335         for (l = db->static_browse_domains; l; l = l->next)
336             if (avahi_domain_equal((char*) l->text, domain)) {
337                 /* We had this entry already in the static entries */
338                 return DBUS_HANDLER_RESULT_HANDLED;
339             }
340     
341     db->callback(db, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, domain, (AvahiLookupResultFlags) flags, db->userdata);
342
343     return DBUS_HANDLER_RESULT_HANDLED;
344
345 fail:
346     dbus_error_free (&error);
347     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
348 }
349
350 /* AvahiServiceTypeBrowser */
351
352 AvahiServiceTypeBrowser* avahi_service_type_browser_new(
353     AvahiClient *client,
354     AvahiIfIndex interface,
355     AvahiProtocol protocol,
356     const char *domain,
357     AvahiLookupFlags flags, 
358     AvahiServiceTypeBrowserCallback callback,
359     void *userdata) {
360         
361     AvahiServiceTypeBrowser *b = NULL;
362     DBusMessage *message = NULL, *reply = NULL;
363     DBusError error;
364     char *path;
365     int32_t i_interface, i_protocol;
366     uint32_t u_flags;
367
368     assert(client);
369     assert(callback);
370
371     dbus_error_init(&error);
372
373     if (!avahi_client_is_connected(client)) {
374         avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
375         goto fail;
376     }
377
378     if (!domain)
379         domain = "";
380
381     if (!(b = avahi_new(AvahiServiceTypeBrowser, 1))) {
382         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
383         goto fail;
384     }
385
386     b->client = client;
387     b->callback = callback;
388     b->userdata = userdata;
389     b->path = NULL;
390     b->domain = NULL;
391     b->interface = interface;
392     b->protocol = protocol;
393
394     AVAHI_LLIST_PREPEND(AvahiServiceTypeBrowser, service_type_browsers, client->service_type_browsers, b);
395
396     if (domain[0])
397         if (!(b->domain = avahi_strdup(domain))) {
398             avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
399             goto fail;
400         }
401           
402     if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "ServiceTypeBrowserNew"))) {
403         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
404         goto fail;
405     }
406     
407     i_interface = (int32_t) interface;
408     i_protocol = (int32_t) protocol;
409     u_flags = (uint32_t) flags;
410
411     if (!dbus_message_append_args(
412             message,
413             DBUS_TYPE_INT32, &i_interface,
414             DBUS_TYPE_INT32, &i_protocol,
415             DBUS_TYPE_STRING, &domain,
416             DBUS_TYPE_UINT32, &u_flags,
417             DBUS_TYPE_INVALID)) {
418         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
419         goto fail;
420     }
421
422     if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
423         dbus_error_is_set(&error)) {
424         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
425         goto fail;
426     }
427
428     if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
429         dbus_error_is_set(&error) ||
430         !path) {
431         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
432         goto fail;
433     }
434
435     if (!(b->path = avahi_strdup(path))) {
436
437         /* FIXME: We don't remove the object on the server side */
438
439         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
440         goto fail;
441     }
442
443     dbus_message_unref(message);
444     dbus_message_unref(reply);
445
446     return b;
447
448 fail:
449     
450     if (dbus_error_is_set(&error)) {
451         avahi_client_set_dbus_error(client, &error);
452         dbus_error_free(&error);
453     }
454
455     if (b)
456         avahi_service_type_browser_free(b);
457     
458     if (message)
459         dbus_message_unref(message);
460
461     if (reply)
462         dbus_message_unref(reply);
463
464     return NULL;
465 }
466
467 AvahiClient* avahi_service_type_browser_get_client (AvahiServiceTypeBrowser *b) {
468     assert(b);
469     return b->client;
470 }
471
472 int avahi_service_type_browser_free (AvahiServiceTypeBrowser *b) {
473     AvahiClient *client;
474     int r = AVAHI_OK;
475
476     assert(b);
477     client = b->client;
478
479     if (b->path && avahi_client_is_connected(b->client))
480         r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "Free");
481
482     AVAHI_LLIST_REMOVE(AvahiServiceTypeBrowser, service_type_browsers, b->client->service_type_browsers, b);
483
484     avahi_free(b->path);
485     avahi_free(b->domain);
486     avahi_free(b);
487     return r;
488 }
489
490 DBusHandlerResult avahi_service_type_browser_event (AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
491     AvahiServiceTypeBrowser *b = NULL;
492     DBusError error;
493     const char *path;
494     char *domain, *type = NULL;
495     int32_t interface, protocol;
496     uint32_t flags = 0;
497
498     assert(client);
499     assert(message);
500     
501     dbus_error_init (&error);
502
503     if (!(path = dbus_message_get_path(message)))
504         goto fail;
505
506     for (b = client->service_type_browsers; b; b = b->service_type_browsers_next)
507         if (strcmp (b->path, path) == 0)
508             break;
509
510     if (!b)
511         goto fail;
512
513     domain = b->domain;
514     interface = b->interface;
515     protocol = b->protocol;
516     
517     switch (event) {
518         case AVAHI_BROWSER_NEW:
519         case AVAHI_BROWSER_REMOVE:
520             if (!dbus_message_get_args(
521                     message, &error,
522                     DBUS_TYPE_INT32, &interface,
523                     DBUS_TYPE_INT32, &protocol,
524                     DBUS_TYPE_STRING, &type,
525                     DBUS_TYPE_STRING, &domain,
526                     DBUS_TYPE_UINT32, &flags,
527                     DBUS_TYPE_INVALID) ||
528                 dbus_error_is_set(&error)) {
529                 fprintf(stderr, "Failed to parse browser event.\n");
530                 goto fail;
531             }
532             break;
533             
534         case AVAHI_BROWSER_CACHE_EXHAUSTED:
535         case AVAHI_BROWSER_ALL_FOR_NOW:
536             break;
537
538         case AVAHI_BROWSER_FAILURE: {
539             char *etxt;
540             
541             if (!dbus_message_get_args(
542                     message, &error,
543                     DBUS_TYPE_STRING, &etxt,
544                     DBUS_TYPE_INVALID) ||
545                 dbus_error_is_set (&error)) {
546                 fprintf(stderr, "Failed to parse browser event.\n");
547                 goto fail;
548             }
549             
550             avahi_client_set_errno(b->client, avahi_error_dbus_to_number(etxt));
551             break;
552         }
553     }
554
555     b->callback(b, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, type, domain, (AvahiLookupResultFlags) flags, b->userdata);
556
557     return DBUS_HANDLER_RESULT_HANDLED;
558
559 fail:
560     dbus_error_free (&error);
561     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
562 }
563
564 /* AvahiServiceBrowser */
565
566 AvahiServiceBrowser* avahi_service_browser_new(
567     AvahiClient *client,
568     AvahiIfIndex interface,
569     AvahiProtocol protocol,
570     const char *type,
571     const char *domain,
572     AvahiLookupFlags flags, 
573     AvahiServiceBrowserCallback callback,
574     void *userdata) {
575     
576     AvahiServiceBrowser *b = NULL;
577     DBusMessage *message = NULL, *reply = NULL;
578     DBusError error;
579     char *path;
580     int32_t i_protocol, i_interface;
581     uint32_t u_flags;
582
583     assert(client);
584     assert(type);
585     assert(callback);
586
587     dbus_error_init(&error);
588
589     if (!avahi_client_is_connected(client)) {
590         avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
591         goto fail;
592     }
593
594     if (!domain)
595         domain = "";
596
597     if (!(b = avahi_new(AvahiServiceBrowser, 1))) {
598         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
599         goto fail;
600     }
601     
602     b->client = client;
603     b->callback = callback;
604     b->userdata = userdata;
605     b->path = NULL;
606     b->type = b->domain = NULL;
607     b->interface = interface;
608     b->protocol = protocol;
609
610     AVAHI_LLIST_PREPEND(AvahiServiceBrowser, service_browsers, client->service_browsers, b);
611
612     if (!(b->type = avahi_strdup(type))) {
613         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
614         goto fail;
615     }
616     
617     if (domain && domain[0])
618         if (!(b->domain = avahi_strdup(domain))) {
619             avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
620             goto fail;
621         }
622     
623     if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "ServiceBrowserNew"))) {
624         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
625         goto fail;
626     }
627
628     i_interface = (int32_t) interface;
629     i_protocol = (int32_t) protocol;
630     u_flags = (uint32_t) flags;
631
632     if (!dbus_message_append_args(
633             message,
634             DBUS_TYPE_INT32, &i_interface,
635             DBUS_TYPE_INT32, &i_protocol,
636             DBUS_TYPE_STRING, &type,
637             DBUS_TYPE_STRING, &domain,
638             DBUS_TYPE_UINT32, &u_flags,
639             DBUS_TYPE_INVALID)) {
640         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
641         goto fail;
642     }
643
644     if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
645         dbus_error_is_set(&error)) {
646         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
647         goto fail;
648     }
649
650     if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
651         dbus_error_is_set(&error) ||
652         !path) {
653         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
654         goto fail;
655     }
656
657     if (!(b->path = avahi_strdup(path))) {
658
659         /* FIXME: We don't remove the object on the server side */
660
661         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
662         goto fail;
663     }
664
665     dbus_message_unref(message);
666     dbus_message_unref(reply);
667     
668     return b;
669
670 fail:
671     if (dbus_error_is_set(&error)) {
672         avahi_client_set_dbus_error(client, &error);
673         dbus_error_free(&error);
674     }
675
676     if (b)
677         avahi_service_browser_free(b);
678     
679     if (message)
680         dbus_message_unref(message);
681
682     if (reply)
683         dbus_message_unref(reply);
684
685     return NULL;
686 }
687
688 AvahiClient* avahi_service_browser_get_client (AvahiServiceBrowser *b) {
689     assert(b);
690     return b->client;
691 }
692
693 int avahi_service_browser_free (AvahiServiceBrowser *b) {
694     AvahiClient *client;
695     int r = AVAHI_OK;
696
697     assert(b);
698     client = b->client;
699
700     if (b->path && avahi_client_is_connected(b->client))
701         r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "Free");
702
703     AVAHI_LLIST_REMOVE(AvahiServiceBrowser, service_browsers, b->client->service_browsers, b);
704
705     avahi_free(b->path);
706     avahi_free(b->type);
707     avahi_free(b->domain);
708     avahi_free(b);
709     return r;
710 }
711
712 DBusHandlerResult avahi_service_browser_event(AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
713     AvahiServiceBrowser *b = NULL;
714     DBusError error;
715     const char *path;
716     char *name = NULL, *type, *domain;
717     int32_t interface, protocol;
718     uint32_t flags = 0;
719
720     dbus_error_init (&error);
721
722     if (!(path = dbus_message_get_path(message)))
723         goto fail;
724
725     for (b = client->service_browsers; b; b = b->service_browsers_next)
726         if (strcmp (b->path, path) == 0)
727             break;
728
729     if (!b)
730         goto fail;
731
732     type = b->type;
733     domain = b->domain;
734     interface = b->interface;
735     protocol = b->protocol;
736
737     switch (event) {
738         case AVAHI_BROWSER_NEW:
739         case AVAHI_BROWSER_REMOVE:
740             
741             if (!dbus_message_get_args (
742                     message, &error,
743                     DBUS_TYPE_INT32, &interface,
744                     DBUS_TYPE_INT32, &protocol,
745                     DBUS_TYPE_STRING, &name,
746                     DBUS_TYPE_STRING, &type,
747                     DBUS_TYPE_STRING, &domain,
748                     DBUS_TYPE_UINT32, &flags,
749                     DBUS_TYPE_INVALID) ||
750                 dbus_error_is_set(&error)) {
751                 fprintf(stderr, "Failed to parse browser event.\n");
752                 goto fail;
753             }
754             break;
755
756         case AVAHI_BROWSER_CACHE_EXHAUSTED:
757         case AVAHI_BROWSER_ALL_FOR_NOW:
758             break;
759
760         case AVAHI_BROWSER_FAILURE: {
761             char *etxt;
762             
763             if (!dbus_message_get_args(
764                     message, &error,
765                     DBUS_TYPE_STRING, &etxt,
766                     DBUS_TYPE_INVALID) ||
767                 dbus_error_is_set (&error)) {
768                 fprintf(stderr, "Failed to parse browser event.\n");
769                 goto fail;
770             }
771             
772             avahi_client_set_errno(b->client, avahi_error_dbus_to_number(etxt));
773             break;
774         }
775     }
776
777     b->callback(b, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, name, type, domain, (AvahiLookupResultFlags) flags, b->userdata);
778
779     return DBUS_HANDLER_RESULT_HANDLED;
780
781 fail:
782     dbus_error_free (&error);
783     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
784 }
785
786 /* AvahiRecordBrowser */
787
788 AvahiRecordBrowser* avahi_record_browser_new(
789     AvahiClient *client,
790     AvahiIfIndex interface,
791     AvahiProtocol protocol,
792     const char *name,
793     uint16_t clazz,
794     uint16_t type,
795     AvahiLookupFlags flags,
796     AvahiRecordBrowserCallback callback,
797     void *userdata) {
798     
799     AvahiRecordBrowser *b = NULL;
800     DBusMessage *message = NULL, *reply = NULL;
801     DBusError error;
802     char *path;
803     int32_t i_protocol, i_interface;
804     uint32_t u_flags;
805
806     assert(client);
807     assert(name);
808     assert(callback);
809
810     dbus_error_init(&error);
811
812     if (!avahi_client_is_connected(client)) {
813         avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
814         goto fail;
815     }
816
817     if (!(b = avahi_new(AvahiRecordBrowser, 1))) {
818         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
819         goto fail;
820     }
821     
822     b->client = client;
823     b->callback = callback;
824     b->userdata = userdata;
825     b->path = NULL;
826     b->name = NULL;
827     b->clazz = clazz;
828     b->type = type;
829     b->interface = interface;
830     b->protocol = protocol;
831
832     AVAHI_LLIST_PREPEND(AvahiRecordBrowser, record_browsers, client->record_browsers, b);
833
834     if (!(b->name = avahi_strdup(name))) {
835         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
836         goto fail;
837     }
838     
839     if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "RecordBrowserNew"))) {
840         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
841         goto fail;
842     }
843
844     i_interface = (int32_t) interface;
845     i_protocol = (int32_t) protocol;
846     u_flags = (uint32_t) flags;
847
848     if (!dbus_message_append_args(
849             message,
850             DBUS_TYPE_INT32, &i_interface,
851             DBUS_TYPE_INT32, &i_protocol,
852             DBUS_TYPE_STRING, &name,
853             DBUS_TYPE_UINT16, &clazz,
854             DBUS_TYPE_UINT16, &type,
855             DBUS_TYPE_UINT32, &u_flags,
856             DBUS_TYPE_INVALID)) {
857         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
858         goto fail;
859     }
860
861     if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
862         dbus_error_is_set(&error)) {
863         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
864         goto fail;
865     }
866
867     if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
868         dbus_error_is_set(&error) ||
869         !path) {
870         avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
871         goto fail;
872     }
873
874     if (!(b->path = avahi_strdup(path))) {
875
876         /* FIXME: We don't remove the object on the server side */
877
878         avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
879         goto fail;
880     }
881
882     dbus_message_unref(message);
883     dbus_message_unref(reply);
884     
885     return b;
886
887 fail:
888     if (dbus_error_is_set(&error)) {
889         avahi_client_set_dbus_error(client, &error);
890         dbus_error_free(&error);
891     }
892
893     if (b)
894         avahi_record_browser_free(b);
895     
896     if (message)
897         dbus_message_unref(message);
898
899     if (reply)
900         dbus_message_unref(reply);
901
902     return NULL;
903 }
904
905 AvahiClient* avahi_record_browser_get_client (AvahiRecordBrowser *b) {
906     assert(b);
907     return b->client;
908 }
909
910 int avahi_record_browser_free (AvahiRecordBrowser *b) {
911     AvahiClient *client;
912     int r = AVAHI_OK;
913
914     assert(b);
915     client = b->client;
916
917     if (b->path && avahi_client_is_connected(b->client))
918         r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "Free");
919
920     AVAHI_LLIST_REMOVE(AvahiRecordBrowser, record_browsers, b->client->record_browsers, b);
921
922     avahi_free(b->path);
923     avahi_free(b->name);
924     avahi_free(b);
925     return r;
926 }
927
928 DBusHandlerResult avahi_record_browser_event(AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
929     AvahiRecordBrowser *b = NULL;
930     DBusError error;
931     const char *path;
932     char *name;
933     int32_t interface, protocol;
934     uint32_t flags = 0;
935     uint16_t clazz, type;
936     void *rdata = NULL;
937     int rdata_size = 0;
938
939     dbus_error_init (&error);
940
941     if (!(path = dbus_message_get_path(message)))
942         goto fail;
943
944     for (b = client->record_browsers; b; b = b->record_browsers_next)
945         if (strcmp (b->path, path) == 0)
946             break;
947
948     if (!b)
949         goto fail;
950
951     interface = b->interface;
952     protocol = b->protocol;
953     clazz = b->clazz;
954     type = b->type;
955     name = b->name;
956
957     switch (event) {
958         case AVAHI_BROWSER_NEW:
959         case AVAHI_BROWSER_REMOVE: {
960             DBusMessageIter iter, sub;
961             int j;
962
963             if (!dbus_message_get_args (
964                     message, &error,
965                     DBUS_TYPE_INT32, &interface,
966                     DBUS_TYPE_INT32, &protocol,
967                     DBUS_TYPE_STRING, &name,
968                     DBUS_TYPE_UINT16, &clazz,
969                     DBUS_TYPE_UINT16, &type,
970                     DBUS_TYPE_INVALID) ||
971                 dbus_error_is_set(&error)) {
972                 fprintf(stderr, "Failed to parse browser event.\n");
973                 goto fail;
974             }
975
976
977             dbus_message_iter_init(message, &iter);
978
979             for (j = 0; j < 5; j++)
980                 dbus_message_iter_next(&iter);
981     
982             if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
983                 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
984                 goto fail;
985
986             dbus_message_iter_recurse(&iter, &sub);
987             dbus_message_iter_get_fixed_array(&sub, &rdata, &rdata_size);
988
989             dbus_message_iter_next(&iter);
990             
991             if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
992                 goto fail;
993
994             dbus_message_iter_get_basic(&iter, &flags);
995             
996             break;
997         }
998
999         case AVAHI_BROWSER_CACHE_EXHAUSTED:
1000         case AVAHI_BROWSER_ALL_FOR_NOW:
1001             break;
1002
1003         case AVAHI_BROWSER_FAILURE: {
1004             char *etxt;
1005             
1006             if (!dbus_message_get_args(
1007                     message, &error,
1008                     DBUS_TYPE_STRING, &etxt,
1009                     DBUS_TYPE_INVALID) ||
1010                 dbus_error_is_set (&error)) {
1011                 fprintf(stderr, "Failed to parse browser event.\n");
1012                 goto fail;
1013             }
1014             
1015             avahi_client_set_errno(b->client, avahi_error_dbus_to_number(etxt));
1016             break;
1017         }
1018     }
1019
1020     b->callback(b, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, name, clazz, type, rdata, (size_t) rdata_size, (AvahiLookupResultFlags) flags, b->userdata);
1021
1022     return DBUS_HANDLER_RESULT_HANDLED;
1023
1024 fail:
1025     dbus_error_free (&error);
1026     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1027 }
1028