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 void
40 : 2 : valent_lan_connection_handshake_cb (GSocketConnection *connection,
41 : : GAsyncResult *result,
42 : : gpointer user_data)
43 : : {
44 : 4 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
45 [ - - + - ]: 2 : g_autoptr (GIOStream) ret = NULL;
46 : 2 : GError *error = NULL;
47 : :
48 : 2 : ret = valent_lan_connection_handshake_finish (connection, result, &error);
49 [ - + ]: 2 : if (ret == NULL)
50 : : {
51 : 0 : g_task_return_error (task, g_steal_pointer (&error));
52 [ # # ]: 0 : return;
53 : : }
54 : :
55 [ + - ]: 2 : g_task_return_pointer (task, g_steal_pointer (&ret), g_object_unref);
56 : : }
57 : :
58 : : static void
59 : 1 : g_socket_client_connect_to_host_cb (GSocketClient *client,
60 : : GAsyncResult *result,
61 : : gpointer user_data)
62 : : {
63 : 2 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
64 : 1 : ValentChannel *channel = g_task_get_source_object (task);
65 : 1 : GCancellable *cancellable = g_task_get_cancellable (task);
66 [ - - + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
67 : 1 : g_autoptr (GTlsCertificate) certificate = NULL;
68 [ - - + - ]: 1 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
69 : 1 : GError *error = NULL;
70 : :
71 : 1 : connection = g_socket_client_connect_to_host_finish (client, result, &error);
72 [ - + ]: 1 : if (connection == NULL)
73 : : {
74 : 0 : g_task_return_error (task, g_steal_pointer (&error));
75 [ # # ]: 0 : return;
76 : : }
77 : :
78 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
79 : : * acts as the TLS client when opening TCP connections.
80 : : */
81 : 1 : certificate = valent_channel_ref_certificate (channel);
82 : 1 : peer_certificate = valent_channel_ref_peer_certificate (channel);
83 [ + - ]: 1 : valent_lan_connection_handshake_async (connection,
84 : : certificate,
85 : : peer_certificate,
86 : : TRUE, /* is_client */
87 : : cancellable,
88 : : (GAsyncReadyCallback)valent_lan_connection_handshake_cb,
89 : : g_object_ref (task));
90 : : }
91 : :
92 : : static void
93 : 1 : valent_lan_channel_download (ValentChannel *channel,
94 : : JsonNode *packet,
95 : : GCancellable *cancellable,
96 : : GAsyncReadyCallback callback,
97 : : gpointer user_data)
98 : : {
99 : 1 : ValentLanChannel *self = VALENT_LAN_CHANNEL (channel);
100 : 1 : g_autoptr (GTask) task = NULL;
101 : 1 : JsonObject *info;
102 : 1 : int64_t port;
103 : 1 : goffset size;
104 [ + - - - ]: 1 : g_autoptr (GSocketClient) client = NULL;
105 : 1 : GError *error = NULL;
106 : :
107 [ - + ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
108 [ + - ]: 1 : g_assert (VALENT_IS_PACKET (packet));
109 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
110 : :
111 : 1 : task = g_task_new (channel, cancellable, callback, user_data);
112 [ + - ]: 1 : g_task_set_source_tag (task, valent_lan_channel_download);
113 : :
114 : : /* Get the connection information
115 : : */
116 : 1 : info = valent_packet_get_payload_full (packet, &size, &error);
117 [ - + ]: 1 : if (info == NULL)
118 : : {
119 : 0 : g_task_return_error (task, g_steal_pointer (&error));
120 : 0 : return;
121 : : }
122 : :
123 [ + - ]: 1 : if ((port = json_object_get_int_member (info, "port")) == 0 ||
124 [ - + ]: 1 : (port < VALENT_LAN_TRANSFER_PORT_MIN || port > VALENT_LAN_TRANSFER_PORT_MAX))
125 : : {
126 : 0 : g_task_return_new_error (task,
127 : : VALENT_PACKET_ERROR,
128 : : VALENT_PACKET_ERROR_INVALID_FIELD,
129 : : "expected \"port\" field holding a uint16 between %u-%u",
130 : : VALENT_LAN_TRANSFER_PORT_MIN,
131 : : VALENT_LAN_TRANSFER_PORT_MAX);
132 : 0 : return;
133 : : }
134 : :
135 : : /* Open a connection to the host at the expected port
136 : : */
137 : 1 : client = g_object_new (G_TYPE_SOCKET_CLIENT,
138 : : "enable-proxy", FALSE,
139 : : NULL);
140 [ + - ]: 2 : g_socket_client_connect_to_host_async (client,
141 : 1 : self->host,
142 : : (uint16_t)port,
143 : : cancellable,
144 : : (GAsyncReadyCallback)g_socket_client_connect_to_host_cb,
145 : : g_object_ref (task));
146 : : }
147 : :
148 : : static void
149 : 1 : g_socket_listener_accept_cb (GSocketListener *listener,
150 : : GAsyncResult *result,
151 : : gpointer user_data)
152 : : {
153 : 2 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
154 : 1 : ValentChannel *channel = g_task_get_source_object (task);
155 : 1 : GCancellable *cancellable = g_task_get_cancellable (task);
156 [ - - + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
157 : 1 : g_autoptr (GTlsCertificate) certificate = NULL;
158 [ - - + - ]: 1 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
159 : 1 : GError *error = NULL;
160 : :
161 : 1 : connection = g_socket_listener_accept_finish (listener, result, NULL, &error);
162 : 1 : g_socket_listener_close (listener);
163 [ - + ]: 1 : if (connection == NULL)
164 : : {
165 : 0 : g_task_return_error (task, g_steal_pointer (&error));
166 [ # # ]: 0 : return;
167 : : }
168 : :
169 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
170 : : * acts as the TLS client when opening TCP connections.
171 : : */
172 : 1 : certificate = valent_channel_ref_certificate (channel);
173 : 1 : peer_certificate = valent_channel_ref_peer_certificate (channel);
174 [ + - ]: 1 : valent_lan_connection_handshake_async (connection,
175 : : certificate,
176 : : peer_certificate,
177 : : FALSE, /* is_client */
178 : : cancellable,
179 : : (GAsyncReadyCallback)valent_lan_connection_handshake_cb,
180 : : g_object_ref (task));
181 : : }
182 : :
183 : : static void
184 : 1 : valent_lan_channel_upload (ValentChannel *channel,
185 : : JsonNode *packet,
186 : : GCancellable *cancellable,
187 : : GAsyncReadyCallback callback,
188 : : gpointer user_data)
189 : : {
190 : 1 : g_autoptr (GTask) task = NULL;
191 [ - - + - ]: 1 : g_autoptr (GSocketListener) listener = NULL;
192 : 1 : uint16_t port = VALENT_LAN_TRANSFER_PORT_MIN;
193 : 1 : JsonObject *info;
194 : 1 : GError *error = NULL;
195 : :
196 [ - + ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
197 [ + - ]: 1 : g_assert (VALENT_IS_PACKET (packet));
198 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
199 : :
200 : 1 : task = g_task_new (channel, cancellable, callback, user_data);
201 [ + - ]: 1 : g_task_set_source_tag (task, valent_lan_channel_upload);
202 : :
203 : : /* Find an open port and prepare the payload information
204 : : */
205 : 1 : listener = g_socket_listener_new ();
206 [ - + ]: 1 : while (!g_socket_listener_add_inet_port (listener, port, NULL, &error))
207 : : {
208 [ # # ]: 0 : if (port >= VALENT_LAN_TRANSFER_PORT_MAX)
209 : : {
210 : 0 : g_task_return_error (task, g_steal_pointer (&error));
211 [ # # ]: 0 : return;
212 : : }
213 : :
214 : 0 : port++;
215 : 0 : g_clear_error (&error);
216 : : }
217 : :
218 : 1 : info = json_object_new();
219 : 1 : json_object_set_int_member (info, "port", (int64_t)port);
220 : 1 : valent_packet_set_payload_info (packet, info);
221 : :
222 : : /* Listen for the incoming connection and queue the packet
223 : : */
224 : 1 : g_socket_listener_accept_async (listener,
225 : : cancellable,
226 : : (GAsyncReadyCallback)g_socket_listener_accept_cb,
227 : : g_object_ref (task));
228 [ + - ]: 1 : valent_channel_write_packet (channel, packet, cancellable, NULL, NULL);
229 : : }
230 : :
231 : : /*
232 : : * GObject
233 : : */
234 : : static void
235 : 6 : valent_lan_channel_finalize (GObject *object)
236 : : {
237 : 6 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
238 : :
239 [ + - ]: 6 : g_clear_pointer (&self->host, g_free);
240 : :
241 : 6 : G_OBJECT_CLASS (valent_lan_channel_parent_class)->finalize (object);
242 : 6 : }
243 : :
244 : : static void
245 : 2 : valent_lan_channel_get_property (GObject *object,
246 : : guint prop_id,
247 : : GValue *value,
248 : : GParamSpec *pspec)
249 : : {
250 : 2 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
251 : :
252 [ + + - ]: 2 : switch ((ValentLanChannelProperty)prop_id)
253 : : {
254 : 1 : case PROP_HOST:
255 : 1 : g_value_set_string (value, self->host);
256 : 1 : break;
257 : :
258 : 1 : case PROP_PORT:
259 : 1 : g_value_set_uint (value, self->port);
260 : 1 : break;
261 : :
262 : 0 : default:
263 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
264 : : }
265 : 2 : }
266 : :
267 : : static void
268 : 12 : valent_lan_channel_set_property (GObject *object,
269 : : guint prop_id,
270 : : const GValue *value,
271 : : GParamSpec *pspec)
272 : : {
273 : 12 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
274 : :
275 [ + + - ]: 12 : switch ((ValentLanChannelProperty)prop_id)
276 : : {
277 : 6 : case PROP_HOST:
278 : 6 : self->host = g_value_dup_string (value);
279 : 6 : break;
280 : :
281 : 6 : case PROP_PORT:
282 : 6 : self->port = g_value_get_uint (value);
283 : 6 : break;
284 : :
285 : 0 : default:
286 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
287 : : }
288 : 12 : }
289 : :
290 : : static void
291 : 1 : valent_lan_channel_class_init (ValentLanChannelClass *klass)
292 : : {
293 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
294 : 1 : ValentChannelClass *channel_class = VALENT_CHANNEL_CLASS (klass);
295 : :
296 : 1 : object_class->finalize = valent_lan_channel_finalize;
297 : 1 : object_class->get_property = valent_lan_channel_get_property;
298 : 1 : object_class->set_property = valent_lan_channel_set_property;
299 : :
300 : 1 : channel_class->download = valent_lan_channel_download;
301 : 1 : channel_class->upload = valent_lan_channel_upload;
302 : :
303 : : /**
304 : : * ValentLanChannel:host:
305 : : *
306 : : * The remote host for the channel.
307 : : *
308 : : * This could be an IPv4 or IPv6 address, or a hostname.
309 : : */
310 : 2 : properties [PROP_HOST] =
311 : 1 : g_param_spec_string ("host", NULL, NULL,
312 : : NULL,
313 : : (G_PARAM_READWRITE |
314 : : G_PARAM_CONSTRUCT_ONLY |
315 : : G_PARAM_EXPLICIT_NOTIFY |
316 : : G_PARAM_STATIC_STRINGS));
317 : :
318 : : /**
319 : : * ValentLanChannel:port:
320 : : *
321 : : * The remote port for the channel.
322 : : */
323 : 2 : properties [PROP_PORT] =
324 : 1 : g_param_spec_uint ("port", NULL, NULL,
325 : : 0, G_MAXUINT16,
326 : : VALENT_LAN_PROTOCOL_PORT,
327 : : (G_PARAM_READWRITE |
328 : : G_PARAM_CONSTRUCT_ONLY |
329 : : G_PARAM_EXPLICIT_NOTIFY |
330 : : G_PARAM_STATIC_STRINGS));
331 : :
332 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
333 : 1 : }
334 : :
335 : : static void
336 : 6 : valent_lan_channel_init (ValentLanChannel *self)
337 : : {
338 : 6 : }
339 : :
|