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