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