1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2008 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22 
23 /*
24  * TODO: - locking
25  *       - cancellation
26  *       - error handling
27  */
28 
29 #include <config.h>
30 #include <string.h>
31 #include <glib/gi18n-lib.h>
32 
33 #include <avahi-client/client.h>
34 #include <avahi-client/lookup.h>
35 #include <avahi-common/error.h>
36 #include <avahi-common/timeval.h>
37 #include <avahi-glib/glib-watch.h>
38 #include <avahi-glib/glib-malloc.h>
39 
40 #include <net/if.h>
41 
42 #include "gvfsdnssdutils.h"
43 #include "gvfsdnssdresolver.h"
44 
45 enum
46 {
47   PROP_0,
48   PROP_ENCODED_TRIPLE,
49   PROP_REQUIRED_TXT_KEYS,
50   PROP_SERVICE_NAME,
51   PROP_SERVICE_TYPE,
52   PROP_DOMAIN,
53   PROP_TIMEOUT_MSEC,
54 
55   PROP_IS_RESOLVED,
56   PROP_ADDRESS,
57   PROP_INTERFACE,
58   PROP_PORT,
59   PROP_TXT_RECORDS,
60 };
61 
62 enum {
63     CHANGED,
64     LAST_SIGNAL
65 };
66 
67 static guint signals[LAST_SIGNAL] = { 0 };
68 
69 struct _GVfsDnsSdResolver
70 {
71   GObject parent_instance;
72 
73   char *encoded_triple;
74   char *service_name;
75   char *service_type;
76   char *domain;
77   char *required_txt_keys;
78   char **required_txt_keys_broken_out;
79   guint timeout_msec;
80 
81   gboolean is_resolved;
82   char *address;
83   gchar *interface;
84   guint port;
85   char **txt_records;
86 
87   AvahiServiceResolver *avahi_resolver;
88   guint start_avahi_resolver_id;
89 };
90 
91 
92 struct _GVfsDnsSdResolverClass
93 {
94   GObjectClass parent_class;
95 
96   /* signals */
97   void (*changed) (GVfsDnsSdResolver *resolver);
98 };
99 
100 G_DEFINE_TYPE (GVfsDnsSdResolver, g_vfs_dns_sd_resolver, G_TYPE_OBJECT);
101 
102 static gboolean resolver_supports_mdns = FALSE;
103 static AvahiClient *global_client = NULL;
104 static gboolean avahi_initialized = FALSE;
105 static void free_global_avahi_client (void);
106 static AvahiClient *get_global_avahi_client (GError **error);
107 
108 static void ensure_avahi_resolver (GVfsDnsSdResolver  *resolver);
109 
110 static void service_resolver_cb (AvahiServiceResolver   *resolver,
111                                  AvahiIfIndex            interface,
112                                  AvahiProtocol           protocol,
113                                  AvahiResolverEvent      event,
114                                  const char             *name,
115                                  const char             *type,
116                                  const char             *domain,
117                                  const char             *host_name,
118                                  const AvahiAddress     *a,
119                                  uint16_t                port,
120                                  AvahiStringList        *txt,
121                                  AvahiLookupResultFlags  flags,
122                                  void                   *user_data);
123 
124 
125 
126 static GList *resolvers = NULL;
127 
128 static void
129 clear_avahi_data (GVfsDnsSdResolver *resolver);
130 
131 static void
remove_client_from_resolver(GVfsDnsSdResolver * resolver)132 remove_client_from_resolver (GVfsDnsSdResolver *resolver)
133 {
134   if (resolver->avahi_resolver != NULL)
135     {
136       avahi_service_resolver_free (resolver->avahi_resolver);
137       resolver->avahi_resolver = NULL;
138     }
139 
140   clear_avahi_data (resolver);
141 }
142 
143 static void
add_client_to_resolver(GVfsDnsSdResolver * resolver)144 add_client_to_resolver (GVfsDnsSdResolver *resolver)
145 {
146   ensure_avahi_resolver (resolver);
147 }
148 
149 /* Callback for state changes on the Client */
150 static void
avahi_client_callback(AvahiClient * client,AvahiClientState state,void * userdata)151 avahi_client_callback (AvahiClient *client, AvahiClientState state, void *userdata)
152 {
153   if (global_client == NULL)
154     global_client = client;
155 
156   if (state == AVAHI_CLIENT_FAILURE)
157     {
158       if (avahi_client_errno (client) == AVAHI_ERR_DISCONNECTED)
159         {
160           free_global_avahi_client ();
161 
162           /* Attempt to reconnect */
163           get_global_avahi_client (NULL);
164         }
165     }
166   else if (state == AVAHI_CLIENT_S_RUNNING)
167     {
168       /* Start resolving again */
169       g_list_foreach (resolvers, (GFunc) add_client_to_resolver, NULL);
170     }
171 }
172 
173 static void
free_global_avahi_client(void)174 free_global_avahi_client (void)
175 {
176   /* Remove current resolvers */
177   g_list_foreach (resolvers, (GFunc) remove_client_from_resolver, NULL);
178 
179   /* Destroy client */
180   if (global_client)
181     {
182       avahi_client_free (global_client);
183     }
184   global_client = NULL;
185   avahi_initialized = FALSE;
186 }
187 
188 static AvahiClient *
get_global_avahi_client(GError ** error)189 get_global_avahi_client (GError **error)
190 {
191   static AvahiGLibPoll *glib_poll = NULL;
192   int avahi_error;
193 
194   if (!avahi_initialized)
195     {
196       avahi_initialized = TRUE;
197 
198       if (glib_poll == NULL)
199         {
200           avahi_set_allocator (avahi_glib_allocator ());
201           glib_poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT);
202         }
203 
204       /* Create a new AvahiClient instance */
205       global_client = avahi_client_new (avahi_glib_poll_get (glib_poll),
206                                         AVAHI_CLIENT_NO_FAIL,
207                                         avahi_client_callback,
208                                         glib_poll,
209                                         &avahi_error);
210 
211       if (global_client == NULL)
212         {
213           g_set_error (error,
214                        G_IO_ERROR,
215                        G_IO_ERROR_FAILED,
216                        _("Error initializing Avahi: %s"),
217                        avahi_strerror (avahi_error));
218           goto out;
219         }
220     }
221 
222  out:
223 
224   return global_client;
225 }
226 
227 static gboolean
start_avahi_resolver(gpointer user_data)228 start_avahi_resolver (gpointer user_data)
229 {
230   GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (user_data);
231   AvahiClient *avahi_client;
232 
233   avahi_client = get_global_avahi_client (NULL);
234   if (avahi_client == NULL)
235     goto out;
236 
237   resolver->avahi_resolver = avahi_service_resolver_new (avahi_client,
238                                                          AVAHI_IF_UNSPEC,
239                                                          AVAHI_PROTO_UNSPEC,
240                                                          resolver->service_name,
241                                                          resolver->service_type,
242                                                          resolver->domain,
243                                                          AVAHI_PROTO_UNSPEC,
244                                                          0, /* AvahiLookupFlags */
245                                                          service_resolver_cb,
246                                                          resolver);
247 
248 out:
249   resolver->start_avahi_resolver_id = 0;
250   g_object_unref (resolver);
251   return FALSE;
252 }
253 
254 static void
ensure_avahi_resolver(GVfsDnsSdResolver * resolver)255 ensure_avahi_resolver (GVfsDnsSdResolver  *resolver)
256 {
257   if (resolver->avahi_resolver != NULL || resolver->start_avahi_resolver_id != 0)
258     return;
259 
260   resolver->start_avahi_resolver_id = g_idle_add (start_avahi_resolver, g_object_ref (resolver));
261 }
262 
263 static void
g_vfs_dns_sd_resolver_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)264 g_vfs_dns_sd_resolver_get_property (GObject    *object,
265                                     guint       prop_id,
266                                     GValue     *value,
267                                     GParamSpec *pspec)
268 {
269   GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (object);
270 
271   switch (prop_id)
272     {
273     case PROP_ENCODED_TRIPLE:
274       g_value_set_string (value, resolver->encoded_triple);
275       break;
276 
277     case PROP_REQUIRED_TXT_KEYS:
278       g_value_set_string (value, resolver->required_txt_keys);
279       break;
280 
281     case PROP_SERVICE_NAME:
282       g_value_set_string (value, resolver->service_name);
283       break;
284 
285     case PROP_SERVICE_TYPE:
286       g_value_set_string (value, resolver->service_type);
287       break;
288 
289     case PROP_DOMAIN:
290       g_value_set_string (value, resolver->domain);
291       break;
292 
293     case PROP_TIMEOUT_MSEC:
294       g_value_set_uint (value, resolver->timeout_msec);
295       break;
296 
297     case PROP_IS_RESOLVED:
298       g_value_set_boolean (value, resolver->is_resolved);
299       break;
300 
301     case PROP_ADDRESS:
302       g_value_set_string (value, resolver->address);
303       break;
304 
305     case PROP_INTERFACE:
306       g_value_set_string (value, resolver->interface);
307       break;
308 
309     case PROP_PORT:
310       g_value_set_uint (value, resolver->port);
311       break;
312 
313     case PROP_TXT_RECORDS:
314       g_value_set_boxed (value, resolver->txt_records);
315       break;
316 
317       default:
318         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
319     }
320 }
321 
322 static void
g_vfs_dns_sd_resolver_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)323 g_vfs_dns_sd_resolver_set_property (GObject      *object,
324                                     guint         prop_id,
325                                     const GValue *value,
326                                     GParamSpec   *pspec)
327 {
328   GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (object);
329 
330   switch (prop_id)
331     {
332     case PROP_ENCODED_TRIPLE:
333       resolver->encoded_triple = g_strdup (g_value_get_string (value));
334       break;
335 
336     case PROP_REQUIRED_TXT_KEYS:
337       resolver->required_txt_keys = g_strdup (g_value_get_string (value));
338       if (resolver->required_txt_keys != NULL)
339         {
340           /* TODO: maybe support escaping ',' */
341           resolver->required_txt_keys_broken_out = g_strsplit (resolver->required_txt_keys, ",", 0);
342         }
343       break;
344 
345     case PROP_SERVICE_NAME:
346       resolver->service_name = g_strdup (g_value_get_string (value));
347       break;
348 
349     case PROP_SERVICE_TYPE:
350       resolver->service_type = g_strdup (g_value_get_string (value));
351       break;
352 
353     case PROP_DOMAIN:
354       resolver->domain = g_strdup (g_value_get_string (value));
355       break;
356 
357     case PROP_TIMEOUT_MSEC:
358       resolver->timeout_msec = g_value_get_uint (value);
359       break;
360 
361     default:
362       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
363     }
364 }
365 
366 static void
g_vfs_dns_sd_resolver_finalize(GObject * object)367 g_vfs_dns_sd_resolver_finalize (GObject *object)
368 {
369   GVfsDnsSdResolver *resolver;
370 
371   resolver = G_VFS_DNS_SD_RESOLVER (object);
372 
373   g_free (resolver->encoded_triple);
374   g_free (resolver->service_name);
375   g_free (resolver->service_type);
376   g_free (resolver->domain);
377   g_free (resolver->required_txt_keys);
378   g_strfreev (resolver->required_txt_keys_broken_out);
379 
380   g_free (resolver->address);
381   g_free (resolver->interface);
382   g_strfreev (resolver->txt_records);
383 
384   if (resolver->avahi_resolver != NULL)
385     avahi_service_resolver_free (resolver->avahi_resolver);
386 
387   if (resolver->start_avahi_resolver_id != 0)
388     g_source_remove (resolver->start_avahi_resolver_id);
389 
390   resolvers = g_list_remove (resolvers, resolver);
391 
392   /* free the global avahi client for the last resolver */
393   if (resolvers == NULL)
394     {
395       free_global_avahi_client ();
396     }
397 
398   G_OBJECT_CLASS (g_vfs_dns_sd_resolver_parent_class)->finalize (object);
399 }
400 
401 static void
g_vfs_dns_sd_resolver_constructed(GObject * object)402 g_vfs_dns_sd_resolver_constructed (GObject *object)
403 {
404   GVfsDnsSdResolver *resolver;
405 
406   resolver = G_VFS_DNS_SD_RESOLVER (object);
407 
408   if (resolver->encoded_triple != NULL)
409     {
410       GError *error;
411 
412       if (resolver->service_name != NULL)
413         {
414           g_warning ("Ignoring service-name since encoded-triple is already set");
415           g_free (resolver->service_name);
416           resolver->service_name = NULL;
417         }
418 
419       if (resolver->service_type != NULL)
420         {
421           g_warning ("Ignoring service-type since encoded-triple is already set");
422           g_free (resolver->service_type);
423           resolver->service_type = NULL;
424         }
425 
426       if (resolver->domain != NULL)
427         {
428           g_warning ("Ignoring domain since encoded-triple is already set");
429           g_free (resolver->domain);
430           resolver->domain = NULL;
431         }
432 
433 
434       error = NULL;
435       if (!g_vfs_decode_dns_sd_triple (resolver->encoded_triple,
436                                        &(resolver->service_name),
437                                        &(resolver->service_type),
438                                        &(resolver->domain),
439                                        &error))
440         {
441           /* GObject construction can't fail. So whine if the triple isn't valid. */
442           g_warning ("Malformed construction data passed: %s", error->message);
443           g_error_free (error);
444 
445           g_free (resolver->encoded_triple);
446           g_free (resolver->service_name);
447           g_free (resolver->service_type);
448           g_free (resolver->domain);
449           resolver->encoded_triple = NULL;
450           resolver->service_name = NULL;
451           resolver->service_type = NULL;
452           resolver->domain = NULL;
453           goto out;
454         }
455     }
456 
457   /* Always set encoded triple to what we encode; this is because we can decode
458    * an encoded triple that isn't 100% properly URI encoded, e.g.
459    *
460    *  "davidz's public files on quad.fubar.dk._webdav._tcp.local"
461    *
462    * will be properly decoded. But we want to return a properly URI encoded triple
463    *
464    *  "davidz%27s%20public%20files%20on%20quad%2efubar%2edk._webdav._tcp.local"
465    *
466    * for e.g. setting the GMountSpec. This is useful because the use can
467    * put the former into the pathbar in a file manager and then it will
468    * be properly rewritten on mount.
469    */
470   g_free (resolver->encoded_triple);
471   resolver->encoded_triple = g_vfs_encode_dns_sd_triple (resolver->service_name,
472                                                          resolver->service_type,
473                                                          resolver->domain);
474 
475   /* start resolving immediately */
476   ensure_avahi_resolver (resolver);
477 
478   resolvers = g_list_prepend (resolvers, resolver);
479 
480  out:
481 
482   if (G_OBJECT_CLASS (g_vfs_dns_sd_resolver_parent_class)->constructed != NULL)
483     G_OBJECT_CLASS (g_vfs_dns_sd_resolver_parent_class)->constructed (object);
484 }
485 
486 static void
g_vfs_dns_sd_resolver_class_init(GVfsDnsSdResolverClass * klass)487 g_vfs_dns_sd_resolver_class_init (GVfsDnsSdResolverClass *klass)
488 {
489   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
490 
491   resolver_supports_mdns = (avahi_nss_support () > 0);
492 
493   gobject_class->get_property = g_vfs_dns_sd_resolver_get_property;
494   gobject_class->set_property = g_vfs_dns_sd_resolver_set_property;
495   gobject_class->finalize = g_vfs_dns_sd_resolver_finalize;
496   gobject_class->constructed = g_vfs_dns_sd_resolver_constructed;
497 
498   /**
499    * GVfsDnsSdResolver::changed:
500    * @resolver: The resolver emitting the signal.
501    *
502    * Emitted when resolved data changes.
503    */
504   signals[CHANGED] = g_signal_new ("changed",
505                                    G_VFS_TYPE_DNS_SD_RESOLVER,
506                                    G_SIGNAL_RUN_LAST,
507                                    G_STRUCT_OFFSET (GVfsDnsSdResolverClass, changed),
508                                    NULL,
509                                    NULL,
510                                    g_cclosure_marshal_VOID__VOID,
511                                    G_TYPE_NONE,
512                                    0);
513 
514 
515   /**
516    * GVfsDnsSdResolver:encoded-triple:
517    *
518    * The encoded DNS-SD triple for the resolver.
519    */
520   g_object_class_install_property (gobject_class,
521                                    PROP_ENCODED_TRIPLE,
522                                    g_param_spec_string ("encoded-triple",
523                                                         "Encoded triple",
524                                                         "Encoded triple",
525                                                         NULL,
526                                                         G_PARAM_CONSTRUCT_ONLY |
527                                                         G_PARAM_READWRITE |
528                                                         G_PARAM_STATIC_NAME |
529                                                         G_PARAM_STATIC_BLURB |
530                                                         G_PARAM_STATIC_NICK));
531 
532   /**
533    * GVfsDnsSdResolver:required-txt-keys:
534    *
535    * A comma separated list of keys that must appear in the TXT
536    * records in order to consider the service being resolved.
537    */
538   g_object_class_install_property (gobject_class,
539                                    PROP_REQUIRED_TXT_KEYS,
540                                    g_param_spec_string ("required-txt-keys",
541                                                         "Required TXT keys",
542                                                         "Required TXT keys",
543                                                         NULL,
544                                                         G_PARAM_CONSTRUCT_ONLY |
545                                                         G_PARAM_READWRITE |
546                                                         G_PARAM_STATIC_NAME |
547                                                         G_PARAM_STATIC_BLURB |
548                                                         G_PARAM_STATIC_NICK));
549 
550   /**
551    * GVfsDnsSdResolver:service-name:
552    *
553    * The name of the service for the resolver.
554    */
555   g_object_class_install_property (gobject_class,
556                                    PROP_SERVICE_NAME,
557                                    g_param_spec_string ("service-name",
558                                                         "Service Name",
559                                                         "Service Name",
560                                                         NULL,
561                                                         G_PARAM_CONSTRUCT_ONLY |
562                                                         G_PARAM_READWRITE |
563                                                         G_PARAM_STATIC_NAME |
564                                                         G_PARAM_STATIC_BLURB |
565                                                         G_PARAM_STATIC_NICK));
566 
567   /**
568    * GVfsDnsSdResolver:service-type:
569    *
570    * The type of the service for the resolver.
571    */
572   g_object_class_install_property (gobject_class,
573                                    PROP_SERVICE_TYPE,
574                                    g_param_spec_string ("service-type",
575                                                         "Service Type",
576                                                         "Service Type",
577                                                         NULL,
578                                                         G_PARAM_CONSTRUCT_ONLY |
579                                                         G_PARAM_READWRITE |
580                                                         G_PARAM_STATIC_NAME |
581                                                         G_PARAM_STATIC_BLURB |
582                                                         G_PARAM_STATIC_NICK));
583 
584   /**
585    * GVfsDnsSdResolver:domain:
586    *
587    * The domain for the resolver.
588    */
589   g_object_class_install_property (gobject_class,
590                                    PROP_DOMAIN,
591                                    g_param_spec_string ("domain",
592                                                         "Domain",
593                                                         "Domain",
594                                                         NULL,
595                                                         G_PARAM_CONSTRUCT_ONLY |
596                                                         G_PARAM_READWRITE |
597                                                         G_PARAM_STATIC_NAME |
598                                                         G_PARAM_STATIC_BLURB |
599                                                         G_PARAM_STATIC_NICK));
600 
601   /**
602    * GVfsDnsSdResolver:timeout-msec:
603    *
604    * Timeout in milliseconds to use when resolving.
605    */
606   g_object_class_install_property (gobject_class,
607                                    PROP_TIMEOUT_MSEC,
608                                    g_param_spec_uint ("timeout-msec",
609                                                       "Timeout in milliseconds",
610                                                       "Timeout in milliseconds",
611                                                       0,
612                                                       G_MAXUINT,
613                                                       5000,
614                                                       G_PARAM_CONSTRUCT |
615                                                       G_PARAM_READWRITE |
616                                                       G_PARAM_STATIC_NAME |
617                                                       G_PARAM_STATIC_BLURB |
618                                                       G_PARAM_STATIC_NICK));
619 
620   /**
621    * GVfsDnsSdResolver:is-resolved:
622    *
623    * Whether the service is resolved.
624    */
625   g_object_class_install_property (gobject_class,
626                                    PROP_IS_RESOLVED,
627                                    g_param_spec_boolean ("is-resolved",
628                                                          "Is resolved",
629                                                          "Is resolved",
630                                                          FALSE,
631                                                          G_PARAM_READABLE |
632                                                          G_PARAM_STATIC_NAME |
633                                                          G_PARAM_STATIC_BLURB |
634                                                          G_PARAM_STATIC_NICK));
635 
636   /**
637    * GVfsDnsSdResolver:address:
638    *
639    * The resolved address.
640    */
641   g_object_class_install_property (gobject_class,
642                                    PROP_DOMAIN,
643                                    g_param_spec_string ("address",
644                                                         "Address",
645                                                         "Address",
646                                                         NULL,
647                                                         G_PARAM_READABLE |
648                                                         G_PARAM_STATIC_NAME |
649                                                         G_PARAM_STATIC_BLURB |
650                                                         G_PARAM_STATIC_NICK));
651 
652   /**
653    * GVfsDnsSdResolver:interface:
654    *
655    * The resolved interface.
656    */
657   g_object_class_install_property (gobject_class,
658                                    PROP_INTERFACE,
659                                    g_param_spec_string ("interface",
660                                                         "Interface",
661                                                         "Interface",
662                                                         NULL,
663                                                         G_PARAM_READABLE |
664                                                         G_PARAM_STATIC_NAME |
665                                                         G_PARAM_STATIC_BLURB |
666                                                         G_PARAM_STATIC_NICK));
667 
668   /**
669    * GVfsDnsSdResolver:port:
670    *
671    * The resolved port.
672    */
673   g_object_class_install_property (gobject_class,
674                                    PROP_PORT,
675                                    g_param_spec_uint ("port",
676                                                       "Port",
677                                                       "Port",
678                                                       0,
679                                                       65536,
680                                                       0,
681                                                       G_PARAM_READABLE |
682                                                       G_PARAM_STATIC_NAME |
683                                                       G_PARAM_STATIC_BLURB |
684                                                       G_PARAM_STATIC_NICK));
685 
686   /**
687    * GVfsDnsSdResolver:txt-records:
688    *
689    * The resolved TXT records.
690    */
691   g_object_class_install_property (gobject_class,
692                                    PROP_TXT_RECORDS,
693                                    g_param_spec_boxed ("txt-records",
694                                                        "TXT Records",
695                                                        "TXT Records",
696                                                        G_TYPE_STRV,
697                                                        G_PARAM_READABLE |
698                                                        G_PARAM_STATIC_NAME |
699                                                        G_PARAM_STATIC_BLURB |
700                                                        G_PARAM_STATIC_NICK));
701 }
702 
703 static void
g_vfs_dns_sd_resolver_init(GVfsDnsSdResolver * resolver)704 g_vfs_dns_sd_resolver_init (GVfsDnsSdResolver *resolver)
705 {
706 }
707 
708 gboolean
g_vfs_dns_sd_resolver_is_resolved(GVfsDnsSdResolver * resolver)709 g_vfs_dns_sd_resolver_is_resolved (GVfsDnsSdResolver *resolver)
710 {
711   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), FALSE);
712   return resolver->is_resolved;
713 }
714 
715 const gchar *
g_vfs_dns_sd_resolver_get_encoded_triple(GVfsDnsSdResolver * resolver)716 g_vfs_dns_sd_resolver_get_encoded_triple (GVfsDnsSdResolver *resolver)
717 {
718   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
719   return resolver->encoded_triple;
720 }
721 
722 const gchar *
g_vfs_dns_sd_resolver_get_required_txt_keys(GVfsDnsSdResolver * resolver)723 g_vfs_dns_sd_resolver_get_required_txt_keys (GVfsDnsSdResolver *resolver)
724 {
725   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
726   return resolver->required_txt_keys;
727 }
728 
729 const gchar *
g_vfs_dns_sd_resolver_get_service_name(GVfsDnsSdResolver * resolver)730 g_vfs_dns_sd_resolver_get_service_name (GVfsDnsSdResolver *resolver)
731 {
732   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
733   return resolver->service_name;
734 }
735 
736 const gchar *
g_vfs_dns_sd_resolver_get_service_type(GVfsDnsSdResolver * resolver)737 g_vfs_dns_sd_resolver_get_service_type (GVfsDnsSdResolver *resolver)
738 {
739   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
740   return resolver->service_type;
741 }
742 
743 const gchar *
g_vfs_dns_sd_resolver_get_domain(GVfsDnsSdResolver * resolver)744 g_vfs_dns_sd_resolver_get_domain (GVfsDnsSdResolver *resolver)
745 {
746   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
747   return resolver->domain;
748 }
749 
750 gchar *
g_vfs_dns_sd_resolver_get_address(GVfsDnsSdResolver * resolver)751 g_vfs_dns_sd_resolver_get_address (GVfsDnsSdResolver *resolver)
752 {
753   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
754   return g_strdup (resolver->address);
755 }
756 
757 gchar *
g_vfs_dns_sd_resolver_get_interface(GVfsDnsSdResolver * resolver)758 g_vfs_dns_sd_resolver_get_interface (GVfsDnsSdResolver *resolver)
759 {
760   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
761   return g_strdup (resolver->interface);
762 }
763 
764 guint
g_vfs_dns_sd_resolver_get_port(GVfsDnsSdResolver * resolver)765 g_vfs_dns_sd_resolver_get_port (GVfsDnsSdResolver *resolver)
766 {
767   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), (guint) -1);
768   return resolver->port;
769 }
770 
771 gchar **
g_vfs_dns_sd_resolver_get_txt_records(GVfsDnsSdResolver * resolver)772 g_vfs_dns_sd_resolver_get_txt_records (GVfsDnsSdResolver *resolver)
773 {
774   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
775   return g_strdupv (resolver->txt_records);
776 }
777 
778 gchar *
g_vfs_dns_sd_resolver_lookup_txt_record(GVfsDnsSdResolver * resolver,const gchar * key)779 g_vfs_dns_sd_resolver_lookup_txt_record (GVfsDnsSdResolver *resolver,
780                                          const gchar       *key)
781 {
782   gint n;
783   gchar *result;
784   gsize key_len;
785 
786   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), NULL);
787   g_return_val_if_fail (key != NULL, NULL);
788 
789   result = NULL;
790 
791 
792   if (resolver->txt_records == NULL)
793     goto out;
794 
795   key_len = strlen (key);
796 
797   for (n = 0; resolver->txt_records[n] != NULL; n++)
798     {
799       const gchar *s = resolver->txt_records[n];
800       const gchar *p;
801 
802       p = strchr (s, '=');
803       if (p != NULL && (p - s) == key_len)
804         {
805           if (g_ascii_strncasecmp (s,
806                                    key,
807                                    p - s) == 0)
808             {
809               result = g_strdup (p + 1);
810               goto out;
811             }
812         }
813     }
814 
815  out:
816   return result;
817 }
818 
819 GVfsDnsSdResolver *
g_vfs_dns_sd_resolver_new_for_encoded_triple(const gchar * encoded_triple,const gchar * required_txt_keys)820 g_vfs_dns_sd_resolver_new_for_encoded_triple (const gchar *encoded_triple,
821                                               const gchar *required_txt_keys)
822 
823 {
824   g_return_val_if_fail (encoded_triple != NULL, NULL);
825 
826   return G_VFS_DNS_SD_RESOLVER (g_object_new (G_VFS_TYPE_DNS_SD_RESOLVER,
827                                               "encoded-triple", encoded_triple,
828                                               "required-txt-keys", required_txt_keys,
829                                               NULL));
830 }
831 
832 GVfsDnsSdResolver *
g_vfs_dns_sd_resolver_new_for_service(const gchar * service_name,const gchar * service_type,const gchar * domain,const gchar * required_txt_keys)833 g_vfs_dns_sd_resolver_new_for_service (const gchar *service_name,
834                                        const gchar *service_type,
835                                        const gchar *domain,
836                                        const gchar *required_txt_keys)
837 {
838   g_return_val_if_fail (service_name != NULL, NULL);
839   g_return_val_if_fail (service_type != NULL, NULL);
840   g_return_val_if_fail (domain != NULL, NULL);
841 
842   return G_VFS_DNS_SD_RESOLVER (g_object_new (G_VFS_TYPE_DNS_SD_RESOLVER,
843                                               "service-name", service_name,
844                                               "service-type", service_type,
845                                               "domain", domain,
846                                               "required-txt-keys", required_txt_keys,
847                                               NULL));
848 }
849 
850 static int
safe_strcmp(const char * a,const char * b)851 safe_strcmp (const char *a, const char *b)
852 {
853   if (a == NULL)
854     a = "";
855   if (b == NULL)
856     b = "";
857   return strcmp (a, b);
858 }
859 
860 static gboolean
strv_equal(char ** a,char ** b)861 strv_equal (char **a, char **b)
862 {
863   static char *dummy[1] = {NULL};
864   int n;
865   gboolean ret;
866 
867   if (a == NULL)
868     a = dummy;
869   if (b == NULL)
870     b = dummy;
871 
872   ret = FALSE;
873 
874   if (g_strv_length (a) != g_strv_length (b))
875     goto out;
876 
877   for (n = 0; a[n] != NULL && b[n] != NULL; n++)
878     {
879       if (strcmp (a[n], b[n]) != 0)
880         goto out;
881     }
882 
883   ret = TRUE;
884 
885  out:
886   return ret;
887 
888 }
889 
890 static gboolean
has_required_txt_keys(GVfsDnsSdResolver * resolver)891 has_required_txt_keys (GVfsDnsSdResolver *resolver)
892 {
893   gboolean ret;
894   int n;
895   char *value;
896 
897   ret = FALSE;
898 
899   if (resolver->required_txt_keys_broken_out != NULL)
900     {
901       for (n = 0; resolver->required_txt_keys_broken_out[n] != NULL; n++)
902         {
903           value = g_vfs_dns_sd_resolver_lookup_txt_record (resolver,
904                                                            resolver->required_txt_keys_broken_out[n]);
905           if (value == NULL)
906             {
907               /* key is missing */
908               goto out;
909             }
910           g_free (value);
911         }
912     }
913 
914   ret = TRUE;
915 
916  out:
917   return ret;
918 }
919 
920 static void
set_avahi_data(GVfsDnsSdResolver * resolver,const char * host_name,AvahiProtocol protocol,const AvahiAddress * a,AvahiIfIndex interface,uint16_t port,AvahiStringList * txt)921 set_avahi_data (GVfsDnsSdResolver    *resolver,
922                 const char           *host_name,
923                 AvahiProtocol         protocol,
924                 const AvahiAddress   *a,
925                 AvahiIfIndex          interface,
926                 uint16_t              port,
927                 AvahiStringList      *txt)
928 {
929   char *address;
930   gchar ifname[IF_NAMESIZE] = {0};
931   gboolean changed;
932   AvahiStringList *l;
933   GPtrArray *p;
934   char **txt_records;
935   gboolean is_resolved;
936 
937   changed = FALSE;
938 
939   if (resolver_supports_mdns)
940     {
941       address = g_strdup (host_name);
942     }
943   else
944     {
945       char aa[128];
946 
947       avahi_address_snprint (aa, sizeof(aa), a);
948       if (protocol == AVAHI_PROTO_INET6)
949         {
950           /* an ipv6 address, follow RFC 2732 */
951           address = g_strdup_printf ("[%s]", aa);
952         }
953       else
954         {
955           address = g_strdup (aa);
956         }
957     }
958 
959   if (safe_strcmp (resolver->address, address) != 0)
960     {
961       g_free (resolver->address);
962       resolver->address = g_strdup (address);
963       g_object_notify (G_OBJECT (resolver), "address");
964       changed = TRUE;
965     }
966 
967   g_free (address);
968 
969   if_indextoname (interface, ifname);
970   if (safe_strcmp (resolver->interface, ifname) != 0)
971     {
972       g_free (resolver->interface);
973       resolver->interface = g_strdup (ifname);
974       g_object_notify (G_OBJECT (resolver), "interface");
975       changed = TRUE;
976     }
977 
978   if (resolver->port != port)
979     {
980       resolver->port = port;
981       g_object_notify (G_OBJECT (resolver), "port");
982       changed = TRUE;
983     }
984 
985   p = g_ptr_array_new ();
986   for (l = txt; l != NULL; l = avahi_string_list_get_next (l))
987     {
988       g_ptr_array_add (p, g_strdup ((const char *) l->text));
989     }
990   g_ptr_array_add (p, NULL);
991   txt_records = (char **) g_ptr_array_free (p, FALSE);
992 
993   if (strv_equal (resolver->txt_records, txt_records))
994     {
995       g_strfreev (txt_records);
996     }
997   else
998     {
999       g_strfreev (resolver->txt_records);
1000       resolver->txt_records = txt_records;
1001       g_object_notify (G_OBJECT (resolver), "txt-records");
1002       changed = TRUE;
1003     }
1004 
1005   is_resolved = has_required_txt_keys (resolver);
1006 
1007   if (is_resolved != resolver->is_resolved)
1008     {
1009       resolver->is_resolved = is_resolved;
1010       g_object_notify (G_OBJECT (resolver), "is-resolved");
1011       changed = TRUE;
1012     }
1013 
1014   if (changed)
1015     g_signal_emit (resolver, signals[CHANGED], 0);
1016 }
1017 
1018 static void
clear_avahi_data(GVfsDnsSdResolver * resolver)1019 clear_avahi_data (GVfsDnsSdResolver *resolver)
1020 {
1021   gboolean changed;
1022 
1023   changed = FALSE;
1024 
1025   if (resolver->is_resolved)
1026     {
1027       resolver->is_resolved = FALSE;
1028       g_object_notify (G_OBJECT (resolver), "is-resolved");
1029       changed = TRUE;
1030     }
1031 
1032   if (resolver->address != NULL)
1033     {
1034       g_free (resolver->address);
1035       resolver->address = NULL;
1036       g_object_notify (G_OBJECT (resolver), "address");
1037       changed = TRUE;
1038     }
1039 
1040   if (resolver->interface != NULL)
1041     {
1042       g_free (resolver->interface);
1043       resolver->interface = NULL;
1044       g_object_notify (G_OBJECT (resolver), "interface");
1045       changed = TRUE;
1046     }
1047 
1048   if (resolver->port != 0)
1049     {
1050       resolver->port = 0;
1051       g_object_notify (G_OBJECT (resolver), "port");
1052       changed = TRUE;
1053     }
1054 
1055   if (resolver->txt_records != NULL)
1056     {
1057       resolver->txt_records = NULL;
1058       g_object_notify (G_OBJECT (resolver), "txt-records");
1059       changed = TRUE;
1060     }
1061 
1062   if (changed)
1063     g_signal_emit (resolver, signals[CHANGED], 0);
1064 }
1065 
1066 static void
service_resolver_cb(AvahiServiceResolver * avahi_resolver,AvahiIfIndex interface,AvahiProtocol protocol,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * a,uint16_t port,AvahiStringList * txt,AvahiLookupResultFlags flags,void * user_data)1067 service_resolver_cb (AvahiServiceResolver   *avahi_resolver,
1068                      AvahiIfIndex            interface,
1069                      AvahiProtocol           protocol,
1070                      AvahiResolverEvent      event,
1071                      const char             *name,
1072                      const char             *type,
1073                      const char             *domain,
1074                      const char             *host_name,
1075                      const AvahiAddress     *a,
1076                      uint16_t                port,
1077                      AvahiStringList        *txt,
1078                      AvahiLookupResultFlags  flags,
1079                      void                   *user_data)
1080 {
1081   GVfsDnsSdResolver *resolver = G_VFS_DNS_SD_RESOLVER (user_data);
1082 
1083   switch (event)
1084     {
1085     case AVAHI_RESOLVER_FOUND:
1086       set_avahi_data (resolver,
1087                       host_name,
1088                       protocol,
1089                       a,
1090                       interface,
1091                       port,
1092                       txt);
1093       break;
1094 
1095     case AVAHI_RESOLVER_FAILURE:
1096       clear_avahi_data (resolver);
1097       break;
1098     }
1099 }
1100 
1101 
1102 typedef struct
1103 {
1104   GVfsDnsSdResolver *resolver;
1105   guint timeout_id;
1106 } ResolveData;
1107 
1108 static void service_resolver_changed (GVfsDnsSdResolver *resolver, GTask *task);
1109 
1110 static void
resolve_data_free(ResolveData * data)1111 resolve_data_free (ResolveData *data)
1112 {
1113   if (data->timeout_id > 0)
1114     g_source_remove (data->timeout_id);
1115   g_signal_handlers_disconnect_by_func (data->resolver, service_resolver_changed, data);
1116   g_free (data);
1117 }
1118 
1119 static void
service_resolver_changed(GVfsDnsSdResolver * resolver,GTask * task)1120 service_resolver_changed (GVfsDnsSdResolver *resolver,
1121                           GTask *task)
1122 {
1123   ResolveData *data = g_task_get_task_data (task);
1124 
1125   if (resolver->is_resolved)
1126     {
1127       g_task_return_boolean (task, TRUE);
1128       g_object_unref (task);
1129     }
1130   else
1131     {
1132       if (data->resolver->address != NULL)
1133         {
1134           /* keep going until timeout if we're missing TXT records */
1135         }
1136       else
1137         {
1138           g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
1139           /* Translators:
1140            * - the first %s refers to the service type
1141            * - the second %s refers to the service name
1142            * - the third %s refers to the domain
1143            */
1144                                    _("Error resolving “%s” service “%s” on domain “%s”"),
1145                                    data->resolver->service_type,
1146                                    data->resolver->service_name,
1147                                    data->resolver->domain);
1148           g_object_unref (task);
1149         }
1150     }
1151 }
1152 
1153 static gboolean
service_resolver_timed_out(GTask * task)1154 service_resolver_timed_out (GTask *task)
1155 {
1156   ResolveData *data = g_task_get_task_data (task);
1157 
1158   if (data->resolver->address != NULL)
1159     {
1160       /* special case if one of the required TXT records are missing */
1161       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
1162       /* Translators:
1163        * - the first %s refers to the service type
1164        * - the second %s refers to the service name
1165        * - the third %s refers to the domain
1166        * - the fourth %s refers to the required TXT keys
1167        */
1168                                _("Error resolving “%s” service “%s” on domain “%s”. "
1169                                  "One or more TXT records are missing. Keys required: “%s”."),
1170                                data->resolver->service_type,
1171                                data->resolver->service_name,
1172                                data->resolver->domain,
1173                                data->resolver->required_txt_keys);
1174     }
1175   else
1176     {
1177       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
1178       /* Translators:
1179        * - the first %s refers to the service type
1180        * - the second %s refers to the service name
1181        * - the third %s refers to the domain
1182        */
1183                                _("Timed out resolving “%s” service “%s” on domain “%s”"),
1184                                data->resolver->service_type,
1185                                data->resolver->service_name,
1186                                data->resolver->domain);
1187     }
1188 
1189   data->timeout_id = 0;
1190   g_object_unref (task);
1191 
1192   return FALSE;
1193 }
1194 
1195 gboolean
g_vfs_dns_sd_resolver_resolve_finish(GVfsDnsSdResolver * resolver,GAsyncResult * res,GError ** error)1196 g_vfs_dns_sd_resolver_resolve_finish (GVfsDnsSdResolver  *resolver,
1197                                       GAsyncResult       *res,
1198                                       GError            **error)
1199 {
1200   g_return_val_if_fail (g_task_is_valid (res, resolver), FALSE);
1201   g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_dns_sd_resolver_resolve), FALSE);
1202 
1203   return g_task_propagate_boolean (G_TASK (res), error);
1204 }
1205 
1206 void
g_vfs_dns_sd_resolver_resolve(GVfsDnsSdResolver * resolver,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1207 g_vfs_dns_sd_resolver_resolve (GVfsDnsSdResolver  *resolver,
1208                                GCancellable       *cancellable,
1209                                GAsyncReadyCallback callback,
1210                                gpointer            user_data)
1211 {
1212   ResolveData *data;
1213   GTask *task;
1214 
1215   g_return_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver));
1216 
1217   task = g_task_new (resolver, cancellable, callback, user_data);
1218   g_task_set_source_tag (task, g_vfs_dns_sd_resolver_resolve);
1219 
1220   if (resolver->service_type == NULL)
1221     {
1222       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
1223                                _("Error initializing Avahi resolver"));
1224       g_object_unref (task);
1225       return;
1226     }
1227 
1228   if (resolver->is_resolved)
1229     {
1230       g_task_return_boolean (task, TRUE);
1231       g_object_unref (task);
1232       return;
1233     }
1234 
1235   ensure_avahi_resolver (resolver);
1236 
1237   data = g_new0 (ResolveData, 1);
1238   data->resolver = resolver;
1239   data->timeout_id = g_timeout_add (resolver->timeout_msec,
1240                                     (GSourceFunc) service_resolver_timed_out,
1241                                     task);
1242 
1243   g_task_set_task_data (task, data, (GDestroyNotify) resolve_data_free);
1244 
1245   g_signal_connect (resolver,
1246                     "changed",
1247                     (GCallback) service_resolver_changed,
1248                     task);
1249 }
1250 
1251 
1252 typedef struct
1253 {
1254   GMutex mutex;
1255   GCond cond;
1256   gboolean done;
1257   GError *error;
1258   gboolean ret;
1259 } ResolveDataSync;
1260 
1261 static void
resolve_sync_cb(GVfsDnsSdResolver * resolver,GAsyncResult * res,ResolveDataSync * data)1262 resolve_sync_cb (GVfsDnsSdResolver *resolver,
1263                  GAsyncResult      *res,
1264                  ResolveDataSync   *data)
1265 {
1266   data->ret = g_vfs_dns_sd_resolver_resolve_finish (resolver,
1267                                                     res,
1268                                                     &(data->error));
1269   g_mutex_lock (&data->mutex);
1270   data->done = TRUE;
1271   g_cond_signal (&data->cond);
1272   g_mutex_unlock (&data->mutex);
1273 }
1274 
1275 /* Do not call from the global main loop thread. */
1276 gboolean
g_vfs_dns_sd_resolver_resolve_sync(GVfsDnsSdResolver * resolver,GCancellable * cancellable,GError ** error)1277 g_vfs_dns_sd_resolver_resolve_sync (GVfsDnsSdResolver  *resolver,
1278                                     GCancellable       *cancellable,
1279                                     GError            **error)
1280 {
1281   ResolveDataSync *data;
1282   gboolean ret;
1283 
1284   g_return_val_if_fail (G_VFS_IS_DNS_SD_RESOLVER (resolver), FALSE);
1285 
1286   data = g_new0 (ResolveDataSync, 1);
1287   g_cond_init (&data->cond);
1288   g_mutex_init (&data->mutex);
1289 
1290   g_mutex_lock (&data->mutex);
1291   g_vfs_dns_sd_resolver_resolve (resolver,
1292                                  cancellable,
1293                                  (GAsyncReadyCallback) resolve_sync_cb,
1294                                  data);
1295 
1296   while (!data->done)
1297     g_cond_wait (&data->cond, &data->mutex);
1298   g_mutex_unlock (&data->mutex);
1299 
1300   ret = data->ret;
1301   if (data->error != NULL)
1302     g_propagate_error (error, data->error);
1303 
1304   g_mutex_clear (&data->mutex);
1305   g_cond_clear (&data->cond);
1306   g_free (data);
1307 
1308   return ret;
1309 }
1310