1 /*
2  * ga-service-resolver.c - Source for GaServiceResolver
3  * Copyright (C) 2006-2007 Collabora Ltd.
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.1 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 Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 
27 #include "ga-service-resolver.h"
28 #include "signals-marshal.h"
29 
30 #include "ga-error.h"
31 
32 #include "ga-enums.h"
33 #include "ga-enums-enumtypes.h"
34 
35 #include <avahi-client/lookup.h>
36 
37 G_DEFINE_TYPE(GaServiceResolver, ga_service_resolver, G_TYPE_OBJECT)
38 
39 /* signal enum */
40 enum {
41     FOUND,
42     FAILURE,
43     LAST_SIGNAL
44 };
45 
46 static guint signals[LAST_SIGNAL] = { 0 };
47 
48 /* properties */
49 enum {
50     PROP_PROTOCOL = 1,
51     PROP_IFINDEX,
52     PROP_NAME,
53     PROP_TYPE,
54     PROP_DOMAIN,
55     PROP_FLAGS,
56     PROP_APROTOCOL
57 };
58 
59 /* private structure */
60 typedef struct _GaServiceResolverPrivate GaServiceResolverPrivate;
61 
62 struct _GaServiceResolverPrivate {
63     GaClient *client;
64     AvahiServiceResolver *resolver;
65     AvahiIfIndex interface;
66     AvahiProtocol protocol;
67     AvahiAddress address;
68     uint16_t port;
69     char *name;
70     char *type;
71     char *domain;
72     AvahiProtocol aprotocol;
73     AvahiLookupFlags flags;
74     gboolean dispose_has_run;
75 };
76 
77 #define GA_SERVICE_RESOLVER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), GA_TYPE_SERVICE_RESOLVER, GaServiceResolverPrivate))
78 
ga_service_resolver_init(GaServiceResolver * obj)79 static void ga_service_resolver_init(GaServiceResolver * obj) {
80     GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(obj);
81 
82     /* allocate any data required by the object here */
83     priv->client = NULL;
84     priv->resolver = NULL;
85     priv->name = NULL;
86     priv->type = NULL;
87     priv->domain = NULL;
88     priv->port = 0;
89 }
90 
91 static void ga_service_resolver_dispose(GObject * object);
92 static void ga_service_resolver_finalize(GObject * object);
93 
ga_service_resolver_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)94 static void ga_service_resolver_set_property(GObject * object,
95                                  guint property_id,
96                                  const GValue * value, GParamSpec * pspec) {
97     GaServiceResolver *resolver = GA_SERVICE_RESOLVER(object);
98     GaServiceResolverPrivate *priv =
99             GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
100 
101     g_assert(priv->resolver == NULL);
102     switch (property_id) {
103         case PROP_PROTOCOL:
104             priv->protocol = g_value_get_enum(value);
105             break;
106         case PROP_APROTOCOL:
107             priv->aprotocol = g_value_get_enum(value);
108             break;
109         case PROP_IFINDEX:
110             priv->interface = g_value_get_int(value);
111             break;
112         case PROP_NAME:
113             priv->name = g_strdup(g_value_get_string(value));
114             break;
115         case PROP_TYPE:
116             priv->type = g_strdup(g_value_get_string(value));
117             break;
118         case PROP_DOMAIN:
119             priv->domain = g_strdup(g_value_get_string(value));
120             break;
121         case PROP_FLAGS:
122             priv->flags = g_value_get_enum(value);
123             break;
124         default:
125             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
126             break;
127     }
128 }
129 
ga_service_resolver_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)130 static void ga_service_resolver_get_property(GObject * object,
131                                  guint property_id,
132                                  GValue * value, GParamSpec * pspec) {
133     GaServiceResolver *resolver = GA_SERVICE_RESOLVER(object);
134     GaServiceResolverPrivate *priv =
135             GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
136 
137     switch (property_id) {
138         case PROP_APROTOCOL:
139             g_value_set_enum(value, priv->aprotocol);
140             break;
141         case PROP_PROTOCOL:
142             g_value_set_enum(value, priv->protocol);
143             break;
144         case PROP_IFINDEX:
145             g_value_set_int(value, priv->interface);
146             break;
147         case PROP_NAME:
148             g_value_set_string(value, priv->name);
149             break;
150         case PROP_TYPE:
151             g_value_set_string(value, priv->type);
152             break;
153         case PROP_DOMAIN:
154             g_value_set_string(value, priv->domain);
155             break;
156         case PROP_FLAGS:
157             g_value_set_enum(value, priv->flags);
158             break;
159         default:
160             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
161             break;
162     }
163 }
164 
165 
ga_service_resolver_class_init(GaServiceResolverClass * ga_service_resolver_class)166 static void ga_service_resolver_class_init(GaServiceResolverClass *
167                                ga_service_resolver_class) {
168     GObjectClass *object_class = G_OBJECT_CLASS(ga_service_resolver_class);
169     GParamSpec *param_spec;
170 
171     g_type_class_add_private(ga_service_resolver_class,
172                              sizeof (GaServiceResolverPrivate));
173 
174     object_class->set_property = ga_service_resolver_set_property;
175     object_class->get_property = ga_service_resolver_get_property;
176 
177     object_class->dispose = ga_service_resolver_dispose;
178     object_class->finalize = ga_service_resolver_finalize;
179 
180     signals[FOUND] =
181             g_signal_new("found",
182                          G_OBJECT_CLASS_TYPE(ga_service_resolver_class),
183                          G_SIGNAL_RUN_LAST,
184                          0,
185                          NULL, NULL,
186                          _ga_signals_marshal_VOID__INT_ENUM_STRING_STRING_STRING_STRING_POINTER_INT_POINTER_INT,
187                          G_TYPE_NONE, 10,
188                          G_TYPE_INT,
189                          GA_TYPE_PROTOCOL,
190                          G_TYPE_STRING,
191                          G_TYPE_STRING,
192                          G_TYPE_STRING,
193                          G_TYPE_STRING,
194                          G_TYPE_POINTER,
195                          G_TYPE_INT,
196                          G_TYPE_POINTER, GA_TYPE_LOOKUP_RESULT_FLAGS);
197 
198     signals[FAILURE] =
199             g_signal_new("failure",
200                          G_OBJECT_CLASS_TYPE(ga_service_resolver_class),
201                          G_SIGNAL_RUN_LAST,
202                          0,
203                          NULL, NULL,
204                          g_cclosure_marshal_VOID__POINTER,
205                          G_TYPE_NONE, 1, G_TYPE_POINTER);
206 
207     param_spec = g_param_spec_enum("protocol", "Avahi protocol to resolve on",
208                                    "Avahi protocol to resolve on",
209                                    GA_TYPE_PROTOCOL,
210                                    GA_PROTOCOL_UNSPEC,
211                                    G_PARAM_READWRITE |
212                                    G_PARAM_STATIC_NAME |
213                                    G_PARAM_STATIC_BLURB);
214     g_object_class_install_property(object_class, PROP_PROTOCOL, param_spec);
215 
216     param_spec = g_param_spec_enum("aprotocol", "Address protocol",
217                                    "Avahi protocol of the address to be resolved",
218                                    GA_TYPE_PROTOCOL,
219                                    GA_PROTOCOL_UNSPEC,
220                                    G_PARAM_READWRITE |
221                                    G_PARAM_STATIC_NAME |
222                                    G_PARAM_STATIC_BLURB);
223     g_object_class_install_property(object_class, PROP_APROTOCOL, param_spec);
224 
225     param_spec = g_param_spec_int("interface", "interface index",
226                                   "Interface use for resolver",
227                                   AVAHI_IF_UNSPEC,
228                                   G_MAXINT,
229                                   AVAHI_IF_UNSPEC,
230                                   G_PARAM_READWRITE |
231                                   G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB);
232     g_object_class_install_property(object_class, PROP_IFINDEX, param_spec);
233 
234     param_spec = g_param_spec_string("name", "service name",
235                                      "name to resolve",
236                                      NULL,
237                                      G_PARAM_READWRITE |
238                                      G_PARAM_STATIC_NAME |
239                                      G_PARAM_STATIC_BLURB);
240     g_object_class_install_property(object_class, PROP_NAME, param_spec);
241 
242     param_spec = g_param_spec_string("type", "service type",
243                                      "Service type to browse for",
244                                      NULL,
245                                      G_PARAM_READWRITE |
246                                      G_PARAM_STATIC_NAME |
247                                      G_PARAM_STATIC_BLURB);
248     g_object_class_install_property(object_class, PROP_TYPE, param_spec);
249 
250     param_spec = g_param_spec_string("domain", "service domain",
251                                      "Domain to browse in",
252                                      NULL,
253                                      G_PARAM_READWRITE |
254                                      G_PARAM_STATIC_NAME |
255                                      G_PARAM_STATIC_BLURB);
256     g_object_class_install_property(object_class, PROP_DOMAIN, param_spec);
257 
258     param_spec = g_param_spec_enum("flags", "Lookup flags for the resolver",
259                                    "Resolver lookup flags",
260                                    GA_TYPE_LOOKUP_FLAGS,
261                                    GA_LOOKUP_NO_FLAGS,
262                                    G_PARAM_READWRITE |
263                                    G_PARAM_STATIC_NAME |
264                                    G_PARAM_STATIC_BLURB);
265     g_object_class_install_property(object_class, PROP_FLAGS, param_spec);
266 }
267 
ga_service_resolver_dispose(GObject * object)268 void ga_service_resolver_dispose(GObject * object) {
269     GaServiceResolver *self = GA_SERVICE_RESOLVER(object);
270     GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(self);
271 
272     if (priv->dispose_has_run)
273         return;
274 
275     priv->dispose_has_run = TRUE;
276 
277     if (priv->client)
278         g_object_unref(priv->client);
279     priv->client = NULL;
280 
281     if (priv->resolver)
282         avahi_service_resolver_free(priv->resolver);
283     priv->resolver = NULL;
284 
285     /* release any references held by the object here */
286 
287     if (G_OBJECT_CLASS(ga_service_resolver_parent_class)->dispose)
288         G_OBJECT_CLASS(ga_service_resolver_parent_class)->dispose(object);
289 }
290 
ga_service_resolver_finalize(GObject * object)291 void ga_service_resolver_finalize(GObject * object) {
292     GaServiceResolver *self = GA_SERVICE_RESOLVER(object);
293     GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(self);
294 
295     /* free any data held directly by the object here */
296     g_free(priv->name);
297     priv->name = NULL;
298 
299     g_free(priv->type);
300     priv->type = NULL;
301 
302     g_free(priv->domain);
303     priv->domain = NULL;
304 
305     G_OBJECT_CLASS(ga_service_resolver_parent_class)->finalize(object);
306 }
307 
_avahi_service_resolver_cb(AVAHI_GCC_UNUSED AvahiServiceResolver * 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 * userdata)308 static void _avahi_service_resolver_cb(AVAHI_GCC_UNUSED AvahiServiceResolver * resolver,
309                            AvahiIfIndex interface,
310                            AvahiProtocol protocol,
311                            AvahiResolverEvent event,
312                            const char *name, const char *type,
313                            const char *domain, const char *host_name,
314                            const AvahiAddress * a,
315                            uint16_t port,
316                            AvahiStringList * txt,
317                            AvahiLookupResultFlags flags, void *userdata) {
318     GaServiceResolver *self = GA_SERVICE_RESOLVER(userdata);
319     GaServiceResolverPrivate *priv = GA_SERVICE_RESOLVER_GET_PRIVATE(self);
320 
321     switch (event) {
322         case AVAHI_RESOLVER_FOUND:{
323                 /* FIXME: Double check if this address is always the same */
324                 priv->address = *a;
325                 priv->port = port;
326                 g_signal_emit(self, signals[FOUND], 0,
327                               interface, protocol,
328                               name, type,
329                               domain, host_name, a, port, txt, flags);
330                 break;
331             }
332         case AVAHI_RESOLVER_FAILURE:{
333                 GError *error;
334                 int aerrno = avahi_client_errno(priv->client->avahi_client);
335                 error = g_error_new(GA_ERROR, aerrno,
336                                     "Resolving failed: %s",
337                                     avahi_strerror(aerrno));
338                 g_signal_emit(self, signals[FAILURE], 0, error);
339                 g_error_free(error);
340                 break;
341             }
342     }
343 }
344 
345 
ga_service_resolver_new(AvahiIfIndex interface,AvahiProtocol protocol,const gchar * name,const gchar * type,const gchar * domain,AvahiProtocol address_protocol,GaLookupFlags flags)346 GaServiceResolver *ga_service_resolver_new(AvahiIfIndex interface,
347                                            AvahiProtocol protocol,
348                                            const gchar * name,
349                                            const gchar * type,
350                                            const gchar * domain,
351                                            AvahiProtocol address_protocol,
352                                            GaLookupFlags flags) {
353     return g_object_new(GA_TYPE_SERVICE_RESOLVER, "interface", interface,
354                         "protocol", protocol, "name", name, "type", type,
355                         "domain", domain, "aprotocol", address_protocol,
356                         "flags", flags, NULL);
357 }
358 
ga_service_resolver_attach(GaServiceResolver * resolver,GaClient * client,GError ** error)359 gboolean ga_service_resolver_attach(GaServiceResolver * resolver,
360                            GaClient * client, GError ** error) {
361     GaServiceResolverPrivate *priv =
362             GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
363 
364     g_assert(client != NULL);
365     g_object_ref(client);
366 
367     priv->client = client;
368 
369     priv->resolver = avahi_service_resolver_new(client->avahi_client,
370                                                 priv->interface,
371                                                 priv->protocol,
372                                                 priv->name,
373                                                 priv->type, priv->domain,
374                                                 priv->aprotocol,
375                                                 priv->flags,
376                                                 _avahi_service_resolver_cb,
377                                                 resolver);
378     if (priv->resolver == NULL) {
379         if (error != NULL) {
380             int aerrno = avahi_client_errno(client->avahi_client);
381             *error = g_error_new(GA_ERROR, aerrno,
382                                  "Attaching group failed: %s",
383                                  avahi_strerror(aerrno));
384         }
385 /*         printf("Failed to add resolver\n"); */
386         return FALSE;
387     }
388     return TRUE;
389 }
390 
ga_service_resolver_get_address(GaServiceResolver * resolver,AvahiAddress * address,uint16_t * port)391 gboolean ga_service_resolver_get_address(GaServiceResolver * resolver,
392                                 AvahiAddress * address, uint16_t * port) {
393     GaServiceResolverPrivate *priv =
394             GA_SERVICE_RESOLVER_GET_PRIVATE(resolver);
395     if (priv->port == 0) {
396 /*         printf("PORT == 0\n"); */
397         return FALSE;
398     }
399 
400     *address = priv->address;
401     *port = priv->port;
402     return TRUE;
403 }
404