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