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