Branch data Line data Source code
1 : : /* GDBus - GLib D-Bus Library
2 : : *
3 : : * Copyright (C) 2008-2010 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: David Zeuthen <davidz@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <stdlib.h>
26 : : #include <string.h>
27 : : #include <stdio.h>
28 : : #include <errno.h>
29 : :
30 : : #include "gioerror.h"
31 : : #include "gdbusutils.h"
32 : : #include "gdbusaddress.h"
33 : : #include "gdbuserror.h"
34 : : #include "gioenumtypes.h"
35 : : #include "glib-private.h"
36 : : #include "gnetworkaddress.h"
37 : : #include "gsocketclient.h"
38 : : #include "giostream.h"
39 : : #include "gasyncresult.h"
40 : : #include "gtask.h"
41 : : #include "glib-private.h"
42 : : #include "gdbusprivate.h"
43 : : #include "gstdio.h"
44 : :
45 : : #ifdef HAVE_UNISTD_H
46 : : #include <unistd.h>
47 : : #endif
48 : : #include <sys/stat.h>
49 : : #include <sys/types.h>
50 : : #include <gio/gunixsocketaddress.h>
51 : :
52 : : #ifdef G_OS_WIN32
53 : : #include <windows.h>
54 : : #endif
55 : :
56 : : #include "glibintl.h"
57 : :
58 : : /**
59 : : * GDBusAddress:
60 : : *
61 : : * Routines for working with D-Bus addresses. A D-Bus address is a string
62 : : * like `unix:tmpdir=/tmp/my-app-name`. The exact format of addresses
63 : : * is explained in detail in the
64 : : * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
65 : : *
66 : : * TCP D-Bus connections are supported, but accessing them via a proxy is
67 : : * currently not supported.
68 : : *
69 : : * Since GLib 2.72, `unix:` addresses are supported on Windows with `AF_UNIX`
70 : : * support (Windows 10).
71 : : */
72 : :
73 : : static gchar *get_session_address_platform_specific (GError **error);
74 : : static gchar *get_session_address_dbus_launch (GError **error);
75 : :
76 : : /* ---------------------------------------------------------------------------------------------------- */
77 : :
78 : : /**
79 : : * g_dbus_is_address:
80 : : * @string: A string.
81 : : *
82 : : * Checks if @string is a
83 : : * [D-Bus address](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
84 : : *
85 : : * This doesn't check if @string is actually supported by #GDBusServer
86 : : * or #GDBusConnection - use g_dbus_is_supported_address() to do more
87 : : * checks.
88 : : *
89 : : * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
90 : : *
91 : : * Since: 2.26
92 : : */
93 : : gboolean
94 : 63 : g_dbus_is_address (const gchar *string)
95 : : {
96 : : guint n;
97 : : gchar **a;
98 : : gboolean ret;
99 : :
100 : 63 : ret = FALSE;
101 : :
102 : 63 : g_return_val_if_fail (string != NULL, FALSE);
103 : :
104 : 63 : a = g_strsplit (string, ";", 0);
105 : 63 : if (a[0] == NULL)
106 : 1 : goto out;
107 : :
108 : 121 : for (n = 0; a[n] != NULL; n++)
109 : : {
110 : 69 : if (!_g_dbus_address_parse_entry (a[n],
111 : : NULL,
112 : : NULL,
113 : : NULL))
114 : 10 : goto out;
115 : : }
116 : :
117 : 52 : ret = TRUE;
118 : :
119 : 63 : out:
120 : 63 : g_strfreev (a);
121 : 63 : return ret;
122 : : }
123 : :
124 : : static gboolean
125 : 57 : is_valid_unix (const gchar *address_entry,
126 : : GHashTable *key_value_pairs,
127 : : GError **error)
128 : : {
129 : : gboolean ret;
130 : : GPtrArray *keys;
131 : : const gchar *path;
132 : : const gchar *dir;
133 : : const gchar *tmpdir;
134 : : const gchar *abstract;
135 : :
136 : 57 : ret = FALSE;
137 : 57 : path = NULL;
138 : 57 : dir = NULL;
139 : 57 : tmpdir = NULL;
140 : 57 : abstract = NULL;
141 : :
142 : 57 : keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
143 : 126 : for (guint i = 0; i < keys->len; ++i)
144 : : {
145 : 71 : const gchar *key = g_ptr_array_index (keys, i);
146 : 71 : if (g_strcmp0 (key, "path") == 0)
147 : 18 : path = g_hash_table_lookup (key_value_pairs, key);
148 : 53 : else if (g_strcmp0 (key, "dir") == 0)
149 : 18 : dir = g_hash_table_lookup (key_value_pairs, key);
150 : 35 : else if (g_strcmp0 (key, "tmpdir") == 0)
151 : 17 : tmpdir = g_hash_table_lookup (key_value_pairs, key);
152 : 18 : else if (g_strcmp0 (key, "abstract") == 0)
153 : 12 : abstract = g_hash_table_lookup (key_value_pairs, key);
154 : 6 : else if (g_strcmp0 (key, "guid") != 0)
155 : : {
156 : 2 : g_set_error (error,
157 : : G_IO_ERROR,
158 : : G_IO_ERROR_INVALID_ARGUMENT,
159 : : _("Unsupported key “%s” in address entry “%s”"),
160 : : key,
161 : : address_entry);
162 : 2 : goto out;
163 : : }
164 : : }
165 : :
166 : : /* Exactly one key must be set */
167 : 55 : if ((path != NULL) + (dir != NULL) + (tmpdir != NULL) + (abstract != NULL) > 1)
168 : : {
169 : 12 : g_set_error (error,
170 : : G_IO_ERROR,
171 : : G_IO_ERROR_INVALID_ARGUMENT,
172 : : _("Meaningless key/value pair combination in address entry “%s”"),
173 : : address_entry);
174 : 12 : goto out;
175 : : }
176 : 43 : else if (path == NULL && dir == NULL && tmpdir == NULL && abstract == NULL)
177 : : {
178 : 2 : g_set_error (error,
179 : : G_IO_ERROR,
180 : : G_IO_ERROR_INVALID_ARGUMENT,
181 : : _("Address “%s” is invalid (need exactly one of path, dir, tmpdir, or abstract keys)"),
182 : : address_entry);
183 : 2 : goto out;
184 : : }
185 : :
186 : 41 : ret = TRUE;
187 : :
188 : 57 : out:
189 : 57 : g_ptr_array_unref (keys);
190 : :
191 : 57 : return ret;
192 : : }
193 : :
194 : : static gboolean
195 : 35 : is_valid_nonce_tcp (const gchar *address_entry,
196 : : GHashTable *key_value_pairs,
197 : : GError **error)
198 : : {
199 : : gboolean ret;
200 : : GPtrArray *keys;
201 : : const gchar *host;
202 : : const gchar *port;
203 : : const gchar *family;
204 : : const gchar *nonce_file;
205 : : gint port_num;
206 : : gchar *endp;
207 : :
208 : 35 : ret = FALSE;
209 : 35 : host = NULL;
210 : 35 : port = NULL;
211 : 35 : family = NULL;
212 : 35 : nonce_file = NULL;
213 : :
214 : 35 : keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
215 : 134 : for (guint i = 0; i < keys->len; ++i)
216 : : {
217 : 101 : const gchar *key = g_ptr_array_index (keys, i);
218 : 101 : if (g_strcmp0 (key, "host") == 0)
219 : 33 : host = g_hash_table_lookup (key_value_pairs, key);
220 : 68 : else if (g_strcmp0 (key, "port") == 0)
221 : 28 : port = g_hash_table_lookup (key_value_pairs, key);
222 : 40 : else if (g_strcmp0 (key, "family") == 0)
223 : 14 : family = g_hash_table_lookup (key_value_pairs, key);
224 : 26 : else if (g_strcmp0 (key, "noncefile") == 0)
225 : 20 : nonce_file = g_hash_table_lookup (key_value_pairs, key);
226 : 6 : else if (g_strcmp0 (key, "guid") != 0)
227 : : {
228 : 2 : g_set_error (error,
229 : : G_IO_ERROR,
230 : : G_IO_ERROR_INVALID_ARGUMENT,
231 : : _("Unsupported key “%s” in address entry “%s”"),
232 : : key,
233 : : address_entry);
234 : 2 : goto out;
235 : : }
236 : : }
237 : :
238 : 33 : if (port != NULL)
239 : : {
240 : 28 : port_num = strtol (port, &endp, 10);
241 : 28 : if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
242 : : {
243 : 16 : g_set_error (error,
244 : : G_IO_ERROR,
245 : : G_IO_ERROR_INVALID_ARGUMENT,
246 : : _("Error in address “%s” — the “%s” attribute is malformed"),
247 : : address_entry, "port");
248 : 16 : goto out;
249 : : }
250 : : }
251 : :
252 : 17 : if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
253 : : {
254 : 2 : g_set_error (error,
255 : : G_IO_ERROR,
256 : : G_IO_ERROR_INVALID_ARGUMENT,
257 : : _("Error in address “%s” — the “%s” attribute is malformed"),
258 : : address_entry, "family");
259 : 2 : goto out;
260 : : }
261 : :
262 : : if (host != NULL)
263 : : {
264 : : /* TODO: validate host */
265 : : }
266 : :
267 : 15 : if (nonce_file != NULL && *nonce_file == '\0')
268 : : {
269 : 2 : g_set_error (error,
270 : : G_IO_ERROR,
271 : : G_IO_ERROR_INVALID_ARGUMENT,
272 : : _("Error in address “%s” — the “%s” attribute is malformed"),
273 : : address_entry, "noncefile");
274 : 2 : goto out;
275 : : }
276 : :
277 : 13 : ret = TRUE;
278 : :
279 : 35 : out:
280 : 35 : g_ptr_array_unref (keys);
281 : :
282 : 35 : return ret;
283 : : }
284 : :
285 : : static gboolean
286 : 34 : is_valid_tcp (const gchar *address_entry,
287 : : GHashTable *key_value_pairs,
288 : : GError **error)
289 : : {
290 : : gboolean ret;
291 : : GPtrArray *keys;
292 : : const gchar *host;
293 : : const gchar *port;
294 : : const gchar *family;
295 : : gint port_num;
296 : : gchar *endp;
297 : :
298 : 34 : ret = FALSE;
299 : 34 : host = NULL;
300 : 34 : port = NULL;
301 : 34 : family = NULL;
302 : :
303 : 34 : keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
304 : 98 : for (guint i = 0; i < keys->len; ++i)
305 : : {
306 : 66 : const gchar *key = g_ptr_array_index (keys, i);
307 : 66 : if (g_strcmp0 (key, "host") == 0)
308 : 30 : host = g_hash_table_lookup (key_value_pairs, key);
309 : 36 : else if (g_strcmp0 (key, "port") == 0)
310 : 22 : port = g_hash_table_lookup (key_value_pairs, key);
311 : 14 : else if (g_strcmp0 (key, "family") == 0)
312 : 8 : family = g_hash_table_lookup (key_value_pairs, key);
313 : 6 : else if (g_strcmp0 (key, "guid") != 0)
314 : : {
315 : 2 : g_set_error (error,
316 : : G_IO_ERROR,
317 : : G_IO_ERROR_INVALID_ARGUMENT,
318 : : _("Unsupported key “%s” in address entry “%s”"),
319 : : key,
320 : : address_entry);
321 : 2 : goto out;
322 : : }
323 : : }
324 : :
325 : 32 : if (port != NULL)
326 : : {
327 : 22 : port_num = strtol (port, &endp, 10);
328 : 22 : if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
329 : : {
330 : 8 : g_set_error (error,
331 : : G_IO_ERROR,
332 : : G_IO_ERROR_INVALID_ARGUMENT,
333 : : _("Error in address “%s” — the “%s” attribute is malformed"),
334 : : address_entry, "port");
335 : 8 : goto out;
336 : : }
337 : : }
338 : :
339 : 24 : if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
340 : : {
341 : 4 : g_set_error (error,
342 : : G_IO_ERROR,
343 : : G_IO_ERROR_INVALID_ARGUMENT,
344 : : _("Error in address “%s” — the “%s” attribute is malformed"),
345 : : address_entry, "family");
346 : 4 : goto out;
347 : : }
348 : :
349 : : if (host != NULL)
350 : : {
351 : : /* TODO: validate host */
352 : : }
353 : :
354 : 20 : ret= TRUE;
355 : :
356 : 34 : out:
357 : 34 : g_ptr_array_unref (keys);
358 : :
359 : 34 : return ret;
360 : : }
361 : :
362 : : /**
363 : : * g_dbus_is_supported_address:
364 : : * @string: A string.
365 : : * @error: Return location for error or %NULL.
366 : : *
367 : : * Like g_dbus_is_address() but also checks if the library supports the
368 : : * transports in @string and that key/value pairs for each transport
369 : : * are valid. See the specification of the
370 : : * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
371 : : *
372 : : * Returns: %TRUE if @string is a valid D-Bus address that is
373 : : * supported by this library, %FALSE if @error is set.
374 : : *
375 : : * Since: 2.26
376 : : */
377 : : gboolean
378 : 129 : g_dbus_is_supported_address (const gchar *string,
379 : : GError **error)
380 : : {
381 : : guint n;
382 : : gchar **a;
383 : : gboolean ret;
384 : :
385 : 129 : ret = FALSE;
386 : :
387 : 129 : g_return_val_if_fail (string != NULL, FALSE);
388 : 129 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
389 : :
390 : 129 : a = g_strsplit (string, ";", 0);
391 : 207 : for (n = 0; a[n] != NULL; n++)
392 : : {
393 : : gchar *transport_name;
394 : : GHashTable *key_value_pairs;
395 : : gboolean supported;
396 : :
397 : 137 : if (!_g_dbus_address_parse_entry (a[n],
398 : : &transport_name,
399 : : &key_value_pairs,
400 : : error))
401 : 59 : goto out;
402 : :
403 : 136 : supported = FALSE;
404 : 136 : if (g_strcmp0 (transport_name, "unix") == 0)
405 : 57 : supported = is_valid_unix (a[n], key_value_pairs, error);
406 : 79 : else if (g_strcmp0 (transport_name, "tcp") == 0)
407 : 34 : supported = is_valid_tcp (a[n], key_value_pairs, error);
408 : 45 : else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
409 : 35 : supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
410 : 10 : else if (g_strcmp0 (a[n], "autolaunch:") == 0)
411 : 4 : supported = TRUE;
412 : : else
413 : 6 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
414 : : _("Unknown or unsupported transport “%s” for address “%s”"),
415 : 6 : transport_name, a[n]);
416 : :
417 : 136 : g_free (transport_name);
418 : 136 : g_hash_table_unref (key_value_pairs);
419 : :
420 : 136 : if (!supported)
421 : 58 : goto out;
422 : : }
423 : :
424 : 70 : ret = TRUE;
425 : :
426 : 129 : out:
427 : 129 : g_strfreev (a);
428 : :
429 : 129 : g_assert (ret || (!ret && (error == NULL || *error != NULL)));
430 : :
431 : 129 : return ret;
432 : : }
433 : :
434 : : gboolean
435 : 2485 : _g_dbus_address_parse_entry (const gchar *address_entry,
436 : : gchar **out_transport_name,
437 : : GHashTable **out_key_value_pairs,
438 : : GError **error)
439 : : {
440 : : gboolean ret;
441 : : GHashTable *key_value_pairs;
442 : : gchar *transport_name;
443 : : gchar **kv_pairs;
444 : : const gchar *s;
445 : : guint n;
446 : :
447 : 2485 : ret = FALSE;
448 : 2485 : kv_pairs = NULL;
449 : 2485 : transport_name = NULL;
450 : 2485 : key_value_pairs = NULL;
451 : :
452 : 2485 : s = strchr (address_entry, ':');
453 : 2485 : if (s == NULL)
454 : : {
455 : 4 : g_set_error (error,
456 : : G_IO_ERROR,
457 : : G_IO_ERROR_INVALID_ARGUMENT,
458 : : _("Address element “%s” does not contain a colon (:)"),
459 : : address_entry);
460 : 4 : goto out;
461 : : }
462 : 2481 : else if (s == address_entry)
463 : : {
464 : 2 : g_set_error (error,
465 : : G_IO_ERROR,
466 : : G_IO_ERROR_INVALID_ARGUMENT,
467 : : _("Transport name in address element “%s” must not be empty"),
468 : : address_entry);
469 : 2 : goto out;
470 : : }
471 : :
472 : 2479 : transport_name = g_strndup (address_entry, s - address_entry);
473 : 2479 : key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
474 : :
475 : 2479 : kv_pairs = g_strsplit (s + 1, ",", 0);
476 : 7317 : for (n = 0; kv_pairs[n] != NULL; n++)
477 : : {
478 : 4843 : const gchar *kv_pair = kv_pairs[n];
479 : : gchar *key;
480 : : gchar *value;
481 : :
482 : 4843 : s = strchr (kv_pair, '=');
483 : 4843 : if (s == NULL)
484 : : {
485 : 1 : g_set_error (error,
486 : : G_IO_ERROR,
487 : : G_IO_ERROR_INVALID_ARGUMENT,
488 : : _("Key/Value pair %d, “%s”, in address element “%s” does not contain an equal sign"),
489 : : n,
490 : : kv_pair,
491 : : address_entry);
492 : 1 : goto out;
493 : : }
494 : 4842 : else if (s == kv_pair)
495 : : {
496 : 2 : g_set_error (error,
497 : : G_IO_ERROR,
498 : : G_IO_ERROR_INVALID_ARGUMENT,
499 : : _("Key/Value pair %d, “%s”, in address element “%s” must not have an empty key"),
500 : : n,
501 : : kv_pair,
502 : : address_entry);
503 : 2 : goto out;
504 : : }
505 : :
506 : 4840 : key = g_uri_unescape_segment (kv_pair, s, NULL);
507 : 4840 : value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
508 : 4840 : if (key == NULL || value == NULL)
509 : : {
510 : 2 : g_set_error (error,
511 : : G_IO_ERROR,
512 : : G_IO_ERROR_INVALID_ARGUMENT,
513 : : _("Error unescaping key or value in Key/Value pair %d, “%s”, in address element “%s”"),
514 : : n,
515 : : kv_pair,
516 : : address_entry);
517 : 2 : g_free (key);
518 : 2 : g_free (value);
519 : 2 : goto out;
520 : : }
521 : 4838 : g_hash_table_insert (key_value_pairs, key, value);
522 : : }
523 : :
524 : 2474 : ret = TRUE;
525 : :
526 : 2485 : out:
527 : 2485 : if (ret)
528 : : {
529 : 2474 : if (out_transport_name != NULL)
530 : 2415 : *out_transport_name = g_steal_pointer (&transport_name);
531 : 2474 : if (out_key_value_pairs != NULL)
532 : 2415 : *out_key_value_pairs = g_steal_pointer (&key_value_pairs);
533 : : }
534 : :
535 : 2485 : g_clear_pointer (&key_value_pairs, g_hash_table_unref);
536 : 2485 : g_free (transport_name);
537 : 2485 : g_strfreev (kv_pairs);
538 : :
539 : 2485 : return ret;
540 : : }
541 : :
542 : : /* ---------------------------------------------------------------------------------------------------- */
543 : :
544 : : static GIOStream *
545 : : g_dbus_address_try_connect_one (const gchar *address_entry,
546 : : gchar **out_guid,
547 : : GCancellable *cancellable,
548 : : GError **error);
549 : :
550 : : /* TODO: Declare an extension point called GDBusTransport (or similar)
551 : : * and move code below to extensions implementing said extension
552 : : * point. That way we can implement a D-Bus transport over X11 without
553 : : * making libgio link to libX11...
554 : : */
555 : : static GIOStream *
556 : 2253 : g_dbus_address_connect (const gchar *address_entry,
557 : : const gchar *transport_name,
558 : : GHashTable *key_value_pairs,
559 : : GCancellable *cancellable,
560 : : GError **error)
561 : : {
562 : : GIOStream *ret;
563 : : GSocketConnectable *connectable;
564 : : const gchar *nonce_file;
565 : :
566 : 2253 : connectable = NULL;
567 : 2253 : ret = NULL;
568 : 2253 : nonce_file = NULL;
569 : :
570 : 2253 : if (g_strcmp0 (transport_name, "unix") == 0)
571 : : {
572 : : const gchar *path;
573 : : const gchar *abstract;
574 : 2245 : path = g_hash_table_lookup (key_value_pairs, "path");
575 : 2245 : abstract = g_hash_table_lookup (key_value_pairs, "abstract");
576 : 2245 : if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
577 : : {
578 : 0 : g_set_error (error,
579 : : G_IO_ERROR,
580 : : G_IO_ERROR_INVALID_ARGUMENT,
581 : : _("Error in address “%s” — the unix transport requires exactly one of the "
582 : : "keys “path” or “abstract” to be set"),
583 : : address_entry);
584 : : }
585 : 2245 : else if (path != NULL)
586 : : {
587 : 2245 : connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
588 : : }
589 : 0 : else if (abstract != NULL)
590 : : {
591 : 0 : connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
592 : : -1,
593 : : G_UNIX_SOCKET_ADDRESS_ABSTRACT));
594 : : }
595 : : else
596 : : {
597 : : g_assert_not_reached ();
598 : : }
599 : : }
600 : 8 : else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
601 : 8 : {
602 : : const gchar *s;
603 : : const gchar *host;
604 : : glong port;
605 : : gchar *endp;
606 : : gboolean is_nonce;
607 : :
608 : 8 : is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
609 : :
610 : 8 : host = g_hash_table_lookup (key_value_pairs, "host");
611 : 8 : if (host == NULL)
612 : : {
613 : 0 : g_set_error (error,
614 : : G_IO_ERROR,
615 : : G_IO_ERROR_INVALID_ARGUMENT,
616 : : /* Translators: The first placeholder is a D-Bus connection address,
617 : : * the second is the literal name of an attribute in the address.
618 : : */
619 : : _("Error in address “%s” — the %s attribute is missing or malformed"),
620 : : address_entry,
621 : : "host");
622 : 0 : goto out;
623 : : }
624 : :
625 : 8 : s = g_hash_table_lookup (key_value_pairs, "port");
626 : 8 : if (s == NULL)
627 : 0 : s = "0";
628 : 8 : port = strtol (s, &endp, 10);
629 : 8 : if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
630 : : {
631 : 0 : g_set_error (error,
632 : : G_IO_ERROR,
633 : : G_IO_ERROR_INVALID_ARGUMENT,
634 : : _("Error in address “%s” — the %s attribute is missing or malformed"),
635 : : address_entry,
636 : : "port");
637 : 0 : goto out;
638 : : }
639 : :
640 : :
641 : 8 : if (is_nonce)
642 : : {
643 : 4 : nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
644 : 4 : if (nonce_file == NULL)
645 : : {
646 : 0 : g_set_error (error,
647 : : G_IO_ERROR,
648 : : G_IO_ERROR_INVALID_ARGUMENT,
649 : : _("Error in address “%s” — the %s attribute is missing or malformed"),
650 : : address_entry,
651 : : "noncefile");
652 : 0 : goto out;
653 : : }
654 : : }
655 : :
656 : : /* TODO: deal with family key/value-pair */
657 : 8 : connectable = g_network_address_new (host, port);
658 : : }
659 : 0 : else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
660 : : {
661 : : gchar *autolaunch_address;
662 : 0 : autolaunch_address = get_session_address_dbus_launch (error);
663 : 0 : if (autolaunch_address != NULL)
664 : : {
665 : 0 : ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
666 : 0 : g_free (autolaunch_address);
667 : 0 : goto out;
668 : : }
669 : : else
670 : : {
671 : 0 : g_prefix_error (error, _("Error auto-launching: "));
672 : : }
673 : : }
674 : : else
675 : : {
676 : 0 : g_set_error (error,
677 : : G_IO_ERROR,
678 : : G_IO_ERROR_INVALID_ARGUMENT,
679 : : _("Unknown or unsupported transport “%s” for address “%s”"),
680 : : transport_name,
681 : : address_entry);
682 : : }
683 : :
684 : 2253 : if (connectable != NULL)
685 : : {
686 : : GSocketClient *client;
687 : : GSocketConnection *connection;
688 : :
689 : 2253 : g_assert (ret == NULL);
690 : 2253 : client = g_socket_client_new ();
691 : :
692 : : /* Disable proxy support to prevent a deadlock on startup, since loading a
693 : : * proxy resolver causes the GIO modules to be loaded, and there will
694 : : * almost certainly be one of them which then tries to use GDBus.
695 : : * See: https://bugzilla.gnome.org/show_bug.cgi?id=792499 */
696 : 2253 : g_socket_client_set_enable_proxy (client, FALSE);
697 : :
698 : 2253 : connection = g_socket_client_connect (client,
699 : : connectable,
700 : : cancellable,
701 : : error);
702 : 2253 : g_object_unref (connectable);
703 : 2253 : g_object_unref (client);
704 : 2253 : if (connection == NULL)
705 : 28 : goto out;
706 : :
707 : 2225 : ret = G_IO_STREAM (connection);
708 : :
709 : 2225 : if (nonce_file != NULL)
710 : : {
711 : : gchar nonce_contents[16 + 1];
712 : : size_t num_bytes_read;
713 : : FILE *f;
714 : : int errsv;
715 : :
716 : : /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
717 : 4 : f = g_fopen (nonce_file, "rbe");
718 : 4 : errsv = errno;
719 : 4 : if (f == NULL)
720 : : {
721 : 1 : g_set_error (error,
722 : : G_IO_ERROR,
723 : : G_IO_ERROR_INVALID_ARGUMENT,
724 : : _("Error opening nonce file “%s”: %s"),
725 : : nonce_file,
726 : : g_strerror (errsv));
727 : 1 : g_object_unref (ret);
728 : 1 : ret = NULL;
729 : 2 : goto out;
730 : : }
731 : 3 : num_bytes_read = fread (nonce_contents,
732 : : sizeof (gchar),
733 : : 16 + 1,
734 : : f);
735 : 3 : errsv = errno;
736 : 3 : if (num_bytes_read != 16)
737 : : {
738 : 1 : if (num_bytes_read == 0)
739 : : {
740 : 0 : g_set_error (error,
741 : : G_IO_ERROR,
742 : : G_IO_ERROR_INVALID_ARGUMENT,
743 : : _("Error reading from nonce file “%s”: %s"),
744 : : nonce_file,
745 : : g_strerror (errsv));
746 : : }
747 : : else
748 : : {
749 : 1 : g_set_error (error,
750 : : G_IO_ERROR,
751 : : G_IO_ERROR_INVALID_ARGUMENT,
752 : : _("Error reading from nonce file “%s”, expected 16 bytes, got %d"),
753 : : nonce_file,
754 : : (gint) num_bytes_read);
755 : : }
756 : 1 : g_object_unref (ret);
757 : 1 : ret = NULL;
758 : 1 : fclose (f);
759 : 1 : goto out;
760 : : }
761 : 2 : fclose (f);
762 : :
763 : 2 : if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
764 : : nonce_contents,
765 : : 16,
766 : : NULL,
767 : : cancellable,
768 : : error))
769 : : {
770 : 0 : g_prefix_error (error, _("Error writing contents of nonce file “%s” to stream:"), nonce_file);
771 : 0 : g_object_unref (ret);
772 : 0 : ret = NULL;
773 : 0 : goto out;
774 : : }
775 : : }
776 : : }
777 : :
778 : 2221 : out:
779 : :
780 : 2253 : return ret;
781 : : }
782 : :
783 : : static GIOStream *
784 : 2253 : g_dbus_address_try_connect_one (const gchar *address_entry,
785 : : gchar **out_guid,
786 : : GCancellable *cancellable,
787 : : GError **error)
788 : : {
789 : : GIOStream *ret;
790 : : GHashTable *key_value_pairs;
791 : : gchar *transport_name;
792 : : const gchar *guid;
793 : :
794 : 2253 : ret = NULL;
795 : 2253 : transport_name = NULL;
796 : 2253 : key_value_pairs = NULL;
797 : :
798 : 2253 : if (!_g_dbus_address_parse_entry (address_entry,
799 : : &transport_name,
800 : : &key_value_pairs,
801 : : error))
802 : 0 : goto out;
803 : :
804 : 2253 : ret = g_dbus_address_connect (address_entry,
805 : : transport_name,
806 : : key_value_pairs,
807 : : cancellable,
808 : : error);
809 : 2253 : if (ret == NULL)
810 : 30 : goto out;
811 : :
812 : 2223 : guid = g_hash_table_lookup (key_value_pairs, "guid");
813 : 2223 : if (guid != NULL && out_guid != NULL)
814 : 0 : *out_guid = g_strdup (guid);
815 : :
816 : 2223 : out:
817 : 2253 : g_free (transport_name);
818 : 2253 : if (key_value_pairs != NULL)
819 : 2253 : g_hash_table_unref (key_value_pairs);
820 : 2253 : return ret;
821 : : }
822 : :
823 : :
824 : : /* ---------------------------------------------------------------------------------------------------- */
825 : :
826 : : typedef struct {
827 : : gchar *address;
828 : : gchar *guid;
829 : : } GetStreamData;
830 : :
831 : : static void
832 : 0 : get_stream_data_free (GetStreamData *data)
833 : : {
834 : 0 : g_free (data->address);
835 : 0 : g_free (data->guid);
836 : 0 : g_free (data);
837 : 0 : }
838 : :
839 : : static void
840 : 0 : get_stream_thread_func (GTask *task,
841 : : gpointer source_object,
842 : : gpointer task_data,
843 : : GCancellable *cancellable)
844 : : {
845 : 0 : GetStreamData *data = task_data;
846 : : GIOStream *stream;
847 : 0 : GError *error = NULL;
848 : :
849 : 0 : stream = g_dbus_address_get_stream_sync (data->address,
850 : : &data->guid,
851 : : cancellable,
852 : : &error);
853 : 0 : if (stream)
854 : 0 : g_task_return_pointer (task, stream, g_object_unref);
855 : : else
856 : 0 : g_task_return_error (task, error);
857 : 0 : }
858 : :
859 : : /**
860 : : * g_dbus_address_get_stream:
861 : : * @address: A valid D-Bus address.
862 : : * @cancellable: (nullable): A #GCancellable or %NULL.
863 : : * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
864 : : * @user_data: Data to pass to @callback.
865 : : *
866 : : * Asynchronously connects to an endpoint specified by @address and
867 : : * sets up the connection so it is in a state to run the client-side
868 : : * of the D-Bus authentication conversation. @address must be in the
869 : : * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
870 : : *
871 : : * When the operation is finished, @callback will be invoked. You can
872 : : * then call g_dbus_address_get_stream_finish() to get the result of
873 : : * the operation.
874 : : *
875 : : * This is an asynchronous failable function. See
876 : : * g_dbus_address_get_stream_sync() for the synchronous version.
877 : : *
878 : : * Since: 2.26
879 : : */
880 : : void
881 : 0 : g_dbus_address_get_stream (const gchar *address,
882 : : GCancellable *cancellable,
883 : : GAsyncReadyCallback callback,
884 : : gpointer user_data)
885 : : {
886 : : GTask *task;
887 : : GetStreamData *data;
888 : :
889 : 0 : g_return_if_fail (address != NULL);
890 : :
891 : 0 : data = g_new0 (GetStreamData, 1);
892 : 0 : data->address = g_strdup (address);
893 : :
894 : 0 : task = g_task_new (NULL, cancellable, callback, user_data);
895 : 0 : g_task_set_source_tag (task, g_dbus_address_get_stream);
896 : 0 : g_task_set_task_data (task, data, (GDestroyNotify) get_stream_data_free);
897 : 0 : g_task_run_in_thread (task, get_stream_thread_func);
898 : 0 : g_object_unref (task);
899 : : }
900 : :
901 : : /**
902 : : * g_dbus_address_get_stream_finish:
903 : : * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
904 : : * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
905 : : * @error: Return location for error or %NULL.
906 : : *
907 : : * Finishes an operation started with g_dbus_address_get_stream().
908 : : *
909 : : * A server is not required to set a GUID, so @out_guid may be set to %NULL
910 : : * even on success.
911 : : *
912 : : * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
913 : : *
914 : : * Since: 2.26
915 : : */
916 : : GIOStream *
917 : 0 : g_dbus_address_get_stream_finish (GAsyncResult *res,
918 : : gchar **out_guid,
919 : : GError **error)
920 : : {
921 : : GTask *task;
922 : : GetStreamData *data;
923 : : GIOStream *ret;
924 : :
925 : 0 : g_return_val_if_fail (g_task_is_valid (res, NULL), NULL);
926 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
927 : :
928 : 0 : task = G_TASK (res);
929 : 0 : ret = g_task_propagate_pointer (task, error);
930 : :
931 : 0 : if (ret != NULL && out_guid != NULL)
932 : : {
933 : 0 : data = g_task_get_task_data (task);
934 : 0 : *out_guid = data->guid;
935 : 0 : data->guid = NULL;
936 : : }
937 : :
938 : 0 : return ret;
939 : : }
940 : :
941 : : /**
942 : : * g_dbus_address_get_stream_sync:
943 : : * @address: A valid D-Bus address.
944 : : * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
945 : : * @cancellable: (nullable): A #GCancellable or %NULL.
946 : : * @error: Return location for error or %NULL.
947 : : *
948 : : * Synchronously connects to an endpoint specified by @address and
949 : : * sets up the connection so it is in a state to run the client-side
950 : : * of the D-Bus authentication conversation. @address must be in the
951 : : * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
952 : : *
953 : : * A server is not required to set a GUID, so @out_guid may be set to %NULL
954 : : * even on success.
955 : : *
956 : : * This is a synchronous failable function. See
957 : : * g_dbus_address_get_stream() for the asynchronous version.
958 : : *
959 : : * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
960 : : *
961 : : * Since: 2.26
962 : : */
963 : : GIOStream *
964 : 2254 : g_dbus_address_get_stream_sync (const gchar *address,
965 : : gchar **out_guid,
966 : : GCancellable *cancellable,
967 : : GError **error)
968 : : {
969 : : GIOStream *ret;
970 : : gchar **addr_array;
971 : : guint n;
972 : : GError *last_error;
973 : :
974 : 2254 : g_return_val_if_fail (address != NULL, NULL);
975 : 2254 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
976 : :
977 : 2254 : ret = NULL;
978 : 2254 : last_error = NULL;
979 : :
980 : 2254 : addr_array = g_strsplit (address, ";", 0);
981 : 2254 : if (addr_array[0] == NULL)
982 : : {
983 : 1 : last_error = g_error_new_literal (G_IO_ERROR,
984 : : G_IO_ERROR_INVALID_ARGUMENT,
985 : : _("The given address is empty"));
986 : 1 : goto out;
987 : : }
988 : :
989 : 2283 : for (n = 0; addr_array[n] != NULL; n++)
990 : : {
991 : 2253 : const gchar *addr = addr_array[n];
992 : : GError *this_error;
993 : :
994 : 2253 : this_error = NULL;
995 : 2253 : ret = g_dbus_address_try_connect_one (addr,
996 : : out_guid,
997 : : cancellable,
998 : : &this_error);
999 : 2253 : if (ret != NULL)
1000 : : {
1001 : 2223 : goto out;
1002 : : }
1003 : : else
1004 : : {
1005 : 30 : g_assert (this_error != NULL);
1006 : 30 : if (last_error != NULL)
1007 : 0 : g_error_free (last_error);
1008 : 30 : last_error = this_error;
1009 : : }
1010 : : }
1011 : :
1012 : 30 : out:
1013 : 2254 : if (ret != NULL)
1014 : : {
1015 : 2223 : if (last_error != NULL)
1016 : 0 : g_error_free (last_error);
1017 : : }
1018 : : else
1019 : : {
1020 : 31 : g_assert (last_error != NULL);
1021 : 31 : g_propagate_error (error, last_error);
1022 : : }
1023 : :
1024 : 2254 : g_strfreev (addr_array);
1025 : 2254 : return ret;
1026 : : }
1027 : :
1028 : : /* ---------------------------------------------------------------------------------------------------- */
1029 : :
1030 : : /*
1031 : : * Return the address of XDG_RUNTIME_DIR/bus if it exists, belongs to
1032 : : * us, and is a socket, and we are on Unix.
1033 : : */
1034 : : static gchar *
1035 : 68 : get_session_address_xdg (void)
1036 : : {
1037 : : #ifdef G_OS_UNIX
1038 : 68 : gchar *ret = NULL;
1039 : : gchar *bus;
1040 : : gchar *tmp;
1041 : : GStatBuf buf;
1042 : :
1043 : 68 : bus = g_build_filename (g_get_user_runtime_dir (), "bus", NULL);
1044 : :
1045 : : /* if ENOENT, EPERM, etc., quietly don't use it */
1046 : 68 : if (g_stat (bus, &buf) < 0)
1047 : 67 : goto out;
1048 : :
1049 : : /* if it isn't ours, we have incorrectly inherited someone else's
1050 : : * XDG_RUNTIME_DIR; silently don't use it
1051 : : */
1052 : 1 : if (buf.st_uid != geteuid ())
1053 : 0 : goto out;
1054 : :
1055 : : /* if it isn't a socket, silently don't use it */
1056 : 1 : if ((buf.st_mode & S_IFMT) != S_IFSOCK)
1057 : 0 : goto out;
1058 : :
1059 : 1 : tmp = g_dbus_address_escape_value (bus);
1060 : 1 : ret = g_strconcat ("unix:path=", tmp, NULL);
1061 : 1 : g_free (tmp);
1062 : :
1063 : 68 : out:
1064 : 68 : g_free (bus);
1065 : 68 : return ret;
1066 : : #else
1067 : : return NULL;
1068 : : #endif
1069 : : }
1070 : :
1071 : : /* ---------------------------------------------------------------------------------------------------- */
1072 : :
1073 : : #ifdef G_OS_UNIX
1074 : : static gchar *
1075 : 67 : get_session_address_dbus_launch (GError **error)
1076 : : {
1077 : : gchar *ret;
1078 : : gchar *machine_id;
1079 : : gchar *command_line;
1080 : : gchar *launch_stdout;
1081 : : gchar *launch_stderr;
1082 : : gint wait_status;
1083 : : gchar *old_dbus_verbose;
1084 : : gboolean restore_dbus_verbose;
1085 : :
1086 : 67 : ret = NULL;
1087 : 67 : machine_id = NULL;
1088 : 67 : command_line = NULL;
1089 : 67 : launch_stdout = NULL;
1090 : 67 : launch_stderr = NULL;
1091 : 67 : restore_dbus_verbose = FALSE;
1092 : 67 : old_dbus_verbose = NULL;
1093 : :
1094 : : /* Don't run binaries as root if we're setuid. */
1095 : 67 : if (GLIB_PRIVATE_CALL (g_check_setuid) ())
1096 : : {
1097 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1098 : : _("Cannot spawn a message bus when AT_SECURE is set"));
1099 : 0 : goto out;
1100 : : }
1101 : :
1102 : 67 : machine_id = _g_dbus_get_machine_id (error);
1103 : 67 : if (machine_id == NULL)
1104 : : {
1105 : 0 : g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
1106 : 0 : goto out;
1107 : : }
1108 : :
1109 : 67 : if (g_getenv ("DISPLAY") == NULL)
1110 : : {
1111 : 66 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1112 : : _("Cannot autolaunch D-Bus without X11 $DISPLAY"));
1113 : 66 : goto out;
1114 : : }
1115 : :
1116 : : /* We're using private libdbus facilities here. When everything
1117 : : * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
1118 : : * X11 property is correctly documented right now) we should
1119 : : * consider using the spec instead of dbus-launch.
1120 : : *
1121 : : * --autolaunch=MACHINEID
1122 : : * This option implies that dbus-launch should scan for a previ‐
1123 : : * ously-started session and reuse the values found there. If no
1124 : : * session is found, it will start a new session. The --exit-with-
1125 : : * session option is implied if --autolaunch is given. This option
1126 : : * is for the exclusive use of libdbus, you do not want to use it
1127 : : * manually. It may change in the future.
1128 : : */
1129 : :
1130 : : /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1131 : 1 : command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1132 : :
1133 : 1 : if (G_UNLIKELY (_g_dbus_debug_address ()))
1134 : : {
1135 : 0 : _g_dbus_debug_print_lock ();
1136 : 0 : g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
1137 : 0 : old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1138 : 0 : restore_dbus_verbose = TRUE;
1139 : 0 : g_setenv ("DBUS_VERBOSE", "1", TRUE);
1140 : 0 : _g_dbus_debug_print_unlock ();
1141 : : }
1142 : :
1143 : 1 : if (!g_spawn_command_line_sync (command_line,
1144 : : &launch_stdout,
1145 : : &launch_stderr,
1146 : : &wait_status,
1147 : : error))
1148 : : {
1149 : 0 : goto out;
1150 : : }
1151 : :
1152 : 1 : if (!g_spawn_check_wait_status (wait_status, error))
1153 : : {
1154 : 0 : g_prefix_error (error, _("Error spawning command line “%s”: "), command_line);
1155 : 0 : goto out;
1156 : : }
1157 : :
1158 : : /* From the dbus-launch(1) man page:
1159 : : *
1160 : : * --binary-syntax Write to stdout a nul-terminated bus address,
1161 : : * then the bus PID as a binary integer of size sizeof(pid_t),
1162 : : * then the bus X window ID as a binary integer of size
1163 : : * sizeof(long). Integers are in the machine's byte order, not
1164 : : * network byte order or any other canonical byte order.
1165 : : */
1166 : 2 : ret = g_strdup (launch_stdout);
1167 : :
1168 : 67 : out:
1169 : 67 : if (G_UNLIKELY (_g_dbus_debug_address ()))
1170 : : {
1171 : : gchar *s;
1172 : 0 : _g_dbus_debug_print_lock ();
1173 : 0 : g_print ("GDBus-debug:Address: dbus-launch output:");
1174 : 0 : if (launch_stdout != NULL)
1175 : : {
1176 : 0 : s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1177 : 0 : g_print ("\n%s", s);
1178 : 0 : g_free (s);
1179 : : }
1180 : : else
1181 : : {
1182 : 0 : g_print (" (none)\n");
1183 : : }
1184 : 0 : g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1185 : 0 : if (launch_stderr != NULL)
1186 : 0 : g_print ("\n%s", launch_stderr);
1187 : : else
1188 : 0 : g_print (" (none)\n");
1189 : 0 : _g_dbus_debug_print_unlock ();
1190 : : }
1191 : :
1192 : 67 : g_free (machine_id);
1193 : 67 : g_free (command_line);
1194 : 67 : g_free (launch_stdout);
1195 : 67 : g_free (launch_stderr);
1196 : 67 : if (G_UNLIKELY (restore_dbus_verbose))
1197 : : {
1198 : 0 : if (old_dbus_verbose != NULL)
1199 : 0 : g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1200 : : else
1201 : 0 : g_unsetenv ("DBUS_VERBOSE");
1202 : : }
1203 : 67 : g_free (old_dbus_verbose);
1204 : 67 : return ret;
1205 : : }
1206 : :
1207 : : /* end of G_OS_UNIX case */
1208 : : #elif defined(G_OS_WIN32)
1209 : :
1210 : : static gchar *
1211 : : get_session_address_dbus_launch (GError **error)
1212 : : {
1213 : : return _g_dbus_win32_get_session_address_dbus_launch (error);
1214 : : }
1215 : :
1216 : : #else /* neither G_OS_UNIX nor G_OS_WIN32 */
1217 : : static gchar *
1218 : : get_session_address_dbus_launch (GError **error)
1219 : : {
1220 : : g_set_error (error,
1221 : : G_IO_ERROR,
1222 : : G_IO_ERROR_FAILED,
1223 : : _("Cannot determine session bus address (not implemented for this OS)"));
1224 : : return NULL;
1225 : : }
1226 : : #endif /* neither G_OS_UNIX nor G_OS_WIN32 */
1227 : :
1228 : : /* ---------------------------------------------------------------------------------------------------- */
1229 : :
1230 : : static gchar *
1231 : 68 : get_session_address_platform_specific (GError **error)
1232 : : {
1233 : : gchar *ret;
1234 : :
1235 : : /* Use XDG_RUNTIME_DIR/bus if it exists and is suitable. This is appropriate
1236 : : * for systems using the "a session is a user-session" model described in
1237 : : * <http://lists.freedesktop.org/archives/dbus/2015-January/016522.html>,
1238 : : * and implemented in dbus >= 1.9.14 and sd-bus.
1239 : : *
1240 : : * On systems following the more traditional "a session is a login-session"
1241 : : * model, this will fail and we'll fall through to X11 autolaunching
1242 : : * (dbus-launch) below.
1243 : : */
1244 : 68 : ret = get_session_address_xdg ();
1245 : :
1246 : 68 : if (ret != NULL)
1247 : 1 : return ret;
1248 : :
1249 : : /* TODO (#694472): try launchd on OS X, like
1250 : : * _dbus_lookup_session_address_launchd() does, since
1251 : : * 'dbus-launch --autolaunch' probably won't work there
1252 : : */
1253 : :
1254 : : /* As a last resort, try the "autolaunch:" transport. On Unix this means
1255 : : * X11 autolaunching; on Windows this means a different autolaunching
1256 : : * mechanism based on shared memory.
1257 : : */
1258 : 67 : return get_session_address_dbus_launch (error);
1259 : : }
1260 : :
1261 : : /* ---------------------------------------------------------------------------------------------------- */
1262 : :
1263 : : /**
1264 : : * g_dbus_address_get_for_bus_sync:
1265 : : * @bus_type: a #GBusType
1266 : : * @cancellable: (nullable): a #GCancellable or %NULL
1267 : : * @error: return location for error or %NULL
1268 : : *
1269 : : * Synchronously looks up the D-Bus address for the well-known message
1270 : : * bus instance specified by @bus_type. This may involve using various
1271 : : * platform specific mechanisms.
1272 : : *
1273 : : * The returned address will be in the
1274 : : * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
1275 : : *
1276 : : * Returns: (transfer full): a valid D-Bus address string for @bus_type or
1277 : : * %NULL if @error is set
1278 : : *
1279 : : * Since: 2.26
1280 : : */
1281 : : gchar *
1282 : 2265 : g_dbus_address_get_for_bus_sync (GBusType bus_type,
1283 : : GCancellable *cancellable,
1284 : : GError **error)
1285 : : {
1286 : 2265 : gboolean has_elevated_privileges = GLIB_PRIVATE_CALL (g_check_setuid) ();
1287 : 2265 : gchar *ret, *s = NULL;
1288 : : const gchar *starter_bus;
1289 : : GError *local_error;
1290 : :
1291 : 2265 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1292 : :
1293 : 2265 : ret = NULL;
1294 : 2265 : local_error = NULL;
1295 : :
1296 : 2265 : if (G_UNLIKELY (_g_dbus_debug_address ()))
1297 : : {
1298 : : guint n;
1299 : 0 : _g_dbus_debug_print_lock ();
1300 : 0 : s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1301 : 0 : g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
1302 : : s);
1303 : 0 : g_free (s);
1304 : 0 : for (n = 0; n < 3; n++)
1305 : : {
1306 : : const gchar *k;
1307 : : const gchar *v;
1308 : 0 : switch (n)
1309 : : {
1310 : 0 : case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1311 : 0 : case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1312 : 0 : case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1313 : : default: g_assert_not_reached ();
1314 : : }
1315 : 0 : v = g_getenv (k);
1316 : 0 : g_print ("GDBus-debug:Address: env var %s", k);
1317 : 0 : if (v != NULL)
1318 : 0 : g_print ("='%s'\n", v);
1319 : : else
1320 : 0 : g_print (" is not set\n");
1321 : : }
1322 : 0 : _g_dbus_debug_print_unlock ();
1323 : : }
1324 : :
1325 : : /* Don’t load the addresses from the environment if running as setuid, as they
1326 : : * come from an unprivileged caller. */
1327 : 2265 : switch (bus_type)
1328 : : {
1329 : 24 : case G_BUS_TYPE_SYSTEM:
1330 : 24 : if (has_elevated_privileges)
1331 : 0 : ret = NULL;
1332 : : else
1333 : 48 : ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1334 : :
1335 : 24 : if (ret == NULL)
1336 : : {
1337 : : /* While the D-Bus specification says this must be `/var/run/dbus/system_bus_socket`,
1338 : : * a footnote allows it to use localstatedir:
1339 : : * https://dbus.freedesktop.org/doc/dbus-specification.html#ftn.id-1.13.6.4.3.3
1340 : : * or, on systems where /run is the same as /var/run, runstatedir:
1341 : : * https://gitlab.freedesktop.org/dbus/dbus/-/merge_requests/209 */
1342 : 24 : ret = g_strdup ("unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
1343 : : }
1344 : 24 : break;
1345 : :
1346 : 2241 : case G_BUS_TYPE_SESSION:
1347 : 2241 : if (has_elevated_privileges)
1348 : 0 : ret = NULL;
1349 : : else
1350 : 4482 : ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1351 : :
1352 : 2241 : if (ret == NULL)
1353 : : {
1354 : 68 : ret = get_session_address_platform_specific (&local_error);
1355 : : }
1356 : 2241 : break;
1357 : :
1358 : 0 : case G_BUS_TYPE_STARTER:
1359 : 0 : starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1360 : 0 : if (g_strcmp0 (starter_bus, "session") == 0)
1361 : : {
1362 : 0 : ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1363 : 0 : goto out;
1364 : : }
1365 : 0 : else if (g_strcmp0 (starter_bus, "system") == 0)
1366 : : {
1367 : 0 : ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1368 : 0 : goto out;
1369 : : }
1370 : : else
1371 : : {
1372 : 0 : if (starter_bus != NULL)
1373 : : {
1374 : 0 : g_set_error (&local_error,
1375 : : G_IO_ERROR,
1376 : : G_IO_ERROR_FAILED,
1377 : : _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1378 : : " — unknown value “%s”"),
1379 : : starter_bus);
1380 : : }
1381 : : else
1382 : : {
1383 : 0 : g_set_error_literal (&local_error,
1384 : : G_IO_ERROR,
1385 : : G_IO_ERROR_FAILED,
1386 : : _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1387 : : "variable is not set"));
1388 : : }
1389 : : }
1390 : 0 : break;
1391 : :
1392 : 0 : default:
1393 : 0 : g_set_error (&local_error,
1394 : : G_IO_ERROR,
1395 : : G_IO_ERROR_FAILED,
1396 : : _("Unknown bus type %d"),
1397 : : bus_type);
1398 : 0 : break;
1399 : : }
1400 : :
1401 : 2265 : out:
1402 : 2265 : if (G_UNLIKELY (_g_dbus_debug_address ()))
1403 : : {
1404 : 0 : _g_dbus_debug_print_lock ();
1405 : 0 : s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1406 : 0 : if (ret != NULL)
1407 : : {
1408 : 0 : g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
1409 : : ret, s);
1410 : : }
1411 : : else
1412 : : {
1413 : 0 : g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
1414 : 0 : s, local_error ? local_error->message : "");
1415 : : }
1416 : 0 : g_free (s);
1417 : 0 : _g_dbus_debug_print_unlock ();
1418 : : }
1419 : :
1420 : 2265 : if (local_error != NULL)
1421 : 66 : g_propagate_error (error, local_error);
1422 : :
1423 : 2265 : return ret;
1424 : : }
1425 : :
1426 : : /**
1427 : : * g_dbus_address_escape_value:
1428 : : * @string: an unescaped string to be included in a D-Bus address
1429 : : * as the value in a key-value pair
1430 : : *
1431 : : * Escape @string so it can appear in a D-Bus address as the value
1432 : : * part of a key-value pair.
1433 : : *
1434 : : * For instance, if @string is `/run/bus-for-:0`,
1435 : : * this function would return `/run/bus-for-%3A0`,
1436 : : * which could be used in a D-Bus address like
1437 : : * `unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0`.
1438 : : *
1439 : : * Returns: (transfer full): a copy of @string with all
1440 : : * non-optionally-escaped bytes escaped
1441 : : *
1442 : : * Since: 2.36
1443 : : */
1444 : : gchar *
1445 : 36 : g_dbus_address_escape_value (const gchar *string)
1446 : : {
1447 : : GString *s;
1448 : : gsize i;
1449 : :
1450 : 36 : g_return_val_if_fail (string != NULL, NULL);
1451 : :
1452 : : /* There will often not be anything needing escaping at all. */
1453 : 36 : s = g_string_sized_new (strlen (string));
1454 : :
1455 : : /* D-Bus address escaping is mostly the same as URI escaping... */
1456 : 36 : g_string_append_uri_escaped (s, string, "\\/", FALSE);
1457 : :
1458 : : /* ... but '~' is an unreserved character in URIs, but a
1459 : : * non-optionally-escaped character in D-Bus addresses. */
1460 : 1909 : for (i = 0; i < s->len; i++)
1461 : : {
1462 : 1873 : if (G_UNLIKELY (s->str[i] == '~'))
1463 : : {
1464 : 1 : s->str[i] = '%';
1465 : 1 : g_string_insert (s, i + 1, "7E");
1466 : 1 : i += 2;
1467 : : }
1468 : : }
1469 : :
1470 : 36 : return g_string_free (s, FALSE);
1471 : : }
|