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