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 : GTlsCertificate *certificate = NULL;
52 : 1 : 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_get_certificate (channel);
99 : 1 : peer_certificate = valent_channel_get_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 GIOStream *
116 : 1 : valent_lan_channel_upload (ValentChannel *channel,
117 : : JsonNode *packet,
118 : : GCancellable *cancellable,
119 : : GError **error)
120 : : {
121 : 1 : JsonObject *info;
122 : 2 : g_autoptr (GSocketListener) listener = NULL;
123 [ + - ]: 1 : g_autoptr (GSocketConnection) connection = NULL;
124 : 1 : uint16_t port = VALENT_LAN_TRANSFER_PORT_MIN;
125 : 1 : GTlsCertificate *certificate = NULL;
126 : 1 : GTlsCertificate *peer_certificate = NULL;
127 [ + - ]: 1 : g_autoptr (GIOStream) tls_stream = NULL;
128 : :
129 [ + - ]: 1 : g_assert (VALENT_IS_CHANNEL (channel));
130 [ - + ]: 1 : g_assert (VALENT_IS_PACKET (packet));
131 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
132 [ + - - + ]: 1 : g_assert (error == NULL || *error == NULL);
133 : :
134 : : /* Find an open port and prepare the payload information
135 : : */
136 : 1 : listener = g_socket_listener_new ();
137 [ - + ]: 1 : while (!g_socket_listener_add_inet_port (listener, port, NULL, error))
138 : : {
139 [ # # ]: 0 : if (port >= VALENT_LAN_TRANSFER_PORT_MAX)
140 : : return NULL;
141 : :
142 : 0 : port++;
143 : 0 : g_clear_error (error);
144 : : }
145 : :
146 : 1 : info = json_object_new();
147 : 1 : json_object_set_int_member (info, "port", (int64_t)port);
148 : 1 : valent_packet_set_payload_info (packet, info);
149 : :
150 : : /* Queue the packet and wait for the incoming connection
151 : : */
152 : 1 : valent_channel_write_packet (channel, packet, cancellable, NULL, NULL);
153 : 1 : connection = g_socket_listener_accept (listener, NULL, cancellable, error);
154 : 1 : g_socket_listener_close (listener);
155 [ - + ]: 1 : if (connection == NULL)
156 : : return NULL;
157 : :
158 : : /* NOTE: When negotiating an auxiliary connection, a KDE Connect device
159 : : * acts as the TLS server when accepting TCP connections.
160 : : */
161 : 1 : certificate = valent_channel_get_certificate (channel);
162 : 1 : peer_certificate = valent_channel_get_peer_certificate (channel);
163 : 1 : tls_stream = valent_lan_connection_handshake (connection,
164 : : certificate,
165 : : peer_certificate,
166 : : FALSE, /* is_client */
167 : : cancellable,
168 : : error);
169 [ - + ]: 1 : if (tls_stream == NULL)
170 : : {
171 : 0 : g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
172 : 0 : return NULL;
173 : : }
174 : :
175 : : return g_steal_pointer (&tls_stream);
176 : : }
177 : :
178 : : /*
179 : : * GObject
180 : : */
181 : : static void
182 : 6 : valent_lan_channel_finalize (GObject *object)
183 : : {
184 : 6 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
185 : :
186 : 6 : valent_object_lock (VALENT_OBJECT (self));
187 [ + - ]: 6 : g_clear_pointer (&self->host, g_free);
188 : 6 : valent_object_unlock (VALENT_OBJECT (self));
189 : :
190 : 6 : G_OBJECT_CLASS (valent_lan_channel_parent_class)->finalize (object);
191 : 6 : }
192 : :
193 : : static void
194 : 2 : valent_lan_channel_get_property (GObject *object,
195 : : guint prop_id,
196 : : GValue *value,
197 : : GParamSpec *pspec)
198 : : {
199 : 2 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
200 : :
201 [ + + - ]: 2 : switch ((ValentLanChannelProperty)prop_id)
202 : : {
203 : : case PROP_HOST:
204 : 1 : valent_object_lock (VALENT_OBJECT (self));
205 : 1 : g_value_set_string (value, self->host);
206 : 1 : valent_object_unlock (VALENT_OBJECT (self));
207 : 1 : break;
208 : :
209 : : case PROP_PORT:
210 : 1 : valent_object_lock (VALENT_OBJECT (self));
211 : 1 : g_value_set_uint (value, self->port);
212 : 1 : valent_object_unlock (VALENT_OBJECT (self));
213 : 1 : break;
214 : :
215 : 0 : default:
216 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217 : : }
218 : 2 : }
219 : :
220 : : static void
221 : 12 : valent_lan_channel_set_property (GObject *object,
222 : : guint prop_id,
223 : : const GValue *value,
224 : : GParamSpec *pspec)
225 : : {
226 : 12 : ValentLanChannel *self = VALENT_LAN_CHANNEL (object);
227 : :
228 [ + + - ]: 12 : switch ((ValentLanChannelProperty)prop_id)
229 : : {
230 : : case PROP_HOST:
231 : 6 : valent_object_lock (VALENT_OBJECT (self));
232 : 6 : self->host = g_value_dup_string (value);
233 : 6 : valent_object_unlock (VALENT_OBJECT (self));
234 : 6 : break;
235 : :
236 : : case PROP_PORT:
237 : 6 : valent_object_lock (VALENT_OBJECT (self));
238 : 6 : self->port = g_value_get_uint (value);
239 : 6 : valent_object_unlock (VALENT_OBJECT (self));
240 : 6 : break;
241 : :
242 : 0 : default:
243 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244 : : }
245 : 12 : }
246 : :
247 : : static void
248 : 1 : valent_lan_channel_class_init (ValentLanChannelClass *klass)
249 : : {
250 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
251 : 1 : ValentChannelClass *channel_class = VALENT_CHANNEL_CLASS (klass);
252 : :
253 : 1 : object_class->finalize = valent_lan_channel_finalize;
254 : 1 : object_class->get_property = valent_lan_channel_get_property;
255 : 1 : object_class->set_property = valent_lan_channel_set_property;
256 : :
257 : 1 : channel_class->download = valent_lan_channel_download;
258 : 1 : channel_class->upload = valent_lan_channel_upload;
259 : :
260 : : /**
261 : : * ValentLanChannel:host:
262 : : *
263 : : * The remote host for the channel.
264 : : *
265 : : * This could be an IPv4 or IPv6 address, or a hostname.
266 : : */
267 : 2 : properties [PROP_HOST] =
268 : 1 : g_param_spec_string ("host", NULL, NULL,
269 : : NULL,
270 : : (G_PARAM_READWRITE |
271 : : G_PARAM_CONSTRUCT_ONLY |
272 : : G_PARAM_EXPLICIT_NOTIFY |
273 : : G_PARAM_STATIC_STRINGS));
274 : :
275 : : /**
276 : : * ValentLanChannel:port:
277 : : *
278 : : * The remote port for the channel.
279 : : */
280 : 2 : properties [PROP_PORT] =
281 : 1 : g_param_spec_uint ("port", NULL, NULL,
282 : : 0, G_MAXUINT16,
283 : : VALENT_LAN_PROTOCOL_PORT,
284 : : (G_PARAM_READWRITE |
285 : : G_PARAM_CONSTRUCT_ONLY |
286 : : G_PARAM_EXPLICIT_NOTIFY |
287 : : G_PARAM_STATIC_STRINGS));
288 : :
289 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
290 : 1 : }
291 : :
292 : : static void
293 : 6 : valent_lan_channel_init (ValentLanChannel *self)
294 : : {
295 : 6 : }
296 : :
|