1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-address.c Server address parser.
3 *
4 * Copyright (C) 2003 CodeFactory AB
5 * Copyright (C) 2004,2005 Red Hat, Inc.
6 *
7 * Licensed under the Academic Free License version 2.1
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25 #include <config.h>
26 #include "dbus-address.h"
27 #include "dbus-internals.h"
28 #include "dbus-list.h"
29 #include "dbus-string.h"
30 #include "dbus-protocol.h"
31
32 /**
33 * @defgroup DBusAddressInternals Address parsing
34 * @ingroup DBusInternals
35 * @brief Implementation of parsing addresses of D-Bus servers.
36 *
37 * @{
38 */
39
40 /**
41 * Internals of DBusAddressEntry
42 */
43 struct DBusAddressEntry
44 {
45 DBusString method; /**< The address type (unix, tcp, etc.) */
46
47 DBusList *keys; /**< List of keys */
48 DBusList *values; /**< List of values */
49 };
50
51
52 /**
53 *
54 * Sets #DBUS_ERROR_BAD_ADDRESS.
55 * If address_problem_type and address_problem_field are not #NULL,
56 * sets an error message about how the field is no good. Otherwise, sets
57 * address_problem_other as the error message.
58 *
59 * @param error the error to set
60 * @param address_problem_type the address type of the bad address or #NULL
61 * @param address_problem_field the missing field of the bad address or #NULL
62 * @param address_problem_other any other error message or #NULL
63 */
64 void
_dbus_set_bad_address(DBusError * error,const char * address_problem_type,const char * address_problem_field,const char * address_problem_other)65 _dbus_set_bad_address (DBusError *error,
66 const char *address_problem_type,
67 const char *address_problem_field,
68 const char *address_problem_other)
69 {
70 if (address_problem_type != NULL)
71 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
72 "Server address of type %s was missing argument %s",
73 address_problem_type, address_problem_field);
74 else
75 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
76 "Could not parse server address: %s",
77 address_problem_other);
78 }
79
80 /**
81 * #TRUE if the byte need not be escaped when found in a dbus address.
82 * All other bytes are required to be escaped in a valid address.
83 */
84 #define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b) \
85 (((b) >= 'a' && (b) <= 'z') || \
86 ((b) >= 'A' && (b) <= 'Z') || \
87 ((b) >= '0' && (b) <= '9') || \
88 (b) == '-' || \
89 (b) == '_' || \
90 (b) == '/' || \
91 (b) == '\\' || \
92 (b) == '.')
93
94 /**
95 * Appends an escaped version of one string to another string,
96 * using the D-Bus address escaping mechanism
97 *
98 * @param escaped the string to append to
99 * @param unescaped the string to escape
100 * @returns #FALSE if no memory
101 */
102 dbus_bool_t
_dbus_address_append_escaped(DBusString * escaped,const DBusString * unescaped)103 _dbus_address_append_escaped (DBusString *escaped,
104 const DBusString *unescaped)
105 {
106 const char *p;
107 const char *end;
108 dbus_bool_t ret;
109 int orig_len;
110
111 ret = FALSE;
112
113 orig_len = _dbus_string_get_length (escaped);
114 p = _dbus_string_get_const_data (unescaped);
115 end = p + _dbus_string_get_length (unescaped);
116 while (p != end)
117 {
118 if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
119 {
120 if (!_dbus_string_append_byte (escaped, *p))
121 goto out;
122 }
123 else
124 {
125 if (!_dbus_string_append_byte (escaped, '%'))
126 goto out;
127 if (!_dbus_string_append_byte_as_hex (escaped, *p))
128 goto out;
129 }
130
131 ++p;
132 }
133
134 ret = TRUE;
135
136 out:
137 if (!ret)
138 _dbus_string_set_length (escaped, orig_len);
139 return ret;
140 }
141
142 /** @} */ /* End of internals */
143
144 static void
dbus_address_entry_free(DBusAddressEntry * entry)145 dbus_address_entry_free (DBusAddressEntry *entry)
146 {
147 DBusList *link;
148
149 _dbus_string_free (&entry->method);
150
151 link = _dbus_list_get_first_link (&entry->keys);
152 while (link != NULL)
153 {
154 _dbus_string_free (link->data);
155 dbus_free (link->data);
156
157 link = _dbus_list_get_next_link (&entry->keys, link);
158 }
159 _dbus_list_clear (&entry->keys);
160
161 link = _dbus_list_get_first_link (&entry->values);
162 while (link != NULL)
163 {
164 _dbus_string_free (link->data);
165 dbus_free (link->data);
166
167 link = _dbus_list_get_next_link (&entry->values, link);
168 }
169 _dbus_list_clear (&entry->values);
170
171 dbus_free (entry);
172 }
173
174 /**
175 * @defgroup DBusAddress Address parsing
176 * @ingroup DBus
177 * @brief Parsing addresses of D-Bus servers.
178 *
179 * @{
180 */
181
182 /**
183 * Frees a #NULL-terminated array of address entries.
184 *
185 * @param entries the array.
186 */
187 void
dbus_address_entries_free(DBusAddressEntry ** entries)188 dbus_address_entries_free (DBusAddressEntry **entries)
189 {
190 int i;
191
192 for (i = 0; entries[i] != NULL; i++)
193 dbus_address_entry_free (entries[i]);
194 dbus_free (entries);
195 }
196
197 static DBusAddressEntry *
create_entry(void)198 create_entry (void)
199 {
200 DBusAddressEntry *entry;
201
202 entry = dbus_new0 (DBusAddressEntry, 1);
203
204 if (entry == NULL)
205 return NULL;
206
207 if (!_dbus_string_init (&entry->method))
208 {
209 dbus_free (entry);
210 return NULL;
211 }
212
213 return entry;
214 }
215
216 /**
217 * Returns the method string of an address entry. For example, given
218 * the address entry "tcp:host=example.com" it would return the string
219 * "tcp"
220 *
221 * @param entry the entry.
222 * @returns a string describing the method. This string
223 * must not be freed.
224 */
225 const char *
dbus_address_entry_get_method(DBusAddressEntry * entry)226 dbus_address_entry_get_method (DBusAddressEntry *entry)
227 {
228 return _dbus_string_get_const_data (&entry->method);
229 }
230
231 /**
232 * Returns a value from a key of an entry. For example,
233 * given the address "tcp:host=example.com,port=8073" if you asked
234 * for the key "host" you would get the value "example.com"
235 *
236 * The returned value is already unescaped.
237 *
238 * @param entry the entry.
239 * @param key the key.
240 * @returns the key value. This string must not be freed.
241 */
242 const char *
dbus_address_entry_get_value(DBusAddressEntry * entry,const char * key)243 dbus_address_entry_get_value (DBusAddressEntry *entry,
244 const char *key)
245 {
246 DBusList *values, *keys;
247
248 keys = _dbus_list_get_first_link (&entry->keys);
249 values = _dbus_list_get_first_link (&entry->values);
250
251 while (keys != NULL)
252 {
253 _dbus_assert (values != NULL);
254
255 if (_dbus_string_equal_c_str (keys->data, key))
256 return _dbus_string_get_const_data (values->data);
257
258 keys = _dbus_list_get_next_link (&entry->keys, keys);
259 values = _dbus_list_get_next_link (&entry->values, values);
260 }
261
262 return NULL;
263 }
264
265 static dbus_bool_t
append_unescaped_value(DBusString * unescaped,const DBusString * escaped,int escaped_start,int escaped_len,DBusError * error)266 append_unescaped_value (DBusString *unescaped,
267 const DBusString *escaped,
268 int escaped_start,
269 int escaped_len,
270 DBusError *error)
271 {
272 const char *p;
273 const char *end;
274 dbus_bool_t ret;
275
276 ret = FALSE;
277
278 p = _dbus_string_get_const_data (escaped) + escaped_start;
279 end = p + escaped_len;
280 while (p != end)
281 {
282 if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
283 {
284 if (!_dbus_string_append_byte (unescaped, *p))
285 goto out;
286 }
287 else if (*p == '%')
288 {
289 /* Efficiency is king */
290 char buf[3];
291 DBusString hex;
292 int hex_end;
293
294 ++p;
295
296 if ((p + 2) > end)
297 {
298 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
299 "In D-Bus address, percent character was not followed by two hex digits");
300 goto out;
301 }
302
303 buf[0] = *p;
304 ++p;
305 buf[1] = *p;
306 buf[2] = '\0';
307
308 _dbus_string_init_const (&hex, buf);
309
310 if (!_dbus_string_hex_decode (&hex, 0, &hex_end,
311 unescaped,
312 _dbus_string_get_length (unescaped)))
313 goto out;
314
315 if (hex_end != 2)
316 {
317 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
318 "In D-Bus address, percent character was followed by characters other than hex digits");
319 goto out;
320 }
321 }
322 else
323 {
324 /* Error, should have been escaped */
325 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
326 "In D-Bus address, character '%c' should have been escaped\n",
327 *p);
328 goto out;
329 }
330
331 ++p;
332 }
333
334 ret = TRUE;
335
336 out:
337 if (!ret && error && !dbus_error_is_set (error))
338 _DBUS_SET_OOM (error);
339
340 _dbus_assert (ret || error == NULL || dbus_error_is_set (error));
341
342 return ret;
343 }
344
345 /**
346 * Parses an address string of the form:
347 *
348 * method:key=value,key=value;method:key=value
349 *
350 * See the D-Bus specification for complete docs on the format.
351 *
352 * When connecting to an address, the first address entries
353 * in the semicolon-separated list should be tried first.
354 *
355 * @param address the address.
356 * @param entry return location to an array of entries.
357 * @param array_len return location for array length.
358 * @param error address where an error can be returned.
359 * @returns #TRUE on success, #FALSE otherwise.
360 */
361 dbus_bool_t
dbus_parse_address(const char * address,DBusAddressEntry *** entry,int * array_len,DBusError * error)362 dbus_parse_address (const char *address,
363 DBusAddressEntry ***entry,
364 int *array_len,
365 DBusError *error)
366 {
367 DBusString str;
368 int pos, end_pos, len, i;
369 DBusList *entries, *link;
370 DBusAddressEntry **entry_array;
371
372 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
373
374 _dbus_string_init_const (&str, address);
375
376 entries = NULL;
377 pos = 0;
378 len = _dbus_string_get_length (&str);
379
380 while (pos < len)
381 {
382 DBusAddressEntry *entry;
383
384 int found_pos;
385
386 entry = create_entry ();
387 if (!entry)
388 {
389 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
390
391 goto error;
392 }
393
394 /* Append the entry */
395 if (!_dbus_list_append (&entries, entry))
396 {
397 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
398 dbus_address_entry_free (entry);
399 goto error;
400 }
401
402 /* Look for a semi-colon */
403 if (!_dbus_string_find (&str, pos, ";", &end_pos))
404 end_pos = len;
405
406 /* Look for the colon : */
407 if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
408 {
409 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
410 goto error;
411 }
412
413 if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
414 {
415 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
416 goto error;
417 }
418
419 pos = found_pos + 1;
420
421 while (pos < end_pos)
422 {
423 int comma_pos, equals_pos;
424
425 if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
426 comma_pos = end_pos;
427
428 if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
429 equals_pos == pos || equals_pos + 1 == comma_pos)
430 {
431 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
432 "'=' character not found or has no value following it");
433 goto error;
434 }
435 else
436 {
437 DBusString *key;
438 DBusString *value;
439
440 key = dbus_new0 (DBusString, 1);
441
442 if (!key)
443 {
444 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
445 goto error;
446 }
447
448 value = dbus_new0 (DBusString, 1);
449 if (!value)
450 {
451 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
452 dbus_free (key);
453 goto error;
454 }
455
456 if (!_dbus_string_init (key))
457 {
458 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
459 dbus_free (key);
460 dbus_free (value);
461
462 goto error;
463 }
464
465 if (!_dbus_string_init (value))
466 {
467 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
468 _dbus_string_free (key);
469
470 dbus_free (key);
471 dbus_free (value);
472 goto error;
473 }
474
475 if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
476 {
477 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
478 _dbus_string_free (key);
479 _dbus_string_free (value);
480
481 dbus_free (key);
482 dbus_free (value);
483 goto error;
484 }
485
486 if (!append_unescaped_value (value, &str, equals_pos + 1,
487 comma_pos - equals_pos - 1, error))
488 {
489 _dbus_assert (error == NULL || dbus_error_is_set (error));
490 _dbus_string_free (key);
491 _dbus_string_free (value);
492
493 dbus_free (key);
494 dbus_free (value);
495 goto error;
496 }
497
498 if (!_dbus_list_append (&entry->keys, key))
499 {
500 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
501 _dbus_string_free (key);
502 _dbus_string_free (value);
503
504 dbus_free (key);
505 dbus_free (value);
506 goto error;
507 }
508
509 if (!_dbus_list_append (&entry->values, value))
510 {
511 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
512 _dbus_string_free (value);
513
514 dbus_free (value);
515 goto error;
516 }
517 }
518
519 pos = comma_pos + 1;
520 }
521
522 pos = end_pos + 1;
523 }
524
525 *array_len = _dbus_list_get_length (&entries);
526
527 entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
528
529 if (!entry_array)
530 {
531 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
532
533 goto error;
534 }
535
536 entry_array [*array_len] = NULL;
537
538 link = _dbus_list_get_first_link (&entries);
539 i = 0;
540 while (link != NULL)
541 {
542 entry_array[i] = link->data;
543 i++;
544 link = _dbus_list_get_next_link (&entries, link);
545 }
546
547 _dbus_list_clear (&entries);
548 *entry = entry_array;
549
550 return TRUE;
551
552 error:
553
554 link = _dbus_list_get_first_link (&entries);
555 while (link != NULL)
556 {
557 dbus_address_entry_free (link->data);
558 link = _dbus_list_get_next_link (&entries, link);
559 }
560
561 _dbus_list_clear (&entries);
562
563 return FALSE;
564
565 }
566
567 /**
568 * Escapes the given string as a value in a key=value pair
569 * for a D-Bus address.
570 *
571 * @param value the unescaped value
572 * @returns newly-allocated escaped value or #NULL if no memory
573 */
574 char*
dbus_address_escape_value(const char * value)575 dbus_address_escape_value (const char *value)
576 {
577 DBusString escaped;
578 DBusString unescaped;
579 char *ret;
580
581 ret = NULL;
582
583 _dbus_string_init_const (&unescaped, value);
584
585 if (!_dbus_string_init (&escaped))
586 return NULL;
587
588 if (!_dbus_address_append_escaped (&escaped, &unescaped))
589 goto out;
590
591 if (!_dbus_string_steal_data (&escaped, &ret))
592 goto out;
593
594 out:
595 _dbus_string_free (&escaped);
596 return ret;
597 }
598
599 /**
600 * Unescapes the given string as a value in a key=value pair
601 * for a D-Bus address. Note that dbus_address_entry_get_value()
602 * returns an already-unescaped value.
603 *
604 * @param value the escaped value
605 * @param error error to set if the unescaping fails
606 * @returns newly-allocated unescaped value or #NULL if no memory
607 */
608 char*
dbus_address_unescape_value(const char * value,DBusError * error)609 dbus_address_unescape_value (const char *value,
610 DBusError *error)
611 {
612 DBusString unescaped;
613 DBusString escaped;
614 char *ret;
615
616 ret = NULL;
617
618 _dbus_string_init_const (&escaped, value);
619
620 if (!_dbus_string_init (&unescaped))
621 return NULL;
622
623 if (!append_unescaped_value (&unescaped, &escaped,
624 0, _dbus_string_get_length (&escaped),
625 error))
626 goto out;
627
628 if (!_dbus_string_steal_data (&unescaped, &ret))
629 goto out;
630
631 out:
632 if (ret == NULL && error && !dbus_error_is_set (error))
633 _DBUS_SET_OOM (error);
634
635 _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
636
637 _dbus_string_free (&unescaped);
638 return ret;
639 }
640
641 /** @} */ /* End of public API */
642
643 #ifdef DBUS_BUILD_TESTS
644
645 #ifndef DOXYGEN_SHOULD_SKIP_THIS
646
647 #include "dbus-test.h"
648 #include <stdlib.h>
649
650 typedef struct
651 {
652 const char *escaped;
653 const char *unescaped;
654 } EscapeTest;
655
656 static const EscapeTest escape_tests[] = {
657 { "abcde", "abcde" },
658 { "", "" },
659 { "%20%20", " " },
660 { "%24", "$" },
661 { "%25", "%" },
662 { "abc%24", "abc$" },
663 { "%24abc", "$abc" },
664 { "abc%24abc", "abc$abc" },
665 { "/", "/" },
666 { "-", "-" },
667 { "_", "_" },
668 { "A", "A" },
669 { "I", "I" },
670 { "Z", "Z" },
671 { "a", "a" },
672 { "i", "i" },
673 { "z", "z" }
674 };
675
676 static const char* invalid_escaped_values[] = {
677 "%a",
678 "%q",
679 "%az",
680 "%%",
681 "%$$",
682 "abc%a",
683 "%axyz",
684 "%",
685 "$",
686 " ",
687 "*"
688 };
689
690 dbus_bool_t
_dbus_address_test(void)691 _dbus_address_test (void)
692 {
693 DBusAddressEntry **entries;
694 int len;
695 DBusError error;
696 int i;
697
698 dbus_error_init (&error);
699
700 i = 0;
701 while (i < _DBUS_N_ELEMENTS (escape_tests))
702 {
703 const EscapeTest *test = &escape_tests[i];
704 char *escaped;
705 char *unescaped;
706
707 escaped = dbus_address_escape_value (test->unescaped);
708 if (escaped == NULL)
709 _dbus_assert_not_reached ("oom");
710
711 if (strcmp (escaped, test->escaped) != 0)
712 {
713 _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
714 test->unescaped, escaped, test->escaped);
715 exit (1);
716 }
717 dbus_free (escaped);
718
719 unescaped = dbus_address_unescape_value (test->escaped, &error);
720 if (unescaped == NULL)
721 {
722 _dbus_warn ("Failed to unescape '%s': %s\n",
723 test->escaped, error.message);
724 dbus_error_free (&error);
725 exit (1);
726 }
727
728 if (strcmp (unescaped, test->unescaped) != 0)
729 {
730 _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
731 test->escaped, unescaped, test->unescaped);
732 exit (1);
733 }
734 dbus_free (unescaped);
735
736 ++i;
737 }
738
739 i = 0;
740 while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
741 {
742 char *unescaped;
743
744 unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
745 &error);
746 if (unescaped != NULL)
747 {
748 _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
749 invalid_escaped_values[i], unescaped);
750 dbus_free (unescaped);
751 exit (1);
752 }
753
754 _dbus_assert (dbus_error_is_set (&error));
755 dbus_error_free (&error);
756
757 ++i;
758 }
759
760 if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
761 &entries, &len, &error))
762 _dbus_assert_not_reached ("could not parse address");
763 _dbus_assert (len == 2);
764 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
765 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
766 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
767
768 dbus_address_entries_free (entries);
769
770 /* Different possible errors */
771 if (dbus_parse_address ("foo", &entries, &len, &error))
772 _dbus_assert_not_reached ("Parsed incorrect address.");
773 else
774 dbus_error_free (&error);
775
776 if (dbus_parse_address ("foo:bar", &entries, &len, &error))
777 _dbus_assert_not_reached ("Parsed incorrect address.");
778 else
779 dbus_error_free (&error);
780
781 if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error))
782 _dbus_assert_not_reached ("Parsed incorrect address.");
783 else
784 dbus_error_free (&error);
785
786 if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error))
787 _dbus_assert_not_reached ("Parsed incorrect address.");
788 else
789 dbus_error_free (&error);
790
791 if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
792 _dbus_assert_not_reached ("Parsed incorrect address.");
793 else
794 dbus_error_free (&error);
795
796 if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
797 _dbus_assert_not_reached ("Parsed incorrect address.");
798 else
799 dbus_error_free (&error);
800
801 if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
802 _dbus_assert_not_reached ("Parsed incorrect address.");
803 else
804 dbus_error_free (&error);
805
806 if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error))
807 _dbus_assert_not_reached ("Parsed incorrect address.");
808 else
809 dbus_error_free (&error);
810
811 return TRUE;
812 }
813
814 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
815
816 #endif
817