1 /*
2  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
3  * Copyright (C) 2009 Nokia Corporation.
4  * Copyright (C) 2010 Jens Georg <mail@jensge.org>
5  *
6  * Author: Jorn Baayen <jorn@openedhand.com>
7  *         Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
8  *                               <zeeshan.ali@nokia.com>
9  *         Jens Georg <mail@jensge.org>
10  *
11  * SPDX-License-Identifier: LGPL-2.1-or-later
12  *
13  */
14 
15 #include <config.h>
16 
17 #include "gssdp-socket-functions.h"
18 #include "gssdp-socket-source.h"
19 #include "gssdp-protocol.h"
20 #include "gssdp-error.h"
21 
22 #include <glib.h>
23 #include <gio/gio.h>
24 
25 struct _GSSDPSocketSource {
26         GObject parent;
27 };
28 
29 struct _GSSDPSocketSourceClass {
30         GObjectClass parent_class;
31 };
32 
33 struct _GSSDPSocketSourcePrivate {
34         GSource              *source;
35         GSocket              *socket;
36         GSSDPSocketSourceType type;
37 
38         GInetAddress         *address;
39         char                 *device_name;
40         gint                  index;
41         guint                 ttl;
42         guint                 port;
43 };
44 typedef struct _GSSDPSocketSourcePrivate GSSDPSocketSourcePrivate;
45 
46 static void
47 gssdp_socket_source_initable_init (gpointer g_iface,
48                                    gpointer iface_data);
49 
50 G_DEFINE_TYPE_EXTENDED (GSSDPSocketSource,
51                         gssdp_socket_source,
52                         G_TYPE_OBJECT,
53                         0,
54                         G_ADD_PRIVATE (GSSDPSocketSource)
55                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
56                                     gssdp_socket_source_initable_init));
57 
58 enum {
59     PROP_0,
60     PROP_TYPE,
61     PROP_ADDRESS,
62     PROP_TTL,
63     PROP_PORT,
64     PROP_IFA_NAME,
65     PROP_IFA_IDX
66 };
67 
68 static void
gssdp_socket_source_init(GSSDPSocketSource * self)69 gssdp_socket_source_init (GSSDPSocketSource *self)
70 {
71 }
72 
73 static gboolean
74 gssdp_socket_source_do_init (GInitable     *initable,
75                              GCancellable  *cancellable,
76                              GError       **error);
77 
78 static void
gssdp_socket_source_initable_init(gpointer g_iface,G_GNUC_UNUSED gpointer iface_data)79 gssdp_socket_source_initable_init (gpointer               g_iface,
80                                    G_GNUC_UNUSED gpointer iface_data)
81 {
82         GInitableIface *iface = (GInitableIface *)g_iface;
83         iface->init = gssdp_socket_source_do_init;
84 }
85 
86 static void
gssdp_socket_source_get_property(GObject * object,guint property_id,G_GNUC_UNUSED GValue * value,GParamSpec * pspec)87 gssdp_socket_source_get_property (GObject              *object,
88                                   guint                 property_id,
89                                   G_GNUC_UNUSED GValue *value,
90                                   GParamSpec           *pspec)
91 {
92         /* All properties are construct-only, write-only */
93         switch (property_id) {
94         default:
95                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96                 break;
97         }
98 }
99 
100 static void
gssdp_socket_source_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)101 gssdp_socket_source_set_property (GObject          *object,
102                                   guint             property_id,
103                                   const GValue     *value,
104                                   GParamSpec       *pspec)
105 {
106         GSSDPSocketSource *self;
107         GSSDPSocketSourcePrivate *priv;
108 
109         self = GSSDP_SOCKET_SOURCE (object);
110         priv = gssdp_socket_source_get_instance_private (self);
111 
112         switch (property_id) {
113         case PROP_TYPE:
114                 priv->type = g_value_get_int (value);
115                 break;
116         case PROP_IFA_NAME:
117                 priv->device_name = g_value_dup_string (value);
118                 break;
119         case PROP_ADDRESS:
120                 priv->address = g_value_dup_object (value);
121                 break;
122         case PROP_TTL:
123                 priv->ttl = g_value_get_uint (value);
124                 break;
125         case PROP_PORT:
126                 priv->port = g_value_get_uint (value);
127                 break;
128         case PROP_IFA_IDX:
129                 priv->index = g_value_get_int (value);
130                 break;
131         default:
132                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
133                 break;
134         }
135 }
136 
137 /**
138  * gssdp_socket_source_new
139  *
140  * Return value: A new #GSSDPSocketSource
141  **/
142 GSSDPSocketSource *
gssdp_socket_source_new(GSSDPSocketSourceType type,GInetAddress * address,guint ttl,const char * device_name,guint index,GError ** error)143 gssdp_socket_source_new (GSSDPSocketSourceType type,
144                          GInetAddress         *address,
145                          guint                 ttl,
146                          const char           *device_name,
147                          guint                 index,
148                          GError              **error)
149 {
150         return g_initable_new (GSSDP_TYPE_SOCKET_SOURCE,
151                                NULL,
152                                error,
153                                "type",
154                                type,
155                                "address",
156                                address,
157                                "ttl",
158                                ttl,
159                                "device-name",
160                                device_name,
161                                "index",
162                                index,
163                                NULL);
164 }
165 
166 static gboolean
gssdp_socket_source_do_init(GInitable * initable,G_GNUC_UNUSED GCancellable * cancellable,GError ** error)167 gssdp_socket_source_do_init (GInitable                   *initable,
168                              G_GNUC_UNUSED GCancellable  *cancellable,
169                              GError                     **error)
170 {
171         GSSDPSocketSource *self = NULL;
172         GSSDPSocketSourcePrivate *priv = NULL;
173         GSocketAddress *bind_address = NULL;
174         GInetAddress *group = NULL;
175         GError *inner_error = NULL;
176         GSocketFamily family;
177         gboolean success = FALSE;
178         gboolean link_local = FALSE;
179 
180         self = GSSDP_SOCKET_SOURCE (initable);
181         priv = gssdp_socket_source_get_instance_private (self);
182 
183         family = g_inet_address_get_family (priv->address);
184 
185         if (family == G_SOCKET_FAMILY_IPV4)
186                 group = g_inet_address_new_from_string (SSDP_ADDR);
187         else {
188                 /* IPv6 */
189                 /* According to Annex.A, we need to check the scope of the
190                  * address to use the proper multicast group */
191                 if (g_inet_address_get_is_link_local (priv->address)) {
192                             group = g_inet_address_new_from_string (SSDP_V6_LL);
193                             link_local = TRUE;
194                 } else {
195                             group = g_inet_address_new_from_string (SSDP_V6_SL);
196                 }
197         }
198 
199 
200         /* Create socket */
201         priv->socket = g_socket_new (family,
202                                      G_SOCKET_TYPE_DATAGRAM,
203                                      G_SOCKET_PROTOCOL_UDP,
204                                      &inner_error);
205 
206         if (!priv->socket) {
207                 g_propagate_prefixed_error (error,
208                                             inner_error,
209                                             "Could not create socket");
210 
211                 goto error;
212         }
213 
214         /* Enable broadcasting */
215         g_socket_set_broadcast (priv->socket, TRUE);
216 
217         if (!gssdp_socket_enable_info (priv->socket,
218                                        family,
219                                        TRUE,
220                                        &inner_error)) {
221                 g_propagate_prefixed_error (error,
222                                             inner_error,
223                                             "Failed to enable info messages");
224 
225                 goto error;
226         }
227 
228         /* TTL */
229         if (priv->ttl == 0) {
230                 /* UDA/1.0 says 4, UDA/1.1 says 2 */
231                 priv->ttl = 4;
232                 if (family == G_SOCKET_FAMILY_IPV6) {
233                         /* UDA 2.0, Annex A says 10 hops */
234                         priv->ttl = 10;
235                 }
236         }
237 
238         g_socket_set_multicast_ttl (priv->socket, priv->ttl);
239 
240 
241         /* Set up additional things according to the type of socket desired */
242         if (priv->type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) {
243                 /* Enable multicast loopback */
244                 g_socket_set_multicast_loopback (priv->socket, TRUE);
245 
246 
247 #ifdef G_OS_WIN32
248                 bind_address = g_inet_socket_address_new (priv->address,
249                                                           SSDP_PORT);
250 #else
251                 bind_address = g_object_new (G_TYPE_INET_SOCKET_ADDRESS,
252                                              "address", group,
253                                              "port", SSDP_PORT,
254                                              "scope-id", priv->index,
255                                              NULL);
256 #endif
257         } else {
258                 guint port = SSDP_PORT;
259 
260                 if (family != G_SOCKET_FAMILY_IPV6 ||
261                     (!g_inet_address_get_is_loopback (priv->address))) {
262 
263                         if (!gssdp_socket_mcast_interface_set (priv->socket,
264                                                 priv->address,
265                                                 (guint32) priv->index,
266                                                 &inner_error)) {
267                                 g_propagate_prefixed_error (
268                                                 error,
269                                                 inner_error,
270                                                 "Failed to set multicast interface");
271 
272                                 goto error;
273                         }
274                 }
275 
276                 /* Use user-supplied or random port for the socket source used
277                  * by M-SEARCH */
278                 if (priv->type == GSSDP_SOCKET_SOURCE_TYPE_SEARCH)
279                         port = priv->port;
280 
281                 if (link_local) {
282                     bind_address = g_object_new (G_TYPE_INET_SOCKET_ADDRESS,
283                                                  "address", priv->address,
284                                                  "port", port,
285                                                  "scope-id", priv->index,
286                                                  NULL);
287                 } else {
288                     bind_address = g_inet_socket_address_new (priv->address,
289                                                               port);
290                 }
291 
292         }
293 
294         /* Normally g_socket_bind does this, but it is disabled on
295          * windows since SO_REUSEADDR has different semantics
296          * there, also we nees SO_REUSEPORT on OpenBSD. This is a nop
297          * everywhere else.
298          */
299         if (!gssdp_socket_reuse_address (priv->socket,
300                                          TRUE,
301                                          &inner_error)) {
302                 g_propagate_prefixed_error (
303                                 error,
304                                 inner_error,
305                                 "Failed to enable reuse");
306 
307                 goto error;
308         }
309 
310         /* Bind to requested port and address */
311         if (!g_socket_bind (priv->socket,
312                             bind_address,
313                             TRUE,
314                             &inner_error)) {
315                 g_propagate_prefixed_error (error,
316                                             inner_error,
317                                             "Failed to bind socket");
318 
319                 goto error;
320         }
321 
322         if (priv->type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) {
323                 /* The 4th argument 'iface_name' can't be NULL even though Glib API doc says you
324                  * can. 'NULL' will fail the test.
325                  */
326                 if (!g_socket_join_multicast_group (priv->socket,
327                                                     group,
328                                                     FALSE,
329                                                     priv->device_name,  /*   e.g. 'lo' */
330                                                     &inner_error)) {
331                         char *address = g_inet_address_to_string (group);
332                         g_propagate_prefixed_error (error,
333                                                     inner_error,
334                                                     "Failed to join group %s",
335                                                     address);
336                         g_free (address);
337 
338                         goto error;
339                 }
340         }
341 
342         priv->source = g_socket_create_source (priv->socket,
343                                                G_IO_IN | G_IO_ERR,
344                                                NULL);
345         success = TRUE;
346 
347 error:
348         if (bind_address != NULL)
349                 g_object_unref (bind_address);
350         if (group != NULL)
351                 g_object_unref (group);
352         if (!success)
353                 /* Be aware that inner_error has already been free'd by
354                  * g_propagate_error(), so we cannot access its contents
355                  * anymore. */
356                 if (error == NULL)
357                         g_warning ("Failed to create socket source");
358 
359         return success;
360 }
361 
362 GSocket *
gssdp_socket_source_get_socket(GSSDPSocketSource * socket_source)363 gssdp_socket_source_get_socket (GSSDPSocketSource *socket_source)
364 {
365         GSSDPSocketSourcePrivate *priv;
366         g_return_val_if_fail (socket_source != NULL, NULL);
367         priv = gssdp_socket_source_get_instance_private (socket_source);
368 
369         return priv->socket;
370 }
371 
372 void
gssdp_socket_source_set_callback(GSSDPSocketSource * self,GSourceFunc callback,gpointer user_data)373 gssdp_socket_source_set_callback (GSSDPSocketSource *self,
374                                   GSourceFunc        callback,
375                                   gpointer           user_data)
376 {
377         GSSDPSocketSourcePrivate *priv;
378         g_return_if_fail (self != NULL);
379         g_return_if_fail (GSSDP_IS_SOCKET_SOURCE (self));
380         priv = gssdp_socket_source_get_instance_private (self);
381 
382         g_source_set_callback (priv->source, callback, user_data, NULL);
383 }
384 
385 void
gssdp_socket_source_attach(GSSDPSocketSource * self)386 gssdp_socket_source_attach (GSSDPSocketSource *self)
387 {
388         GSSDPSocketSourcePrivate *priv;
389         g_return_if_fail (self != NULL);
390         g_return_if_fail (GSSDP_IS_SOCKET_SOURCE (self));
391         priv = gssdp_socket_source_get_instance_private (self);
392 
393         g_source_attach (priv->source,
394                          g_main_context_get_thread_default ());
395 }
396 
397 static void
gssdp_socket_source_dispose(GObject * object)398 gssdp_socket_source_dispose (GObject *object)
399 {
400         GSSDPSocketSource *self;
401         GSSDPSocketSourcePrivate *priv;
402 
403         self = GSSDP_SOCKET_SOURCE (object);
404         priv = gssdp_socket_source_get_instance_private (self);
405 
406         if (priv->source != NULL) {
407                 g_source_destroy (priv->source);
408                 g_source_unref (priv->source);
409                 priv->source = NULL;
410         }
411 
412         if (priv->socket != NULL) {
413                 g_socket_close (priv->socket, NULL);
414                 g_object_unref (priv->socket);
415                 priv->socket = NULL;
416         }
417 
418         G_OBJECT_CLASS (gssdp_socket_source_parent_class)->dispose (object);
419 }
420 
421 static void
gssdp_socket_source_finalize(GObject * object)422 gssdp_socket_source_finalize (GObject *object)
423 {
424         GSSDPSocketSource *self;
425         GSSDPSocketSourcePrivate *priv;
426 
427         self = GSSDP_SOCKET_SOURCE (object);
428         priv = gssdp_socket_source_get_instance_private (self);
429 
430         g_clear_object (&priv->address);
431 
432         if (priv->device_name != NULL) {
433                 g_free (priv->device_name);
434                 priv->device_name = NULL;
435         }
436 
437         G_OBJECT_CLASS (gssdp_socket_source_parent_class)->finalize (object);
438 }
439 
440 static void
gssdp_socket_source_class_init(GSSDPSocketSourceClass * klass)441 gssdp_socket_source_class_init (GSSDPSocketSourceClass *klass)
442 {
443         GObjectClass *object_class;
444 
445         object_class = G_OBJECT_CLASS (klass);
446 
447         object_class->get_property = gssdp_socket_source_get_property;
448         object_class->set_property = gssdp_socket_source_set_property;
449         object_class->dispose = gssdp_socket_source_dispose;
450         object_class->finalize = gssdp_socket_source_finalize;
451 
452         g_object_class_install_property
453                 (object_class,
454                  PROP_TYPE,
455                  g_param_spec_int
456                         ("type",
457                          "Type",
458                          "Type of socket-source (Multicast/Unicast)",
459                          GSSDP_SOCKET_SOURCE_TYPE_REQUEST,
460                          GSSDP_SOCKET_SOURCE_TYPE_SEARCH,
461                          GSSDP_SOCKET_SOURCE_TYPE_REQUEST,
462                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
463                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
464                          G_PARAM_STATIC_BLURB));
465 
466         g_object_class_install_property
467                 (object_class,
468                  PROP_ADDRESS,
469                  g_param_spec_object
470                         ("address",
471                          "Host address",
472                          "IP address of associated network interface",
473                          G_TYPE_INET_ADDRESS,
474                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
475                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
476                          G_PARAM_STATIC_BLURB));
477 
478         g_object_class_install_property
479                 (object_class,
480                  PROP_IFA_NAME,
481                  g_param_spec_string
482                         ("device-name",
483                          "Interface name",
484                          "Name of associated network interface",
485                          NULL,
486                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
487                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
488                          G_PARAM_STATIC_BLURB));
489 
490         g_object_class_install_property
491                 (object_class,
492                  PROP_TTL,
493                  g_param_spec_uint
494                         ("ttl",
495                          "TTL",
496                          "Time To Live for the socket",
497                          0, 255,
498                          0,
499                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
500                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
501                          G_PARAM_STATIC_BLURB));
502 
503         g_object_class_install_property
504                 (object_class,
505                  PROP_PORT,
506                  g_param_spec_uint
507                         ("port",
508                          "UDP port",
509                          "UDP port to use for TYPE_SEARCH sockets",
510                          0, G_MAXUINT16,
511                          0,
512                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
513                          G_PARAM_STATIC_STRINGS));
514 
515         g_object_class_install_property
516                 (object_class,
517                  PROP_IFA_IDX,
518                  g_param_spec_int
519                         ("index",
520                          "Interface index",
521                          "Interface index of the network device",
522                          -1, G_MAXUINT16,
523                          -1,
524                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
525                          G_PARAM_STATIC_STRINGS));
526 }
527