D-Bus
1.10.12
|
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00002 /* dbus-address.c Server address parser. 00003 * 00004 * Copyright (C) 2003 CodeFactory AB 00005 * Copyright (C) 2004,2005 Red Hat, Inc. 00006 * 00007 * Licensed under the Academic Free License version 2.1 00008 * 00009 * This program is free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00022 * 00023 */ 00024 00025 #include <config.h> 00026 #include "dbus-address.h" 00027 #include "dbus-internals.h" 00028 #include "dbus-list.h" 00029 #include "dbus-string.h" 00030 #include "dbus-protocol.h" 00031 00043 struct DBusAddressEntry 00044 { 00045 DBusString method; 00047 DBusList *keys; 00048 DBusList *values; 00049 }; 00050 00051 00064 void 00065 _dbus_set_bad_address (DBusError *error, 00066 const char *address_problem_type, 00067 const char *address_problem_field, 00068 const char *address_problem_other) 00069 { 00070 if (address_problem_type != NULL) 00071 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00072 "Server address of type %s was missing argument %s", 00073 address_problem_type, address_problem_field); 00074 else 00075 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00076 "Could not parse server address: %s", 00077 address_problem_other); 00078 } 00079 00084 #define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b) \ 00085 (((b) >= 'a' && (b) <= 'z') || \ 00086 ((b) >= 'A' && (b) <= 'Z') || \ 00087 ((b) >= '0' && (b) <= '9') || \ 00088 (b) == '-' || \ 00089 (b) == '_' || \ 00090 (b) == '/' || \ 00091 (b) == '\\' || \ 00092 (b) == '*' || \ 00093 (b) == '.') 00094 00103 dbus_bool_t 00104 _dbus_address_append_escaped (DBusString *escaped, 00105 const DBusString *unescaped) 00106 { 00107 const unsigned char *p; 00108 const unsigned char *end; 00109 dbus_bool_t ret; 00110 int orig_len; 00111 00112 ret = FALSE; 00113 00114 orig_len = _dbus_string_get_length (escaped); 00115 p = (const unsigned char *) _dbus_string_get_const_data (unescaped); 00116 end = p + _dbus_string_get_length (unescaped); 00117 while (p != end) 00118 { 00119 if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) 00120 { 00121 if (!_dbus_string_append_byte (escaped, *p)) 00122 goto out; 00123 } 00124 else 00125 { 00126 if (!_dbus_string_append_byte (escaped, '%')) 00127 goto out; 00128 if (!_dbus_string_append_byte_as_hex (escaped, *p)) 00129 goto out; 00130 } 00131 00132 ++p; 00133 } 00134 00135 ret = TRUE; 00136 00137 out: 00138 if (!ret) 00139 _dbus_string_set_length (escaped, orig_len); 00140 return ret; 00141 } 00142 /* End of internals */ 00144 00145 static void 00146 dbus_address_entry_free (DBusAddressEntry *entry) 00147 { 00148 DBusList *link; 00149 00150 _dbus_string_free (&entry->method); 00151 00152 link = _dbus_list_get_first_link (&entry->keys); 00153 while (link != NULL) 00154 { 00155 _dbus_string_free (link->data); 00156 dbus_free (link->data); 00157 00158 link = _dbus_list_get_next_link (&entry->keys, link); 00159 } 00160 _dbus_list_clear (&entry->keys); 00161 00162 link = _dbus_list_get_first_link (&entry->values); 00163 while (link != NULL) 00164 { 00165 _dbus_string_free (link->data); 00166 dbus_free (link->data); 00167 00168 link = _dbus_list_get_next_link (&entry->values, link); 00169 } 00170 _dbus_list_clear (&entry->values); 00171 00172 dbus_free (entry); 00173 } 00174 00188 void 00189 dbus_address_entries_free (DBusAddressEntry **entries) 00190 { 00191 int i; 00192 00193 for (i = 0; entries[i] != NULL; i++) 00194 dbus_address_entry_free (entries[i]); 00195 dbus_free (entries); 00196 } 00197 00198 static DBusAddressEntry * 00199 create_entry (void) 00200 { 00201 DBusAddressEntry *entry; 00202 00203 entry = dbus_new0 (DBusAddressEntry, 1); 00204 00205 if (entry == NULL) 00206 return NULL; 00207 00208 if (!_dbus_string_init (&entry->method)) 00209 { 00210 dbus_free (entry); 00211 return NULL; 00212 } 00213 00214 return entry; 00215 } 00216 00226 const char * 00227 dbus_address_entry_get_method (DBusAddressEntry *entry) 00228 { 00229 return _dbus_string_get_const_data (&entry->method); 00230 } 00231 00243 const char * 00244 dbus_address_entry_get_value (DBusAddressEntry *entry, 00245 const char *key) 00246 { 00247 DBusList *values, *keys; 00248 00249 keys = _dbus_list_get_first_link (&entry->keys); 00250 values = _dbus_list_get_first_link (&entry->values); 00251 00252 while (keys != NULL) 00253 { 00254 _dbus_assert (values != NULL); 00255 00256 if (_dbus_string_equal_c_str (keys->data, key)) 00257 return _dbus_string_get_const_data (values->data); 00258 00259 keys = _dbus_list_get_next_link (&entry->keys, keys); 00260 values = _dbus_list_get_next_link (&entry->values, values); 00261 } 00262 00263 return NULL; 00264 } 00265 00266 static dbus_bool_t 00267 append_unescaped_value (DBusString *unescaped, 00268 const DBusString *escaped, 00269 int escaped_start, 00270 int escaped_len, 00271 DBusError *error) 00272 { 00273 const char *p; 00274 const char *end; 00275 dbus_bool_t ret; 00276 00277 ret = FALSE; 00278 00279 p = _dbus_string_get_const_data (escaped) + escaped_start; 00280 end = p + escaped_len; 00281 while (p != end) 00282 { 00283 if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) 00284 { 00285 if (!_dbus_string_append_byte (unescaped, *p)) 00286 goto out; 00287 } 00288 else if (*p == '%') 00289 { 00290 /* Efficiency is king */ 00291 char buf[3]; 00292 DBusString hex; 00293 int hex_end; 00294 00295 ++p; 00296 00297 if ((p + 2) > end) 00298 { 00299 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00300 "In D-Bus address, percent character was not followed by two hex digits"); 00301 goto out; 00302 } 00303 00304 buf[0] = *p; 00305 ++p; 00306 buf[1] = *p; 00307 buf[2] = '\0'; 00308 00309 _dbus_string_init_const (&hex, buf); 00310 00311 if (!_dbus_string_hex_decode (&hex, 0, &hex_end, 00312 unescaped, 00313 _dbus_string_get_length (unescaped))) 00314 goto out; 00315 00316 if (hex_end != 2) 00317 { 00318 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00319 "In D-Bus address, percent character was followed by characters other than hex digits"); 00320 goto out; 00321 } 00322 } 00323 else 00324 { 00325 /* Error, should have been escaped */ 00326 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00327 "In D-Bus address, character '%c' should have been escaped\n", 00328 *p); 00329 goto out; 00330 } 00331 00332 ++p; 00333 } 00334 00335 ret = TRUE; 00336 00337 out: 00338 if (!ret && error && !dbus_error_is_set (error)) 00339 _DBUS_SET_OOM (error); 00340 00341 _dbus_assert (ret || error == NULL || dbus_error_is_set (error)); 00342 00343 return ret; 00344 } 00345 00362 dbus_bool_t 00363 dbus_parse_address (const char *address, 00364 DBusAddressEntry ***entry, 00365 int *array_len, 00366 DBusError *error) 00367 { 00368 DBusString str; 00369 int pos, end_pos, len, i; 00370 DBusList *entries, *link; 00371 DBusAddressEntry **entry_array; 00372 00373 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00374 00375 _dbus_string_init_const (&str, address); 00376 00377 entries = NULL; 00378 pos = 0; 00379 len = _dbus_string_get_length (&str); 00380 00381 if (len == 0) 00382 { 00383 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00384 "Empty address '%s'", address); 00385 goto error; 00386 } 00387 00388 while (pos < len) 00389 { 00390 DBusAddressEntry *entry; 00391 00392 int found_pos; 00393 00394 entry = create_entry (); 00395 if (!entry) 00396 { 00397 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00398 00399 goto error; 00400 } 00401 00402 /* Append the entry */ 00403 if (!_dbus_list_append (&entries, entry)) 00404 { 00405 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00406 dbus_address_entry_free (entry); 00407 goto error; 00408 } 00409 00410 /* Look for a semi-colon */ 00411 if (!_dbus_string_find (&str, pos, ";", &end_pos)) 00412 end_pos = len; 00413 00414 /* Look for the colon : */ 00415 if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos)) 00416 { 00417 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon"); 00418 goto error; 00419 } 00420 00421 if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0)) 00422 { 00423 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00424 goto error; 00425 } 00426 00427 pos = found_pos + 1; 00428 00429 while (pos < end_pos) 00430 { 00431 int comma_pos, equals_pos; 00432 00433 if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos)) 00434 comma_pos = end_pos; 00435 00436 if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) || 00437 equals_pos == pos || equals_pos + 1 == comma_pos) 00438 { 00439 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00440 "'=' character not found or has no value following it"); 00441 goto error; 00442 } 00443 else 00444 { 00445 DBusString *key; 00446 DBusString *value; 00447 00448 key = dbus_new0 (DBusString, 1); 00449 00450 if (!key) 00451 { 00452 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00453 goto error; 00454 } 00455 00456 value = dbus_new0 (DBusString, 1); 00457 if (!value) 00458 { 00459 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00460 dbus_free (key); 00461 goto error; 00462 } 00463 00464 if (!_dbus_string_init (key)) 00465 { 00466 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00467 dbus_free (key); 00468 dbus_free (value); 00469 00470 goto error; 00471 } 00472 00473 if (!_dbus_string_init (value)) 00474 { 00475 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00476 _dbus_string_free (key); 00477 00478 dbus_free (key); 00479 dbus_free (value); 00480 goto error; 00481 } 00482 00483 if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0)) 00484 { 00485 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00486 _dbus_string_free (key); 00487 _dbus_string_free (value); 00488 00489 dbus_free (key); 00490 dbus_free (value); 00491 goto error; 00492 } 00493 00494 if (!append_unescaped_value (value, &str, equals_pos + 1, 00495 comma_pos - equals_pos - 1, error)) 00496 { 00497 _dbus_assert (error == NULL || dbus_error_is_set (error)); 00498 _dbus_string_free (key); 00499 _dbus_string_free (value); 00500 00501 dbus_free (key); 00502 dbus_free (value); 00503 goto error; 00504 } 00505 00506 if (!_dbus_list_append (&entry->keys, key)) 00507 { 00508 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00509 _dbus_string_free (key); 00510 _dbus_string_free (value); 00511 00512 dbus_free (key); 00513 dbus_free (value); 00514 goto error; 00515 } 00516 00517 if (!_dbus_list_append (&entry->values, value)) 00518 { 00519 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00520 _dbus_string_free (value); 00521 00522 dbus_free (value); 00523 goto error; 00524 } 00525 } 00526 00527 pos = comma_pos + 1; 00528 } 00529 00530 pos = end_pos + 1; 00531 } 00532 00533 *array_len = _dbus_list_get_length (&entries); 00534 00535 entry_array = dbus_new (DBusAddressEntry *, *array_len + 1); 00536 00537 if (!entry_array) 00538 { 00539 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00540 00541 goto error; 00542 } 00543 00544 entry_array [*array_len] = NULL; 00545 00546 link = _dbus_list_get_first_link (&entries); 00547 i = 0; 00548 while (link != NULL) 00549 { 00550 entry_array[i] = link->data; 00551 i++; 00552 link = _dbus_list_get_next_link (&entries, link); 00553 } 00554 00555 _dbus_list_clear (&entries); 00556 *entry = entry_array; 00557 00558 return TRUE; 00559 00560 error: 00561 00562 link = _dbus_list_get_first_link (&entries); 00563 while (link != NULL) 00564 { 00565 dbus_address_entry_free (link->data); 00566 link = _dbus_list_get_next_link (&entries, link); 00567 } 00568 00569 _dbus_list_clear (&entries); 00570 00571 return FALSE; 00572 00573 } 00574 00582 char* 00583 dbus_address_escape_value (const char *value) 00584 { 00585 DBusString escaped; 00586 DBusString unescaped; 00587 char *ret; 00588 00589 ret = NULL; 00590 00591 _dbus_string_init_const (&unescaped, value); 00592 00593 if (!_dbus_string_init (&escaped)) 00594 return NULL; 00595 00596 if (!_dbus_address_append_escaped (&escaped, &unescaped)) 00597 goto out; 00598 00599 if (!_dbus_string_steal_data (&escaped, &ret)) 00600 goto out; 00601 00602 out: 00603 _dbus_string_free (&escaped); 00604 return ret; 00605 } 00606 00616 char* 00617 dbus_address_unescape_value (const char *value, 00618 DBusError *error) 00619 { 00620 DBusString unescaped; 00621 DBusString escaped; 00622 char *ret; 00623 00624 ret = NULL; 00625 00626 _dbus_string_init_const (&escaped, value); 00627 00628 if (!_dbus_string_init (&unescaped)) 00629 return NULL; 00630 00631 if (!append_unescaped_value (&unescaped, &escaped, 00632 0, _dbus_string_get_length (&escaped), 00633 error)) 00634 goto out; 00635 00636 if (!_dbus_string_steal_data (&unescaped, &ret)) 00637 goto out; 00638 00639 out: 00640 if (ret == NULL && error && !dbus_error_is_set (error)) 00641 _DBUS_SET_OOM (error); 00642 00643 _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error)); 00644 00645 _dbus_string_free (&unescaped); 00646 return ret; 00647 } 00648 /* End of public API */ 00650 00651 #ifdef DBUS_ENABLE_EMBEDDED_TESTS 00652 00653 #ifndef DOXYGEN_SHOULD_SKIP_THIS 00654 00655 #include "dbus-test.h" 00656 #include <stdlib.h> 00657 00658 typedef struct 00659 { 00660 const char *escaped; 00661 const char *unescaped; 00662 } EscapeTest; 00663 00664 static const EscapeTest escape_tests[] = { 00665 { "abcde", "abcde" }, 00666 { "", "" }, 00667 { "%20%20", " " }, 00668 { "%24", "$" }, 00669 { "%25", "%" }, 00670 { "abc%24", "abc$" }, 00671 { "%24abc", "$abc" }, 00672 { "abc%24abc", "abc$abc" }, 00673 { "/", "/" }, 00674 { "-", "-" }, 00675 { "_", "_" }, 00676 { "A", "A" }, 00677 { "I", "I" }, 00678 { "Z", "Z" }, 00679 { "a", "a" }, 00680 { "i", "i" }, 00681 { "z", "z" }, 00682 /* Bug: https://bugs.freedesktop.org/show_bug.cgi?id=53499 */ 00683 { "%c3%b6", "\xc3\xb6" } 00684 }; 00685 00686 static const char* invalid_escaped_values[] = { 00687 "%a", 00688 "%q", 00689 "%az", 00690 "%%", 00691 "%$$", 00692 "abc%a", 00693 "%axyz", 00694 "%", 00695 "$", 00696 " ", 00697 }; 00698 00699 dbus_bool_t 00700 _dbus_address_test (void) 00701 { 00702 DBusAddressEntry **entries; 00703 int len; 00704 DBusError error = DBUS_ERROR_INIT; 00705 int i; 00706 00707 i = 0; 00708 while (i < _DBUS_N_ELEMENTS (escape_tests)) 00709 { 00710 const EscapeTest *test = &escape_tests[i]; 00711 char *escaped; 00712 char *unescaped; 00713 00714 escaped = dbus_address_escape_value (test->unescaped); 00715 if (escaped == NULL) 00716 _dbus_assert_not_reached ("oom"); 00717 00718 if (strcmp (escaped, test->escaped) != 0) 00719 { 00720 _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n", 00721 test->unescaped, escaped, test->escaped); 00722 exit (1); 00723 } 00724 dbus_free (escaped); 00725 00726 unescaped = dbus_address_unescape_value (test->escaped, &error); 00727 if (unescaped == NULL) 00728 { 00729 _dbus_warn ("Failed to unescape '%s': %s\n", 00730 test->escaped, error.message); 00731 dbus_error_free (&error); 00732 exit (1); 00733 } 00734 00735 if (strcmp (unescaped, test->unescaped) != 0) 00736 { 00737 _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n", 00738 test->escaped, unescaped, test->unescaped); 00739 exit (1); 00740 } 00741 dbus_free (unescaped); 00742 00743 ++i; 00744 } 00745 00746 i = 0; 00747 while (i < _DBUS_N_ELEMENTS (invalid_escaped_values)) 00748 { 00749 char *unescaped; 00750 00751 unescaped = dbus_address_unescape_value (invalid_escaped_values[i], 00752 &error); 00753 if (unescaped != NULL) 00754 { 00755 _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n", 00756 invalid_escaped_values[i], unescaped); 00757 dbus_free (unescaped); 00758 exit (1); 00759 } 00760 00761 _dbus_assert (dbus_error_is_set (&error)); 00762 dbus_error_free (&error); 00763 00764 ++i; 00765 } 00766 00767 if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;", 00768 &entries, &len, &error)) 00769 _dbus_assert_not_reached ("could not parse address"); 00770 _dbus_assert (len == 2); 00771 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0); 00772 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0); 00773 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0); 00774 00775 dbus_address_entries_free (entries); 00776 00777 /* Different possible errors */ 00778 if (dbus_parse_address ("", &entries, &len, &error)) 00779 _dbus_assert_not_reached ("Parsed incorrect address."); 00780 else 00781 dbus_error_free (&error); 00782 00783 if (dbus_parse_address ("foo", &entries, &len, &error)) 00784 _dbus_assert_not_reached ("Parsed incorrect address."); 00785 else 00786 dbus_error_free (&error); 00787 00788 if (dbus_parse_address ("foo:bar", &entries, &len, &error)) 00789 _dbus_assert_not_reached ("Parsed incorrect address."); 00790 else 00791 dbus_error_free (&error); 00792 00793 if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error)) 00794 _dbus_assert_not_reached ("Parsed incorrect address."); 00795 else 00796 dbus_error_free (&error); 00797 00798 if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error)) 00799 _dbus_assert_not_reached ("Parsed incorrect address."); 00800 else 00801 dbus_error_free (&error); 00802 00803 if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error)) 00804 _dbus_assert_not_reached ("Parsed incorrect address."); 00805 else 00806 dbus_error_free (&error); 00807 00808 if (dbus_parse_address ("foo:=foo", &entries, &len, &error)) 00809 _dbus_assert_not_reached ("Parsed incorrect address."); 00810 else 00811 dbus_error_free (&error); 00812 00813 if (dbus_parse_address ("foo:foo=", &entries, &len, &error)) 00814 _dbus_assert_not_reached ("Parsed incorrect address."); 00815 else 00816 dbus_error_free (&error); 00817 00818 if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error)) 00819 _dbus_assert_not_reached ("Parsed incorrect address."); 00820 else 00821 dbus_error_free (&error); 00822 00823 return TRUE; 00824 } 00825 00826 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */ 00827 00828 #endif