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"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <gio/gnetworking.h>
10 : : #include <json-glib/json-glib.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-lan-utils.h"
14 : :
15 : : #include "valent-lan-channel.h"
16 : :
17 : :
18 : : struct _ValentLanChannel
19 : : {
20 : : ValentChannel parent_instance;
21 : :
22 : : char *host;
23 : : uint16_t port;
24 : : };
25 : :
26 [ + + + - ]: 12 : G_DEFINE_FINAL_TYPE (ValentLanChannel, valent_lan_channel, VALENT_TYPE_CHANNEL)
27 : :
28 : : typedef enum {
29 : : PROP_HOST = 1,
30 : : PROP_PORT,
31 : : } ValentLanChannelProperty;
32 : :
33 : : static GParamSpec *properties[PROP_PORT + 1] = { NULL, };
34 : :
35 : :
36 : : /*
37 : : * ValentChannel
38 : : */
39 : : static GIOStream *
40 : 1 : valent_lan_channel_download (ValentChannel *channel,
41 : : JsonNode *packet,
42 : : GCancellable *cancellable,
43 : : GError **error)
44 : : {
45 : 1 : ValentLanChannel *self = VALENT_LAN_CHANNEL (channel);
46 : 1 : JsonObject *info;
47 : 1 : int64_t port;
48 : 1 : goffset size;
49 : 2 : g_autoptr (GSocketClient) client = NULL;
50 [ + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
51 [ + - ]: 1 : g_autoptr (GTlsCertificate) certificate = NULL;
52 [ + - ]: 1 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
53 [ + - ]: 1 : g_autofree char *host = NULL;
54 : 1 : g_autoptr (GIOStream) tls_stream = NULL;
55 : :
56 [ - + ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
57 [ + - ]: 1 : g_assert (VALENT_IS_PACKET (packet));
58 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
59 [ + - + - ]: 1 : g_assert (error == NULL || *error == NULL);
60 : :
61 : : /* Get the connection information
62 : : */
63 [ + - ]: 1 : if ((info = valent_packet_get_payload_full (packet, &size, error)) == NULL)
64 : : return NULL;
65 : :
66 [ + - ]: 1 : if ((port = json_object_get_int_member (info, "port")) == 0 ||
67 [ - + ]: 1 : (port < VALENT_LAN_TRANSFER_PORT_MIN || port > VALENT_LAN_TRANSFER_PORT_MAX))
68 : : {
69 : 0 : g_set_error (error,
70 : : VALENT_PACKET_ERROR,
71 : : VALENT_PACKET_ERROR_INVALID_FIELD,
72 : : "expected \"port\" field holding a uint16 between %u-%u",
73 : : VALENT_LAN_TRANSFER_PORT_MIN,
74 : : VALENT_LAN_TRANSFER_PORT_MAX);
75 : 0 : return NULL;
76 : : }
77 : :
78 : 1 : valent_object_lock (VALENT_OBJECT (self));
79 [ - + ]: 1 : host = g_strdup (self->host);
80 : 1 : valent_object_unlock (VALENT_OBJECT (self));
81 : :
82 : : /* Open a connection to the host at the expected port
83 : : */
84 : 1 : client = g_object_new (G_TYPE_SOCKET_CLIENT,
85 : : "enable-proxy", FALSE,
86 : : NULL);
87 : 1 : connection = g_socket_client_connect_to_host (client,
88 : : host,
89 : : (uint16_t)port,
90 : : cancellable,
91 : : error);
92 [ + - ]: 1 : if (connection == NULL)
93 : : return NULL;
94 : :
95 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
96 : : * acts as the TLS client when opening TCP connections.
97 : : */
98 : 1 : certificate = valent_channel_ref_certificate (channel);
99 : 1 : peer_certificate = valent_channel_ref_peer_certificate (channel);
100 : 1 : tls_stream = valent_lan_connection_handshake (connection,
101 : : certificate,
102 : : peer_certificate,
103 : : TRUE, /* is_client */
104 : : cancellable,
105 : : error);
106 [ - + ]: 1 : if (tls_stream == NULL)
107 : : {
108 : 0 : g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
109 : 0 : return NULL;
110 : : }
111 : :
112 : : return g_steal_pointer (&tls_stream);
113 : : }
114 : :
115 : : static void
116 : 0 : valent_lan_connection_handshake_cb (GSocketConnection *connection,
117 : : GAsyncResult *result,
118 : : gpointer user_data)
119 : : {
120 : 0 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
121 [ # # # # ]: 0 : g_autoptr (GIOStream) ret = NULL;
122 : 0 : GError *error = NULL;
123 : :
124 : 0 : ret = valent_lan_connection_handshake_finish (connection, result, &error);
125 [ # # ]: 0 : if (ret == NULL)
126 : : {
127 : 0 : g_task_return_error (task, g_steal_pointer (&error));
128 [ # # ]: 0 : return;
129 : : }
130 : :
131 [ # # ]: 0 : g_task_return_pointer (task, g_steal_pointer (&ret), g_object_unref);
132 : : }
133 : :
134 : : static void
135 : 0 : g_socket_client_connect_to_host_cb (GSocketClient *client,
136 : : GAsyncResult *result,
137 : : gpointer user_data)
138 : : {
139 : 0 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
140 : 0 : ValentChannel *channel = g_task_get_source_object (task);
141 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
142 [ # # # # ]: 0 : g_autoptr (GSocketConnection) connection = NULL;
143 : 0 : g_autoptr (GTlsCertificate) certificate = NULL;
144 [ # # # # ]: 0 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
145 : 0 : GError *error = NULL;
146 : :
147 : 0 : connection = g_socket_client_connect_to_host_finish (client, result, &error);
148 [ # # ]: 0 : if (connection == NULL)
149 : : {
150 : 0 : g_task_return_error (task, g_steal_pointer (&error));
151 [ # # ]: 0 : return;
152 : : }
153 : :
154 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
155 : : * acts as the TLS client when opening TCP connections.
156 : : */
157 : 0 : certificate = valent_channel_ref_certificate (channel);
158 : 0 : peer_certificate = valent_channel_ref_peer_certificate (channel);
159 [ # # ]: 0 : valent_lan_connection_handshake_async (connection,
160 : : certificate,
161 : : peer_certificate,
162 : : TRUE, /* is_client */
163 : : cancellable,
164 : : (GAsyncReadyCallback)valent_lan_connection_handshake_cb,
165 : : g_object_ref (task));
166 : : }
167 : :
168 : : static void
169 : 0 : valent_lan_channel_download_async (ValentChannel *channel,
170 : : JsonNode *packet,
171 : : GCancellable *cancellable,
172 : : GAsyncReadyCallback callback,
173 : : gpointer user_data)
174 : : {
175 : 0 : ValentLanChannel *self = VALENT_LAN_CHANNEL (channel);
176 : 0 : g_autoptr (GTask) task = NULL;
177 : 0 : JsonObject *info;
178 : 0 : int64_t port;
179 : 0 : goffset size;
180 [ # # # # ]: 0 : g_autoptr (GSocketClient) client = NULL;
181 [ # # # # ]: 0 : g_autofree char *host = NULL;
182 : 0 : GError *error = NULL;
183 : :
184 [ # # ]: 0 : g_assert (VALENT_IS_CHANNEL (channel));
185 [ # # ]: 0 : g_assert (VALENT_IS_PACKET (packet));
186 [ # # # # : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # ]
187 : :
188 : 0 : task = g_task_new (channel, cancellable, callback, user_data);
189 [ # # ]: 0 : g_task_set_source_tag (task, valent_lan_channel_download_async);
190 : 0 : g_task_set_task_data (task,
191 : 0 : json_node_ref (packet),
192 : : (GDestroyNotify)json_node_unref);
193 : :
194 : : /* Get the connection information
195 : : */
196 : 0 : info = valent_packet_get_payload_full (packet, &size, &error);
197 [ # # ]: 0 : if (info == NULL)
198 : : {
199 : 0 : g_task_return_error (task, g_steal_pointer (&error));
200 : 0 : return;
201 : : }
202 : :
203 [ # # ]: 0 : if ((port = json_object_get_int_member (info, "port")) == 0 ||
204 [ # # ]: 0 : (port < VALENT_LAN_TRANSFER_PORT_MIN || port > VALENT_LAN_TRANSFER_PORT_MAX))
205 : : {
206 : 0 : g_task_return_new_error (task,
207 : : VALENT_PACKET_ERROR,
208 : : VALENT_PACKET_ERROR_INVALID_FIELD,
209 : : "expected \"port\" field holding a uint16 between %u-%u",
210 : : VALENT_LAN_TRANSFER_PORT_MIN,
211 : : VALENT_LAN_TRANSFER_PORT_MAX);
212 : 0 : return;
213 : : }
214 : :
215 : 0 : valent_object_lock (VALENT_OBJECT (self));
216 [ # # ]: 0 : host = g_strdup (self->host);
217 : 0 : valent_object_unlock (VALENT_OBJECT (self));
218 : :
219 : : /* Open a connection to the host at the expected port
220 : : */
221 : 0 : client = g_object_new (G_TYPE_SOCKET_CLIENT,
222 : : "enable-proxy", FALSE,
223 : : NULL);
224 : 0 : g_socket_client_connect_to_host_async (client,
225 : : host,
226 : : (uint16_t)port,
227 : : cancellable,
228 : : (GAsyncReadyCallback)g_socket_client_connect_to_host_cb,
229 : : g_object_ref (task));
230 : : }
231 : :
232 : : static GIOStream *
233 : 1 : valent_lan_channel_upload (ValentChannel *channel,
234 : : JsonNode *packet,
235 : : GCancellable *cancellable,
236 : : GError **error)
237 : : {
238 : 1 : JsonObject *info;
239 : 2 : g_autoptr (GSocketListener) listener = NULL;
240 [ + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
241 : 1 : uint16_t port = VALENT_LAN_TRANSFER_PORT_MIN;
242 [ + - ]: 1 : g_autoptr (GTlsCertificate) certificate = NULL;
243 [ + - ]: 1 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
244 [ + - ]: 1 : g_autoptr (GIOStream) tls_stream = NULL;
245 : :
246 [ - + ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
247 [ + - ]: 1 : g_assert (VALENT_IS_PACKET (packet));
248 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
249 [ + - + - ]: 1 : g_assert (error == NULL || *error == NULL);
250 : :
251 : : /* Find an open port and prepare the payload information
252 : : */
253 : 1 : listener = g_socket_listener_new ();
254 [ - + ]: 1 : while (!g_socket_listener_add_inet_port (listener, port, NULL, error))
255 : : {
256 [ # # ]: 0 : if (port >= VALENT_LAN_TRANSFER_PORT_MAX)
257 : : return NULL;
258 : :
259 : 0 : port++;
260 : 0 : g_clear_error (error);
261 : : }
262 : :
263 : 1 : info = json_object_new();
264 : 1 : json_object_set_int_member (info, "port", (int64_t)port);
265 : 1 : valent_packet_set_payload_info (packet, info);
266 : :
267 : : /* Queue the packet and wait for the incoming connection
268 : : */
269 : 1 : valent_channel_write_packet (channel, packet, cancellable, NULL, NULL);
270 : 1 : connection = g_socket_listener_accept (listener, NULL, cancellable, error);
271 : 1 : g_socket_listener_close (listener);
272 [ - + ]: 1 : if (connection == NULL)
273 : : return NULL;
274 : :
275 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
276 : : * acts as the TLS server when accepting TCP connections.
277 : : */
278 : 1 : certificate = valent_channel_ref_certificate (channel);
279 : 1 : peer_certificate = valent_channel_ref_peer_certificate (channel);
280 : 1 : tls_stream = valent_lan_connection_handshake (connection,
281 : : certificate,
282 : : peer_certificate,
283 : : FALSE, /* is_client */
284 : : cancellable,
285 : : error);
286 [ - + ]: 1 : if (tls_stream == NULL)
287 : : {
288 : 0 : g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
289 : 0 : return NULL;
290 : : }
291 : :
292 : : return g_steal_pointer (&tls_stream);
293 : : }
294 : :
295 : : static void
296 : 0 : g_socket_listener_accept_cb (GSocketListener *listener,
297 : : GAsyncResult *result,
298 : : gpointer user_data)
299 : : {
300 : 0 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
301 : 0 : ValentChannel *channel = g_task_get_source_object (task);
302 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
303 [ # # # # ]: 0 : g_autoptr (GSocketConnection) connection = NULL;
304 : 0 : g_autoptr (GTlsCertificate) certificate = NULL;
305 [ # # # # ]: 0 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
306 : 0 : GError *error = NULL;
307 : :
308 : 0 : connection = g_socket_listener_accept_finish (listener, result, NULL, &error);
309 : 0 : g_socket_listener_close (listener);
310 [ # # ]: 0 : if (connection == NULL)
311 : : {
312 : 0 : g_task_return_error (task, g_steal_pointer (&error));
313 [ # # ]: 0 : return;
314 : : }
315 : :
316 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
317 : : * acts as the TLS client when opening TCP connections.
318 : : */
319 : 0 : certificate = valent_channel_ref_certificate (channel);
320 : 0 : peer_certificate = valent_channel_ref_peer_certificate (channel);
321 [ # # ]: 0 : valent_lan_connection_handshake_async (connection,
322 : : certificate,
323 : : peer_certificate,
324 : : FALSE, /* is_client */
325 : : cancellable,
326 : : (GAsyncReadyCallback)valent_lan_connection_handshake_cb,
327 : : g_object_ref (task));
328 : : }
329 : :
330 : : static void
331 : 0 : valent_lan_channel_upload_async (ValentChannel *channel,
332 : : JsonNode *packet,
333 : : GCancellable *cancellable,
334 : : GAsyncReadyCallback callback,
335 : : gpointer user_data)
336 : : {
337 : 0 : g_autoptr (GTask) task = NULL;
338 [ # # # # ]: 0 : g_autoptr (GSocketListener) listener = NULL;
339 : 0 : uint16_t port = VALENT_LAN_TRANSFER_PORT_MIN;
340 : 0 : JsonObject *info;
341 : 0 : GError *error = NULL;
342 : :
343 [ # # ]: 0 : g_assert (VALENT_IS_CHANNEL (channel));
344 [ # # ]: 0 : g_assert (VALENT_IS_PACKET (packet));
345 [ # # # # : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # ]
346 : :
347 : 0 : task = g_task_new (channel, cancellable, callback, user_data);
348 [ # # ]: 0 : g_task_set_source_tag (task, valent_lan_channel_upload_async);
349 : 0 : g_task_set_task_data (task,
350 : 0 : json_node_ref (packet),
351 : : (GDestroyNotify)json_node_unref);
352 : :
353 : : /* Find an open port and prepare the payload information
354 : : */
355 : 0 : listener = g_socket_listener_new ();
356 [ # # ]: 0 : while (!g_socket_listener_add_inet_port (listener, port, NULL, &error))
357 : : {
358 [ # # ]: 0 : if (port >= VALENT_LAN_TRANSFER_PORT_MAX)
359 : : {
360 : 0 : g_task_return_error (task, g_steal_pointer (&error));
361 [ # # ]: 0 : return;
362 : : }
363 : :
364 : 0 : port++;
365 : 0 : g_clear_error (&error);
366 : : }
367 : :
368 : 0 : info = json_object_new();
369 : 0 : json_object_set_int_member (info, "port", (int64_t)port);
370 : 0 : valent_packet_set_payload_info (packet, info);
371 : :
372 : : /* Listen for the incoming connection and queue the packet
373 : : */
374 : 0 : g_socket_listener_accept_async (listener,
375 : : cancellable,
376 : : (GAsyncReadyCallback)g_socket_listener_accept_cb,
377 : : g_object_ref (task));
378 [ # # ]: 0 : valent_channel_write_packet (channel, packet, cancellable, NULL, NULL);
379 : : }
380 : :
381 : : /*
382 : : * GObject
383 : : */
384 : : static void
385 : 6 : valent_lan_channel_finalize (GObject *object)
386 : : {
387 : 6 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
388 : :
389 : 6 : valent_object_lock (VALENT_OBJECT (self));
390 [ + - ]: 6 : g_clear_pointer (&self->host, g_free);
391 : 6 : valent_object_unlock (VALENT_OBJECT (self));
392 : :
393 : 6 : G_OBJECT_CLASS (valent_lan_channel_parent_class)->finalize (object);
394 : 6 : }
395 : :
396 : : static void
397 : 2 : valent_lan_channel_get_property (GObject *object,
398 : : guint prop_id,
399 : : GValue *value,
400 : : GParamSpec *pspec)
401 : : {
402 : 2 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
403 : :
404 [ + + - ]: 2 : switch ((ValentLanChannelProperty)prop_id)
405 : : {
406 : : case PROP_HOST:
407 : 1 : valent_object_lock (VALENT_OBJECT (self));
408 : 1 : g_value_set_string (value, self->host);
409 : 1 : valent_object_unlock (VALENT_OBJECT (self));
410 : 1 : break;
411 : :
412 : : case PROP_PORT:
413 : 1 : valent_object_lock (VALENT_OBJECT (self));
414 : 1 : g_value_set_uint (value, self->port);
415 : 1 : valent_object_unlock (VALENT_OBJECT (self));
416 : 1 : break;
417 : :
418 : 0 : default:
419 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
420 : : }
421 : 2 : }
422 : :
423 : : static void
424 : 12 : valent_lan_channel_set_property (GObject *object,
425 : : guint prop_id,
426 : : const GValue *value,
427 : : GParamSpec *pspec)
428 : : {
429 : 12 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
430 : :
431 [ + + - ]: 12 : switch ((ValentLanChannelProperty)prop_id)
432 : : {
433 : : case PROP_HOST:
434 : 6 : valent_object_lock (VALENT_OBJECT (self));
435 : 6 : self->host = g_value_dup_string (value);
436 : 6 : valent_object_unlock (VALENT_OBJECT (self));
437 : 6 : break;
438 : :
439 : : case PROP_PORT:
440 : 6 : valent_object_lock (VALENT_OBJECT (self));
441 : 6 : self->port = g_value_get_uint (value);
442 : 6 : valent_object_unlock (VALENT_OBJECT (self));
443 : 6 : break;
444 : :
445 : 0 : default:
446 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
447 : : }
448 : 12 : }
449 : :
450 : : static void
451 : 1 : valent_lan_channel_class_init (ValentLanChannelClass *klass)
452 : : {
453 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
454 : 1 : ValentChannelClass *channel_class = VALENT_CHANNEL_CLASS (klass);
455 : :
456 : 1 : object_class->finalize = valent_lan_channel_finalize;
457 : 1 : object_class->get_property = valent_lan_channel_get_property;
458 : 1 : object_class->set_property = valent_lan_channel_set_property;
459 : :
460 : 1 : channel_class->download = valent_lan_channel_download;
461 : 1 : channel_class->download_async = valent_lan_channel_download_async;
462 : 1 : channel_class->upload = valent_lan_channel_upload;
463 : 1 : channel_class->upload_async = valent_lan_channel_upload_async;
464 : :
465 : : /**
466 : : * ValentLanChannel:host:
467 : : *
468 : : * The remote host for the channel.
469 : : *
470 : : * This could be an IPv4 or IPv6 address, or a hostname.
471 : : */
472 : 2 : properties [PROP_HOST] =
473 : 1 : g_param_spec_string ("host", NULL, NULL,
474 : : NULL,
475 : : (G_PARAM_READWRITE |
476 : : G_PARAM_CONSTRUCT_ONLY |
477 : : G_PARAM_EXPLICIT_NOTIFY |
478 : : G_PARAM_STATIC_STRINGS));
479 : :
480 : : /**
481 : : * ValentLanChannel:port:
482 : : *
483 : : * The remote port for the channel.
484 : : */
485 : 2 : properties [PROP_PORT] =
486 : 1 : g_param_spec_uint ("port", NULL, NULL,
487 : : 0, G_MAXUINT16,
488 : : VALENT_LAN_PROTOCOL_PORT,
489 : : (G_PARAM_READWRITE |
490 : : G_PARAM_CONSTRUCT_ONLY |
491 : : G_PARAM_EXPLICIT_NOTIFY |
492 : : G_PARAM_STATIC_STRINGS));
493 : :
494 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
495 : 1 : }
496 : :
497 : : static void
498 : 6 : valent_lan_channel_init (ValentLanChannel *self)
499 : : {
500 : 6 : }
501 : :
|