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 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 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 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 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 * 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 * 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 * 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 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 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* 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* 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 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