Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-lan-channel-service"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <gio/gunixinputstream.h>
10 : : #include <valent.h>
11 : :
12 : : #include "valent-lan-channel.h"
13 : : #include "valent-lan-channel-service.h"
14 : : #include "valent-lan-dnssd.h"
15 : : #include "valent-lan-utils.h"
16 : :
17 : : #define IDENTITY_BUFFER_MAX (8192)
18 : : #define IDENTITY_TIMEOUT_MAX (1000)
19 : :
20 : :
21 : : struct _ValentLanChannelService
22 : : {
23 : : ValentChannelService parent_instance;
24 : :
25 : : GNetworkMonitor *monitor;
26 : : gboolean network_available;
27 : :
28 : : /* Service */
29 : : uint16_t port;
30 : : uint16_t tcp_port;
31 : : char *broadcast_address;
32 : : GListModel *dnssd;
33 : : GSocketService *listener;
34 : : GMainLoop *udp_context;
35 : : GSocket *udp_socket4;
36 : : GSocket *udp_socket6;
37 : : GHashTable *channels;
38 : : };
39 : :
40 : : static void g_async_initable_iface_init (GAsyncInitableIface *iface);
41 : :
42 [ + + + - ]: 43 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentLanChannelService, valent_lan_channel_service, VALENT_TYPE_CHANNEL_SERVICE,
43 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, g_async_initable_iface_init))
44 : :
45 : : typedef enum {
46 : : PROP_BROADCAST_ADDRESS = 1,
47 : : PROP_PORT,
48 : : } ValentLanChannelServiceProperty;
49 : :
50 : : static GParamSpec *properties[PROP_PORT + 1] = { NULL, };
51 : :
52 : :
53 : : static void
54 : 0 : on_network_changed (GNetworkMonitor *monitor,
55 : : gboolean network_available,
56 : : ValentLanChannelService *self)
57 : : {
58 [ # # ]: 0 : if (self->network_available == network_available)
59 : : return;
60 : :
61 [ # # ]: 0 : if ((self->network_available = network_available))
62 : 0 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (self), NULL);
63 : : }
64 : :
65 : : static void
66 : 0 : on_channel_destroyed (ValentLanChannelService *self,
67 : : ValentLanChannel *channel)
68 : : {
69 : 0 : GTlsCertificate *certificate = NULL;
70 : 0 : const char *device_id = NULL;
71 : :
72 [ # # ]: 0 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
73 [ # # ]: 0 : g_assert (VALENT_IS_LAN_CHANNEL (channel));
74 : :
75 : 0 : certificate = valent_channel_get_certificate (VALENT_CHANNEL (channel));
76 : 0 : device_id = valent_certificate_get_common_name (certificate);
77 : :
78 : 0 : valent_object_lock (VALENT_OBJECT (self));
79 : 0 : g_hash_table_remove (self->channels, device_id);
80 : 0 : valent_object_unlock (VALENT_OBJECT (self));
81 : 0 : }
82 : :
83 : : /**
84 : : * valent_lan_channel_service_verify_channel:
85 : : * @self: a `ValentLanChannelService`
86 : : * @identity: a KDE Connect identity packet
87 : : * @connection: a `GTlsConnection`
88 : : *
89 : : * Verify an encrypted TLS connection.
90 : : *
91 : : * @device_id should be the `deviceID` field from an identity packet. If it does
92 : : * not match the common name for the peer certificate, %FALSE will be returned.
93 : : *
94 : : * @connection should be an encrypted TLS connection. If there is an existing
95 : : * channel for @device_id with a different certificate, %FALSE will be returned.
96 : : *
97 : : * Returns: %TRUE if successful, or %FALSE on failure
98 : : */
99 : : static gboolean
100 : 3 : valent_lan_channel_service_verify_channel (ValentLanChannelService *self,
101 : : JsonNode *identity,
102 : : GIOStream *connection)
103 : : {
104 : 3 : ValentLanChannel *channel = NULL;
105 : 3 : GTlsCertificate *certificate = NULL;
106 : 6 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
107 : 3 : const char *peer_certificate_cn = NULL;
108 : 3 : const char *device_id = NULL;
109 : :
110 [ + - ]: 3 : g_assert (VALENT_IS_CHANNEL_SERVICE (self));
111 [ - + ]: 3 : g_assert (VALENT_IS_PACKET (identity));;
112 [ + - + - : 3 : g_assert (G_IS_TLS_CONNECTION (connection));
+ - - + ]
113 : :
114 [ - + ]: 3 : if (!valent_packet_get_string (identity, "deviceId", &device_id))
115 : : {
116 : 0 : g_debug ("%s(): expected \"deviceId\" field holding a string",
117 : : G_STRFUNC);
118 : 0 : return FALSE;
119 : : }
120 : :
121 : 3 : g_object_get (connection, "peer-certificate", &peer_certificate, NULL);
122 : 3 : peer_certificate_cn = valent_certificate_get_common_name (peer_certificate);
123 : :
124 [ - + ]: 3 : if (g_strcmp0 (device_id, peer_certificate_cn) != 0)
125 : : {
126 : 0 : g_warning ("%s(): device ID does not match certificate common name",
127 : : G_STRFUNC);
128 : 0 : return FALSE;
129 : : }
130 : :
131 : 3 : valent_object_lock (VALENT_OBJECT (self));
132 : 3 : channel = g_hash_table_lookup (self->channels, device_id);
133 [ - + ]: 3 : if (channel != NULL)
134 : 0 : certificate = valent_channel_get_peer_certificate (VALENT_CHANNEL (channel));
135 : 3 : valent_object_unlock (VALENT_OBJECT (self));
136 : :
137 [ - + - - ]: 3 : if (certificate && !g_tls_certificate_is_same (certificate, peer_certificate))
138 : : {
139 : 0 : g_warning ("%s(): existing channel with different certificate",
140 : : G_STRFUNC);
141 : 0 : return FALSE;
142 : : }
143 : :
144 : : return TRUE;
145 : : }
146 : :
147 : : /*
148 : : * Incoming TCP Connections
149 : : *
150 : : * When an incoming connection is opened to the TCP listener, we are operating
151 : : * as the client. The server expects us to:
152 : : *
153 : : * 1) Accept the TCP connection
154 : : * 2) Read the peer identity packet
155 : : * 3) Negotiate TLS encryption (as the TLS Client)
156 : : */
157 : : static gboolean
158 : 1 : incoming_connection_timeout_cb (gpointer data)
159 : : {
160 [ + - + - : 1 : g_assert (G_IS_CANCELLABLE (data));
- + - - ]
161 : :
162 : 1 : g_cancellable_cancel ((GCancellable *)data);
163 : :
164 : 1 : return G_SOURCE_REMOVE;
165 : : }
166 : :
167 : : static gboolean
168 : 1 : on_incoming_connection (ValentChannelService *service,
169 : : GSocketConnection *connection,
170 : : GCancellable *cancellable,
171 : : GThreadedSocketService *listener)
172 : : {
173 : 1 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (service);
174 : 2 : g_autoptr (GCancellable) timeout = NULL;
175 : 1 : unsigned long cancellable_id = 0;
176 [ + - ]: 1 : g_autoptr (GSocketAddress) s_addr = NULL;
177 : 1 : GInetAddress *i_addr = NULL;
178 [ + - ]: 1 : g_autofree char *host = NULL;
179 : 1 : int64_t port = VALENT_LAN_PROTOCOL_PORT;
180 : 1 : g_autoptr (JsonNode) identity = NULL;
181 [ + - ]: 1 : g_autoptr (JsonNode) peer_identity = NULL;
182 : 1 : const char *device_id;
183 [ + - ]: 1 : g_autoptr (GTlsCertificate) certificate = NULL;
184 : 1 : GTlsCertificate *peer_certificate = NULL;
185 [ + - ]: 1 : g_autoptr (GIOStream) tls_stream = NULL;
186 [ + - ]: 1 : g_autoptr (ValentChannel) channel = NULL;
187 [ + - ]: 1 : g_autoptr (GError) warning = NULL;
188 : :
189 [ + - ]: 1 : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
190 [ - + ]: 1 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
191 : :
192 : : /* Timeout if the peer fails to authenticate in a timely fashion. */
193 : 1 : timeout = g_cancellable_new ();
194 : 1 : g_timeout_add_full (G_PRIORITY_DEFAULT,
195 : : IDENTITY_TIMEOUT_MAX,
196 : : incoming_connection_timeout_cb,
197 : : g_object_ref (timeout),
198 : : g_object_unref);
199 : :
200 [ + - ]: 1 : if (cancellable != NULL)
201 : 1 : cancellable_id = g_cancellable_connect (cancellable,
202 : : G_CALLBACK (g_cancellable_cancel),
203 : : timeout,
204 : : NULL);
205 : :
206 : : /* An incoming TCP connection is in response to an outgoing UDP packet, so the
207 : : * the peer must now write its identity packet. */
208 : 1 : peer_identity = valent_packet_from_stream (g_io_stream_get_input_stream (G_IO_STREAM (connection)),
209 : : IDENTITY_BUFFER_MAX,
210 : : timeout,
211 : : &warning);
212 : :
213 [ - + ]: 1 : if (peer_identity == NULL)
214 : : {
215 [ # # ]: 0 : if (!g_error_matches (warning, G_IO_ERROR, G_IO_ERROR_CANCELLED))
216 : 0 : g_warning ("%s(): %s", G_STRFUNC, warning->message);
217 [ # # ]: 0 : else if (!g_cancellable_is_cancelled (cancellable))
218 : 0 : g_warning ("%s(): timed out waiting for peer identity", G_STRFUNC);
219 : :
220 : 0 : g_cancellable_disconnect (cancellable, cancellable_id);
221 : :
222 : 0 : return TRUE;
223 : : }
224 : :
225 : : /* Ignore identity packets without a deviceId */
226 [ - + ]: 1 : if (!valent_packet_get_string (peer_identity, "deviceId", &device_id))
227 : : {
228 : 0 : g_debug ("%s(): expected \"deviceId\" field holding a string",
229 : : G_STRFUNC);
230 : 0 : return TRUE;
231 : : }
232 : :
233 : 1 : VALENT_JSON (peer_identity, host);
234 : :
235 : : /* NOTE: We're the client when accepting incoming connections */
236 : 1 : certificate = valent_channel_service_ref_certificate (service);
237 : 1 : tls_stream = valent_lan_encrypt_client_connection (connection,
238 : : certificate,
239 : : timeout,
240 : : &warning);
241 : 1 : g_cancellable_disconnect (cancellable, cancellable_id);
242 : :
243 [ - + ]: 1 : if (tls_stream == NULL)
244 : : {
245 [ # # ]: 0 : if (!g_error_matches (warning, G_IO_ERROR, G_IO_ERROR_CANCELLED))
246 : 0 : g_warning ("%s(): %s", G_STRFUNC, warning->message);
247 [ # # ]: 0 : else if (!g_cancellable_is_cancelled (cancellable))
248 : 0 : g_warning ("%s(): timed out waiting for authentication", G_STRFUNC);
249 : :
250 : 0 : return TRUE;
251 : : }
252 : :
253 [ + - ]: 1 : if (!valent_lan_channel_service_verify_channel (self, peer_identity, tls_stream))
254 : : return TRUE;
255 : :
256 : : /* Get the host from the connection */
257 : 1 : s_addr = g_socket_connection_get_remote_address (connection, NULL);
258 : 1 : i_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (s_addr));
259 : 1 : host = g_inet_address_to_string (i_addr);
260 : 1 : valent_packet_get_int (peer_identity, "tcpPort", &port);
261 : :
262 : : /* Create the new channel */
263 : 1 : identity = valent_channel_service_ref_identity (service);
264 : 1 : peer_certificate = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (tls_stream));
265 : 1 : channel = g_object_new (VALENT_TYPE_LAN_CHANNEL,
266 : : "base-stream", tls_stream,
267 : : "certificate", certificate,
268 : : "identity", identity,
269 : : "peer-certificate", peer_certificate,
270 : : "peer-identity", peer_identity,
271 : : "host", host,
272 : 1 : "port", (uint16_t)port,
273 : : NULL);
274 : :
275 : 1 : valent_channel_service_channel (service, channel);
276 : :
277 : 1 : return TRUE;
278 : : }
279 : :
280 : : /**
281 : : * valent_lan_channel_service_tcp_setup:
282 : : * @self: a `ValentLanChannelService`
283 : : * @error: (nullable): a `GError`
284 : : *
285 : : * A wrapper around g_socket_listener_add_inet_port() that can be called
286 : : * multiple times.
287 : : *
288 : : * Returns: %TRUE if successful, or %FALSE with @error set
289 : : */
290 : : static gboolean
291 : 3 : valent_lan_channel_service_tcp_setup (ValentLanChannelService *self,
292 : : GCancellable *cancellable,
293 : : GError **error)
294 : : {
295 : 6 : g_autoptr (GCancellable) destroy = NULL;
296 [ + - ]: 3 : g_autoptr (GSocketService) listener = NULL;
297 : 3 : uint16_t tcp_port = VALENT_LAN_PROTOCOL_PORT;
298 : 3 : uint16_t tcp_port_max;
299 : :
300 [ + - ]: 3 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
301 [ + - + - : 3 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
302 [ + - - + ]: 3 : g_assert (error == NULL || *error == NULL);
303 : :
304 [ + - ]: 3 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
305 : : return FALSE;
306 : :
307 : : /* The effective port range is actually relative to the construct-time
308 : : * `port`property, for running in test environments.
309 : : */
310 : 3 : valent_object_lock (VALENT_OBJECT (self));
311 : 3 : tcp_port = self->port;
312 : 3 : tcp_port_max = tcp_port + (VALENT_LAN_PROTOCOL_PORT_MAX -
313 : : VALENT_LAN_PROTOCOL_PORT_MIN);
314 : 3 : valent_object_unlock (VALENT_OBJECT (self));
315 : :
316 : : /* Pass the service as the callback data for the "run" signal, while the
317 : : * listener holds a reference to the object cancellable.
318 : : */
319 : 3 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
320 : 3 : listener = g_threaded_socket_service_new (g_get_num_processors ());
321 : 3 : g_signal_connect_object (listener,
322 : : "run",
323 : : G_CALLBACK (on_incoming_connection),
324 : : self,
325 : : G_CONNECT_SWAPPED);
326 : :
327 [ - + ]: 3 : while (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (listener),
328 : : tcp_port,
329 : : G_OBJECT (destroy),
330 : : error))
331 : : {
332 [ # # ]: 0 : if (tcp_port >= tcp_port_max)
333 : : {
334 : 0 : g_socket_service_stop (listener);
335 : 0 : g_socket_listener_close (G_SOCKET_LISTENER (listener));
336 : :
337 : 0 : return FALSE;
338 : : }
339 : :
340 : 0 : g_clear_error (error);
341 : 0 : tcp_port++;
342 : : }
343 : :
344 : 3 : valent_object_lock (VALENT_OBJECT (self));
345 : 3 : self->tcp_port = tcp_port;
346 : 3 : self->listener = g_object_ref (listener);
347 : 3 : valent_channel_service_build_identity (VALENT_CHANNEL_SERVICE (self));
348 : 3 : valent_object_unlock (VALENT_OBJECT (self));
349 : :
350 : 3 : return TRUE;
351 : : }
352 : :
353 : : /*
354 : : * Incoming UDP Broadcasts
355 : : *
356 : : * When an identity packet is received over a UDP port (usually a broadcast), we
357 : : * are operating as the server. The client expects us to:
358 : : *
359 : : * 1) Open a TCP connection
360 : : * 2) Write our identity packet
361 : : * 3) Negotiate TLS encryption (as the TLS Server)
362 : : */
363 : : static void
364 : 2 : incoming_broadcast_task (GTask *task,
365 : : gpointer source_object,
366 : : gpointer task_data,
367 : : GCancellable *cancellable)
368 : : {
369 : 2 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (source_object);
370 : 2 : ValentChannelService *service = VALENT_CHANNEL_SERVICE (source_object);
371 : 2 : GSocketAddress *address = G_SOCKET_ADDRESS (task_data);
372 : 2 : JsonNode *peer_identity = NULL;
373 : 2 : g_autoptr (GSocketClient) client = NULL;
374 [ + - - - ]: 2 : g_autoptr (GSocketConnection) connection = NULL;
375 : 2 : GInetAddress *addr = NULL;
376 [ - - ]: 2 : g_autoptr (JsonNode) identity = NULL;
377 [ + - - - ]: 2 : g_autoptr (ValentChannel) channel = NULL;
378 [ + - - - ]: 2 : g_autofree char *host = NULL;
379 : 2 : int64_t port = VALENT_LAN_PROTOCOL_PORT;
380 : 2 : GOutputStream *output_stream;
381 : 2 : g_autoptr (GTlsCertificate) certificate = NULL;
382 : 2 : GTlsCertificate *peer_certificate = NULL;
383 [ + - - - ]: 2 : g_autoptr (GIOStream) tls_stream = NULL;
384 [ - - ]: 2 : g_autoptr (GError) error = NULL;
385 : :
386 [ + - ]: 2 : g_assert (VALENT_IS_CHANNEL_SERVICE (self));
387 [ + - + - : 2 : g_assert (G_IS_SOCKET_ADDRESS (address));
+ - - + ]
388 : :
389 : 2 : addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address));
390 : 2 : host = g_inet_address_to_string (addr);
391 : 2 : peer_identity = g_object_get_data (G_OBJECT (address), "valent-lan-broadcast");
392 : 2 : valent_packet_get_int (peer_identity, "tcpPort", &port);
393 : :
394 : : /* Open a TCP connection to the UDP sender and defined port.
395 : : *
396 : : * Disable the system proxy:
397 : : * - https://bugs.kde.org/show_bug.cgi?id=376187
398 : : * - https://github.com/andyholmes/gnome-shell-extension-gsconnect/issues/125
399 : : */
400 : 2 : client = g_object_new (G_TYPE_SOCKET_CLIENT,
401 : : "enable-proxy", FALSE,
402 : : NULL);
403 : 2 : connection = g_socket_client_connect (client,
404 : : G_SOCKET_CONNECTABLE (address),
405 : : cancellable,
406 : : &error);
407 : :
408 [ - + ]: 2 : if (connection == NULL)
409 : : {
410 : 0 : g_debug ("%s(): connecting to (%s:%"G_GINT64_FORMAT"): %s",
411 : : G_STRFUNC, host, port, error->message);
412 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
413 : : }
414 : :
415 : : /* Write the local identity. Once we do this, both peers will have the ability
416 : : * to authenticate or reject TLS certificates.
417 : : */
418 : 2 : identity = valent_channel_service_ref_identity (service);
419 : 2 : output_stream = g_io_stream_get_output_stream (G_IO_STREAM (connection));
420 : :
421 [ - + ]: 2 : if (!valent_packet_to_stream (output_stream, identity, cancellable, &error))
422 : : {
423 : 0 : g_debug ("%s(): sending identity to (%s:%"G_GINT64_FORMAT"): %s",
424 : : G_STRFUNC, host, port, error->message);
425 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
426 : : }
427 : :
428 : : /* NOTE: We're the server when opening outgoing connections */
429 : 2 : certificate = valent_channel_service_ref_certificate (service);
430 : 2 : tls_stream = valent_lan_encrypt_server_connection (connection,
431 : : certificate,
432 : : cancellable,
433 : : &error);
434 : :
435 [ - + ]: 2 : if (tls_stream == NULL)
436 : : {
437 : 0 : g_debug ("%s(): authenticating (%s:%"G_GINT64_FORMAT"): %s",
438 : : G_STRFUNC, host, port, error->message);
439 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
440 : : }
441 : :
442 [ - + ]: 2 : if (!valent_lan_channel_service_verify_channel (self, peer_identity, tls_stream))
443 : 0 : return g_task_return_boolean (task, TRUE);
444 : :
445 : 2 : peer_certificate = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (tls_stream));
446 : 2 : channel = g_object_new (VALENT_TYPE_LAN_CHANNEL,
447 : : "base-stream", tls_stream,
448 : : "certificate", certificate,
449 : : "identity", identity,
450 : : "peer-certificate", peer_certificate,
451 : : "peer-identity", peer_identity,
452 : : "host", host,
453 : 2 : "port", (uint16_t)port,
454 : : NULL);
455 : :
456 : 2 : valent_channel_service_channel (service, channel);
457 [ - + ]: 2 : g_task_return_boolean (task, TRUE);
458 : : }
459 : :
460 : : static gboolean
461 : 10 : valent_lan_channel_service_socket_recv (GSocket *socket,
462 : : GIOCondition condition,
463 : : gpointer user_data)
464 : : {
465 : 10 : ValentChannelService *service = VALENT_CHANNEL_SERVICE (user_data);
466 : 20 : g_autoptr (GCancellable) cancellable = NULL;
467 [ + + ]: 10 : g_autoptr (GError) error = NULL;
468 : 10 : gssize read = 0;
469 : 10 : char buffer[IDENTITY_BUFFER_MAX + 1] = { 0, };
470 [ - + ]: 10 : g_autoptr (GSocketAddress) incoming = NULL;
471 [ + + ]: 10 : g_autoptr (GSocketAddress) outgoing = NULL;
472 : 10 : GInetAddress *addr = NULL;
473 : 10 : int64_t port = VALENT_LAN_PROTOCOL_PORT;
474 [ + + ]: 10 : g_autoptr (JsonNode) peer_identity = NULL;
475 : 10 : const char *device_id;
476 [ + + ]: 10 : g_autofree char *local_id = NULL;
477 : 10 : g_autoptr (GTask) task = NULL;
478 [ + + ]: 10 : g_autoptr (GError) warning = NULL;
479 : :
480 [ + - + - : 10 : g_assert (G_IS_SOCKET (socket));
- + - - ]
481 [ - + ]: 10 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (user_data));
482 : :
483 [ + + ]: 10 : if (condition != G_IO_IN)
484 : : return G_SOURCE_REMOVE;
485 : :
486 : 7 : cancellable = valent_object_ref_cancellable (VALENT_OBJECT (service));
487 : :
488 : : /* Read the message data and extract the remote address */
489 : 7 : read = g_socket_receive_from (socket,
490 : : &incoming,
491 : : buffer,
492 : : IDENTITY_BUFFER_MAX,
493 : : cancellable,
494 : : &error);
495 : :
496 [ - + ]: 7 : if (read == -1)
497 : : {
498 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
499 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
500 : :
501 : 0 : return G_SOURCE_REMOVE;
502 : : }
503 : :
504 [ - + ]: 7 : if (read == 0)
505 : : {
506 : 0 : g_warning ("%s(): Socket is closed", G_STRFUNC);
507 : 0 : return G_SOURCE_REMOVE;
508 : : }
509 : :
510 : : /* Validate the message as a KDE Connect packet */
511 [ - + ]: 7 : if ((peer_identity = valent_packet_deserialize (buffer, &warning)) == NULL)
512 : : {
513 : 0 : g_warning ("%s(): failed to parse peer-identity: %s",
514 : : G_STRFUNC,
515 : : warning->message);
516 : 0 : return G_SOURCE_CONTINUE;
517 : : }
518 : :
519 : : /* Ignore broadcasts without a deviceId or from ourselves */
520 [ - + ]: 7 : if (!valent_packet_get_string (peer_identity, "deviceId", &device_id))
521 : : {
522 : 0 : g_debug ("%s(): expected \"deviceId\" field holding a string",
523 : : G_STRFUNC);
524 : 0 : return G_SOURCE_CONTINUE;
525 : : }
526 : :
527 : 7 : local_id = valent_channel_service_dup_id (service);
528 : :
529 [ + + ]: 7 : if (g_strcmp0 (device_id, local_id) == 0)
530 : : return G_SOURCE_CONTINUE;
531 : :
532 : 2 : VALENT_JSON (peer_identity, device_id);
533 : :
534 : : /* Get the remote address and port */
535 : 2 : addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (incoming));
536 : :
537 [ + - ]: 2 : if (!valent_packet_get_int (peer_identity, "tcpPort", &port) ||
538 [ - + ]: 2 : (port < VALENT_LAN_PROTOCOL_PORT_MIN || port > VALENT_LAN_PROTOCOL_PORT_MAX))
539 : : {
540 : 0 : g_debug ("%s(): expected \"tcpPort\" field holding a uint16 between %u-%u",
541 : : G_STRFUNC,
542 : : VALENT_LAN_PROTOCOL_PORT_MIN,
543 : : VALENT_LAN_PROTOCOL_PORT_MAX);
544 : 0 : return G_SOURCE_CONTINUE;
545 : : }
546 : :
547 : : /* Defer the remaining work to another thread */
548 : 2 : outgoing = g_inet_socket_address_new (addr, port);
549 : 2 : g_object_set_data_full (G_OBJECT (outgoing),
550 : : "valent-lan-broadcast",
551 : 2 : json_node_ref (peer_identity),
552 : : (GDestroyNotify)json_node_unref);
553 : :
554 : 2 : task = g_task_new (service, cancellable, NULL, NULL);
555 [ + - ]: 2 : g_task_set_source_tag (task, valent_lan_channel_service_socket_recv);
556 : 2 : g_task_set_task_data (task, g_steal_pointer (&outgoing), g_object_unref);
557 : 2 : g_task_run_in_thread (task, incoming_broadcast_task);
558 : :
559 : 2 : return G_SOURCE_CONTINUE;
560 : : }
561 : :
562 : : static gboolean
563 : 6 : valent_lan_channel_service_socket_send (GSocket *socket,
564 : : GIOCondition condition,
565 : : gpointer user_data)
566 : : {
567 : 6 : GSocketAddress *address = G_SOCKET_ADDRESS (user_data);
568 : 6 : GBytes *bytes = NULL;
569 : 6 : gssize written;
570 : 12 : g_autoptr (GError) error = NULL;
571 : :
572 [ + - + - : 6 : g_assert (G_IS_SOCKET (socket));
- + - - ]
573 [ + - + - : 6 : g_assert (G_IS_SOCKET_ADDRESS (address));
+ - - + ]
574 : :
575 [ + - ]: 6 : if (condition != G_IO_OUT)
576 : : return G_SOURCE_REMOVE;
577 : :
578 : 6 : bytes = g_object_get_data (G_OBJECT (address), "valent-lan-broadcast");
579 : 12 : written = g_socket_send_to (socket,
580 : : address,
581 : 6 : g_bytes_get_data (bytes, NULL),
582 : : g_bytes_get_size (bytes),
583 : : NULL,
584 : : &error);
585 : :
586 : : /* We only check for real errors, not partial writes */
587 [ - + ]: 6 : if (written == -1)
588 : 0 : g_warning ("%s(): failed to identify: %s", G_STRFUNC, error->message);
589 : :
590 : : return G_SOURCE_REMOVE;
591 : : }
592 : :
593 : : static void
594 : 6 : valent_lan_channel_service_socket_queue (ValentLanChannelService *self,
595 : : GSocketAddress *address)
596 : : {
597 : 0 : g_autoptr (JsonNode) identity = NULL;
598 [ + - ]: 6 : g_autoptr (GBytes) identity_bytes = NULL;
599 : 6 : char *identity_json = NULL;
600 : 6 : GSocketFamily family = G_SOCKET_FAMILY_INVALID;
601 : :
602 [ + - ]: 6 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
603 [ + - + - : 6 : g_assert (G_IS_SOCKET_ADDRESS (address));
+ - - + ]
604 : :
605 : : /* Ignore errant socket addresses */
606 : 6 : family = g_socket_address_get_family (address);
607 : :
608 [ - + ]: 6 : if (family != G_SOCKET_FAMILY_IPV4 && family != G_SOCKET_FAMILY_IPV6)
609 : 0 : g_return_if_reached ();
610 : :
611 : : /* Serialize the identity */
612 : 6 : identity = valent_channel_service_ref_identity (VALENT_CHANNEL_SERVICE (self));
613 : 6 : identity_json = valent_packet_serialize (identity);
614 : 6 : identity_bytes = g_bytes_new_take (identity_json, strlen (identity_json));
615 : 6 : g_object_set_data_full (G_OBJECT (address),
616 : : "valent-lan-broadcast",
617 : 6 : g_bytes_ref (identity_bytes),
618 : : (GDestroyNotify)g_bytes_unref);
619 : :
620 : 6 : valent_object_lock (VALENT_OBJECT (self));
621 [ + - + - : 12 : if ((self->udp_socket6 != NULL && family == G_SOCKET_FAMILY_IPV6) ||
+ - ]
622 : 6 : (self->udp_socket6 != NULL && g_socket_speaks_ipv4 (self->udp_socket6)))
623 : : {
624 : 12 : g_autoptr (GSource) source = NULL;
625 : :
626 : 6 : source = g_socket_create_source (self->udp_socket6, G_IO_OUT, NULL);
627 : 6 : g_source_set_callback (source,
628 : : G_SOURCE_FUNC (valent_lan_channel_service_socket_send),
629 : : g_object_ref (address),
630 : : g_object_unref);
631 [ + - ]: 6 : g_source_attach (source, g_main_loop_get_context (self->udp_context));
632 : : }
633 : :
634 [ - + - - ]: 6 : if (self->udp_socket4 != NULL && family == G_SOCKET_FAMILY_IPV4)
635 : : {
636 : 6 : g_autoptr (GSource) source = NULL;
637 : :
638 : 0 : source = g_socket_create_source (self->udp_socket4, G_IO_OUT, NULL);
639 : 0 : g_source_set_callback (source,
640 : : G_SOURCE_FUNC (valent_lan_channel_service_socket_send),
641 : : g_object_ref (address),
642 : : g_object_unref);
643 [ # # ]: 0 : g_source_attach (source, g_main_loop_get_context (self->udp_context));
644 : : }
645 [ + - ]: 6 : valent_object_unlock (VALENT_OBJECT (self));
646 : : }
647 : :
648 : : static gpointer
649 : 3 : valent_lan_channel_service_socket_worker (gpointer data)
650 : : {
651 : 6 : g_autoptr (GMainLoop) loop = (GMainLoop *)data;
652 : 3 : GMainContext *context = g_main_loop_get_context (loop);
653 : :
654 : : /* The loop quits when the channel is closed, then the context is drained to
655 : : * ensure all tasks return. */
656 : 3 : g_main_context_push_thread_default (context);
657 : :
658 : 3 : g_main_loop_run (loop);
659 : :
660 [ - + ]: 3 : while (g_main_context_pending (context))
661 : 0 : g_main_context_iteration (NULL, FALSE);
662 : :
663 : 3 : g_main_context_pop_thread_default (context);
664 : :
665 [ + - ]: 3 : return NULL;
666 : : }
667 : :
668 : : static void
669 : 6 : on_items_changed (GListModel *list,
670 : : unsigned int position,
671 : : unsigned int removed,
672 : : unsigned int added,
673 : : ValentLanChannelService *self)
674 : : {
675 : 6 : g_autofree char *service_id = NULL;
676 : :
677 [ - + ]: 6 : if (added == 0)
678 : 0 : return;
679 : :
680 : 6 : service_id = valent_channel_service_dup_id (VALENT_CHANNEL_SERVICE (self));
681 : :
682 [ + + ]: 12 : for (unsigned int i = 0; i < added; i++)
683 : : {
684 : 6 : g_autoptr (GSocketAddress) address = NULL;
685 : 6 : const char *device_id = NULL;
686 : :
687 : 6 : address = g_list_model_get_item (list, position + i);
688 : 6 : device_id = _g_socket_address_get_dnssd_name (address);
689 : :
690 [ + + ]: 6 : if (g_strcmp0 (service_id, device_id) == 0)
691 [ + - ]: 1 : continue;
692 : :
693 : 5 : valent_object_lock (VALENT_OBJECT (self));
694 [ + - ]: 5 : if (!g_hash_table_contains (self->channels, device_id))
695 : 5 : valent_lan_channel_service_socket_queue (self, address);
696 [ + - ]: 5 : valent_object_unlock (VALENT_OBJECT (self));
697 : : }
698 : : }
699 : :
700 : : /**
701 : : * valent_lan_channel_service_udp_setup:
702 : : * @self: a `ValentLanChannelService`
703 : : * @cancellable: (nullable): a `GCancellable`
704 : : * @error: (nullable): a `GError`
705 : : *
706 : : * An analog to valent_lan_channel_service_tcp_setup() that prepares UDP sockets
707 : : * for IPv4 and IPv6, including streams for reading.
708 : : *
709 : : * Returns: %TRUE if successful, or %FALSE with @error set
710 : : */
711 : : static gboolean
712 : 3 : valent_lan_channel_service_udp_setup (ValentLanChannelService *self,
713 : : GCancellable *cancellable,
714 : : GError **error)
715 : : {
716 : 6 : g_autoptr (GMainContext) context = NULL;
717 [ + - ]: 3 : g_autoptr (GMainLoop) loop = NULL;
718 [ + - ]: 3 : g_autoptr (GThread) thread = NULL;
719 [ + - ]: 3 : g_autoptr (GSocket) socket4 = NULL;
720 [ - + ]: 3 : g_autoptr (GSocket) socket6 = NULL;
721 [ + - ]: 3 : g_autoptr (GListModel) services = NULL;
722 [ + - ]: 3 : g_autoptr (GCancellable) destroy = NULL;
723 : 3 : uint16_t port = VALENT_LAN_PROTOCOL_PORT;
724 : :
725 : 3 : VALENT_ENTRY;
726 : :
727 [ + - ]: 3 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
728 : 3 : g_assert (cancellable == NULL || G_CANCELLABLE (cancellable));
729 [ + - - + ]: 3 : g_assert (error == NULL || *error == NULL);
730 : :
731 : : /* Prepare socket(s) for UDP-based discovery */
732 : 3 : valent_object_lock (VALENT_OBJECT (self));
733 : 3 : port = self->port;
734 : 3 : valent_object_unlock (VALENT_OBJECT (self));
735 : :
736 : 3 : socket6 = g_socket_new (G_SOCKET_FAMILY_IPV6,
737 : : G_SOCKET_TYPE_DATAGRAM,
738 : : G_SOCKET_PROTOCOL_UDP,
739 : : NULL);
740 : :
741 [ - + ]: 3 : if (socket6 != NULL)
742 : : {
743 : 6 : g_autoptr (GInetAddress) inet_address = NULL;
744 [ - - + - : 3 : g_autoptr (GSocketAddress) address = NULL;
- - ]
745 : :
746 : 3 : inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
747 : 3 : address = g_inet_socket_address_new (inet_address, port);
748 : :
749 [ - + ]: 3 : if (!g_socket_bind (socket6, address, TRUE, NULL))
750 : : {
751 : 0 : g_clear_object (&socket6);
752 [ # # ]: 0 : VALENT_GOTO (ipv4);
753 : : }
754 : :
755 : 3 : g_socket_set_broadcast (socket6, TRUE);
756 : :
757 : : /* If this socket also speaks IPv4, move on to DNS-SD */
758 [ + - ]: 3 : if (g_socket_speaks_ipv4 (socket6))
759 [ + - - - ]: 3 : VALENT_GOTO (dnssd);
760 : : }
761 : :
762 : 0 : ipv4:
763 : 0 : socket4 = g_socket_new (G_SOCKET_FAMILY_IPV4,
764 : : G_SOCKET_TYPE_DATAGRAM,
765 : : G_SOCKET_PROTOCOL_UDP,
766 : : error);
767 : :
768 [ # # ]: 0 : if (socket4 != NULL)
769 : : {
770 : 0 : g_autoptr (GInetAddress) inet_address = NULL;
771 [ # # # # ]: 0 : g_autoptr (GSocketAddress) address = NULL;
772 : :
773 : 0 : inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
774 : 0 : address = g_inet_socket_address_new (inet_address, port);
775 : :
776 [ # # ]: 0 : if (!g_socket_bind (socket4, address, TRUE, error))
777 : : {
778 : 0 : g_clear_object (&socket4);
779 [ # # ]: 0 : VALENT_GOTO (check);
780 : : }
781 : :
782 [ # # ]: 0 : g_socket_set_broadcast (socket4, TRUE);
783 : : }
784 : :
785 [ # # ]: 0 : check:
786 [ # # # # ]: 0 : if (socket6 != NULL || socket4 != NULL)
787 : 0 : g_clear_error (error);
788 : : else
789 : 3 : VALENT_RETURN (FALSE);
790 : :
791 : 3 : dnssd:
792 : 3 : services = valent_lan_dnssd_new (NULL);
793 : 3 : g_object_bind_property (self, "identity",
794 : : services, "identity",
795 : : G_BINDING_SYNC_CREATE);
796 : 3 : g_signal_connect_object (services,
797 : : "items-changed",
798 : : G_CALLBACK (on_items_changed),
799 : : self, 0);
800 : :
801 : : /* Create a thread-context for UDP broadcasts, and the DNS-SD service */
802 : 3 : context = g_main_context_new ();
803 : 3 : loop = g_main_loop_new (context, FALSE);
804 : 3 : thread = g_thread_try_new ("valent-lan-channel-service",
805 : : valent_lan_channel_service_socket_worker,
806 : 3 : g_main_loop_ref (loop),
807 : : error);
808 : :
809 [ - + ]: 3 : if (thread == NULL)
810 : : {
811 : 0 : g_main_loop_unref (loop);
812 : 3 : VALENT_RETURN (FALSE);
813 : : }
814 : :
815 : : /* Set the thread-context variables before attaching to the context */
816 : 3 : valent_object_lock (VALENT_OBJECT (self));
817 : 3 : self->dnssd = g_object_ref (services);
818 : 3 : self->udp_context = g_main_loop_ref (loop);
819 [ - + ]: 3 : self->udp_socket4 = socket4 ? g_object_ref (socket4) : NULL;
820 [ + - ]: 3 : self->udp_socket6 = socket6 ? g_object_ref (socket6) : NULL;
821 : 3 : valent_object_unlock (VALENT_OBJECT (self));
822 : :
823 : 3 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
824 : :
825 [ + - ]: 3 : if (socket6 != NULL)
826 : : {
827 : 6 : g_autoptr (GSource) source = NULL;
828 : :
829 : 3 : source = g_socket_create_source (socket6, G_IO_IN, destroy);
830 : 3 : g_source_set_callback (source,
831 : : G_SOURCE_FUNC (valent_lan_channel_service_socket_recv),
832 : : g_object_ref (self),
833 : : g_object_unref);
834 [ + - ]: 3 : g_source_attach (source, context);
835 : : }
836 : :
837 [ - + ]: 3 : if (socket4 != NULL)
838 : : {
839 : 3 : g_autoptr (GSource) source = NULL;
840 : :
841 : 0 : source = g_socket_create_source (socket4, G_IO_IN, destroy);
842 : 0 : g_source_set_callback (source,
843 : : G_SOURCE_FUNC (valent_lan_channel_service_socket_recv),
844 : : g_object_ref (self),
845 : : g_object_unref);
846 [ # # ]: 0 : g_source_attach (source, context);
847 : : }
848 : :
849 : 3 : valent_lan_dnssd_attach (VALENT_LAN_DNSSD (services), context);
850 : :
851 [ - + + - ]: 6 : VALENT_RETURN (TRUE);
852 : : }
853 : :
854 : :
855 : : /*
856 : : * ValentChannelService
857 : : */
858 : : static void
859 : 6 : valent_lan_channel_service_build_identity (ValentChannelService *service)
860 : : {
861 : 6 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (service);
862 : 6 : ValentChannelServiceClass *klass;
863 : 12 : g_autoptr (JsonNode) identity = NULL;
864 : :
865 [ + - ]: 6 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (service));
866 : :
867 : : /* Chain-up */
868 : 6 : klass = VALENT_CHANNEL_SERVICE_CLASS (valent_lan_channel_service_parent_class);
869 : 6 : klass->build_identity (service);
870 : :
871 : : /* Set the tcpPort on the packet */
872 : 6 : identity = valent_channel_service_ref_identity (service);
873 : :
874 [ + - ]: 6 : if (identity != NULL)
875 : : {
876 : 6 : JsonObject *body;
877 : :
878 : 6 : body = valent_packet_get_body (identity);
879 : 6 : json_object_set_int_member (body, "tcpPort", self->tcp_port);
880 : : }
881 : 6 : }
882 : :
883 : : static void
884 : 3 : valent_lan_channel_service_channel (ValentChannelService *service,
885 : : ValentChannel *channel)
886 : : {
887 : 3 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (service);
888 : 3 : GTlsCertificate *peer_certificate = NULL;
889 : 3 : const char *device_id = NULL;
890 : :
891 [ + - ]: 3 : g_assert (VALENT_IS_MAIN_THREAD ());
892 [ - + ]: 3 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
893 [ - + ]: 3 : g_assert (VALENT_IS_LAN_CHANNEL (channel));
894 : :
895 : 3 : peer_certificate = valent_channel_get_peer_certificate (channel);
896 : 3 : device_id = valent_certificate_get_common_name (peer_certificate);
897 : :
898 : 3 : valent_object_lock (VALENT_OBJECT (service));
899 [ - + ]: 6 : g_hash_table_replace (self->channels, g_strdup (device_id), channel);
900 : 3 : g_signal_connect_object (channel,
901 : : "destroy",
902 : : G_CALLBACK (on_channel_destroyed),
903 : : self,
904 : : G_CONNECT_SWAPPED);
905 : 3 : valent_object_unlock (VALENT_OBJECT (service));
906 : 3 : }
907 : :
908 : : static void
909 : 1 : valent_lan_channel_service_identify (ValentChannelService *service,
910 : : const char *target)
911 : : {
912 : 1 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (service);
913 : 2 : g_autoptr (GSocketAddress) address = NULL;
914 : :
915 [ + - ]: 1 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (self));
916 : :
917 [ + - ]: 1 : if (!self->network_available)
918 : : return;
919 : :
920 [ + - ]: 1 : if (target != NULL)
921 : : {
922 : 0 : g_autoptr (GSocketConnectable) naddr = NULL;
923 : 1 : const char *hostname = NULL;
924 : 1 : uint16_t port = 0;
925 : 1 : g_autoptr (GError) error = NULL;
926 : :
927 : 1 : naddr = g_network_address_parse (target,
928 : : VALENT_LAN_PROTOCOL_PORT,
929 : : &error);
930 : :
931 [ - + ]: 1 : if (naddr == NULL)
932 : : {
933 : 0 : g_warning ("%s(): failed to parse \"%s\": %s",
934 : : G_STRFUNC,
935 : : target,
936 : : error->message);
937 [ # # ]: 0 : return;
938 : : }
939 : :
940 : 1 : hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (naddr));
941 : 1 : port = g_network_address_get_port (G_NETWORK_ADDRESS (naddr));
942 [ - + ]: 1 : address = g_inet_socket_address_new_from_string (hostname, port);
943 : : }
944 : :
945 [ - + ]: 1 : if (address == NULL)
946 : 0 : address = g_inet_socket_address_new_from_string (self->broadcast_address,
947 : 0 : self->port);
948 : :
949 [ + - ]: 1 : valent_lan_channel_service_socket_queue (self, address);
950 : : }
951 : :
952 : : static void
953 : 3 : valent_lan_channel_service_init_task (GTask *task,
954 : : gpointer source_object,
955 : : gpointer task_data,
956 : : GCancellable *cancellable)
957 : : {
958 : 3 : ValentLanChannelService *self = source_object;
959 : 3 : g_autoptr (GError) error = NULL;
960 : :
961 [ + - ]: 3 : if (g_task_return_error_if_cancelled (task))
962 : : return;
963 : :
964 [ + - - + ]: 6 : if (!valent_lan_channel_service_tcp_setup (self, cancellable, &error) ||
965 : 3 : !valent_lan_channel_service_udp_setup (self, cancellable, &error))
966 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
967 : :
968 [ - + ]: 3 : g_task_return_boolean (task, TRUE);
969 : : }
970 : :
971 : : /*
972 : : * GAsyncInitable
973 : : */
974 : : static void
975 : 3 : valent_lan_channel_service_init_async (GAsyncInitable *initable,
976 : : int io_priority,
977 : : GCancellable *cancellable,
978 : : GAsyncReadyCallback callback,
979 : : gpointer user_data)
980 : : {
981 : 3 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (initable);
982 : 6 : g_autoptr (GTask) task = NULL;
983 [ + - ]: 3 : g_autoptr (GCancellable) destroy = NULL;
984 : :
985 [ + - ]: 3 : g_assert (VALENT_IS_LAN_CHANNEL_SERVICE (initable));
986 [ - + - - : 3 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
987 : :
988 : : /* Cancel initialization if the object is destroyed */
989 : 3 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (initable),
990 : : cancellable);
991 : :
992 : 3 : self->network_available = g_network_monitor_get_network_available (self->monitor);
993 : 3 : g_signal_connect_object (self->monitor,
994 : : "network-changed",
995 : : G_CALLBACK (on_network_changed),
996 : : self, 0);
997 : :
998 : 3 : task = g_task_new (initable, destroy, callback, user_data);
999 : 3 : g_task_set_priority (task, io_priority);
1000 [ + - ]: 3 : g_task_set_source_tag (task, valent_lan_channel_service_init_async);
1001 [ + - ]: 3 : g_task_run_in_thread (task, valent_lan_channel_service_init_task);
1002 : 3 : }
1003 : :
1004 : : static void
1005 : 1 : g_async_initable_iface_init (GAsyncInitableIface *iface)
1006 : : {
1007 : 1 : iface->init_async = valent_lan_channel_service_init_async;
1008 : 1 : }
1009 : :
1010 : : /*
1011 : : * ValentObject
1012 : : */
1013 : : static void
1014 : 6 : valent_lan_channel_service_destroy (ValentObject *object)
1015 : : {
1016 : 6 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (object);
1017 : :
1018 : 6 : g_signal_handlers_disconnect_by_data (self->monitor, self);
1019 : :
1020 [ + + ]: 6 : if (self->udp_context != NULL)
1021 : : {
1022 [ + - ]: 3 : if (self->dnssd != NULL)
1023 : : {
1024 : 3 : g_signal_handlers_disconnect_by_data (self->dnssd, self);
1025 [ + - ]: 3 : g_clear_object (&self->dnssd);
1026 : : }
1027 : :
1028 [ - + ]: 3 : g_clear_object (&self->udp_socket4);
1029 [ + - ]: 3 : g_clear_object (&self->udp_socket6);
1030 : 3 : g_main_loop_quit (self->udp_context);
1031 [ + - ]: 3 : g_clear_pointer (&self->udp_context, g_main_loop_unref);
1032 : : }
1033 : :
1034 [ + + ]: 6 : if (self->listener != NULL)
1035 : : {
1036 : 3 : g_socket_service_stop (G_SOCKET_SERVICE (self->listener));
1037 : 3 : g_socket_listener_close (G_SOCKET_LISTENER (self->listener));
1038 [ + - ]: 3 : g_clear_object (&self->listener);
1039 : : }
1040 : :
1041 : 6 : VALENT_OBJECT_CLASS (valent_lan_channel_service_parent_class)->destroy (object);
1042 : 6 : }
1043 : :
1044 : : /*
1045 : : * GObject
1046 : : */
1047 : : static void
1048 : 3 : valent_lan_channel_service_finalize (GObject *object)
1049 : : {
1050 : 3 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (object);
1051 : :
1052 [ + - ]: 3 : g_clear_pointer (&self->broadcast_address, g_free);
1053 [ + - ]: 3 : g_clear_pointer (&self->channels, g_hash_table_unref);
1054 : :
1055 : 3 : G_OBJECT_CLASS (valent_lan_channel_service_parent_class)->finalize (object);
1056 : 3 : }
1057 : :
1058 : : static void
1059 : 0 : valent_lan_channel_service_get_property (GObject *object,
1060 : : guint prop_id,
1061 : : GValue *value,
1062 : : GParamSpec *pspec)
1063 : : {
1064 : 0 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (object);
1065 : :
1066 [ # # # ]: 0 : switch ((ValentLanChannelServiceProperty)prop_id)
1067 : : {
1068 : 0 : case PROP_BROADCAST_ADDRESS:
1069 : 0 : g_value_set_string (value, self->broadcast_address);
1070 : 0 : break;
1071 : :
1072 : 0 : case PROP_PORT:
1073 : 0 : g_value_set_uint (value, self->port);
1074 : 0 : break;
1075 : :
1076 : 0 : default:
1077 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1078 : : }
1079 : 0 : }
1080 : :
1081 : : static void
1082 : 6 : valent_lan_channel_service_set_property (GObject *object,
1083 : : guint prop_id,
1084 : : const GValue *value,
1085 : : GParamSpec *pspec)
1086 : : {
1087 : 6 : ValentLanChannelService *self = VALENT_LAN_CHANNEL_SERVICE (object);
1088 : :
1089 [ + + - ]: 6 : switch ((ValentLanChannelServiceProperty)prop_id)
1090 : : {
1091 : 3 : case PROP_BROADCAST_ADDRESS:
1092 : 3 : self->broadcast_address = g_value_dup_string (value);
1093 : 3 : break;
1094 : :
1095 : 3 : case PROP_PORT:
1096 : 3 : self->port = g_value_get_uint (value);
1097 : 3 : break;
1098 : :
1099 : 0 : default:
1100 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1101 : : }
1102 : 6 : }
1103 : :
1104 : : static void
1105 : 1 : valent_lan_channel_service_class_init (ValentLanChannelServiceClass *klass)
1106 : : {
1107 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
1108 : 1 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
1109 : 1 : ValentChannelServiceClass *service_class = VALENT_CHANNEL_SERVICE_CLASS (klass);
1110 : :
1111 : 1 : object_class->finalize = valent_lan_channel_service_finalize;
1112 : 1 : object_class->get_property = valent_lan_channel_service_get_property;
1113 : 1 : object_class->set_property = valent_lan_channel_service_set_property;
1114 : :
1115 : 1 : vobject_class->destroy = valent_lan_channel_service_destroy;
1116 : :
1117 : 1 : service_class->build_identity = valent_lan_channel_service_build_identity;
1118 : 1 : service_class->channel = valent_lan_channel_service_channel;
1119 : 1 : service_class->identify = valent_lan_channel_service_identify;
1120 : :
1121 : : /**
1122 : : * ValentLanChannelService:broadcast-address:
1123 : : *
1124 : : * The UDP broadcast address for the service.
1125 : : *
1126 : : * This available as a construct property primarily for use in unit tests.
1127 : : */
1128 : 2 : properties [PROP_BROADCAST_ADDRESS] =
1129 : 1 : g_param_spec_string ("broadcast-address", NULL, NULL,
1130 : : VALENT_LAN_PROTOCOL_ADDR,
1131 : : (G_PARAM_READWRITE |
1132 : : G_PARAM_CONSTRUCT_ONLY |
1133 : : G_PARAM_EXPLICIT_NOTIFY |
1134 : : G_PARAM_STATIC_STRINGS));
1135 : :
1136 : : /**
1137 : : * ValentLanChannelService:port:
1138 : : *
1139 : : * The TCP/IP port for the service.
1140 : : *
1141 : : * The current KDE Connect protocol (v7) defines port 1716 as the default.
1142 : : *
1143 : : * This available as a construct property primarily for use in unit tests.
1144 : : */
1145 : 2 : properties [PROP_PORT] =
1146 : 1 : g_param_spec_uint ("port", NULL, NULL,
1147 : : VALENT_LAN_PROTOCOL_PORT_MIN, VALENT_LAN_PROTOCOL_PORT_MAX,
1148 : : VALENT_LAN_PROTOCOL_PORT,
1149 : : (G_PARAM_READWRITE |
1150 : : G_PARAM_CONSTRUCT_ONLY |
1151 : : G_PARAM_EXPLICIT_NOTIFY |
1152 : : G_PARAM_STATIC_STRINGS));
1153 : :
1154 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
1155 : 1 : }
1156 : :
1157 : : static void
1158 : 3 : valent_lan_channel_service_init (ValentLanChannelService *self)
1159 : : {
1160 : 3 : self->channels = g_hash_table_new_full (g_str_hash,
1161 : : g_str_equal,
1162 : : g_free,
1163 : : NULL);
1164 : 3 : self->monitor = g_network_monitor_get_default ();
1165 : 3 : }
1166 : :
|