* dbus/dbus-address.c (dbus_parse_address): Do not accept zero-length address.
[platform/upstream/dbus.git] / dbus / dbus-address.c
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   if (len == 0)
381   {
382     dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
383                     "Empty address '%s'", address);
384     goto error;
385   }
386   
387   while (pos < len)
388     {
389       DBusAddressEntry *entry;
390
391       int found_pos;
392
393       entry = create_entry ();
394       if (!entry)
395         {
396           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
397
398           goto error;
399         }
400       
401       /* Append the entry */
402       if (!_dbus_list_append (&entries, entry))
403         {
404           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
405           dbus_address_entry_free (entry);
406           goto error;
407         }
408       
409       /* Look for a semi-colon */
410       if (!_dbus_string_find (&str, pos, ";", &end_pos))
411         end_pos = len;
412       
413       /* Look for the colon : */
414       if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
415         {
416           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
417           goto error;
418         }
419
420       if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
421         {
422           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
423           goto error;
424         }
425           
426       pos = found_pos + 1;
427
428       while (pos < end_pos)
429         {
430           int comma_pos, equals_pos;
431
432           if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
433             comma_pos = end_pos;
434           
435           if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
436               equals_pos == pos || equals_pos + 1 == comma_pos)
437             {
438               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
439                               "'=' character not found or has no value following it");
440               goto error;
441             }
442           else
443             {
444               DBusString *key;
445               DBusString *value;
446
447               key = dbus_new0 (DBusString, 1);
448
449               if (!key)
450                 {
451                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
452                   goto error;
453                 }
454
455               value = dbus_new0 (DBusString, 1);
456               if (!value)
457                 {
458                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
459                   dbus_free (key);
460                   goto error;
461                 }
462               
463               if (!_dbus_string_init (key))
464                 {
465                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
466                   dbus_free (key);
467                   dbus_free (value);
468                   
469                   goto error;
470                 }
471               
472               if (!_dbus_string_init (value))
473                 {
474                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
475                   _dbus_string_free (key);
476
477                   dbus_free (key);
478                   dbus_free (value);              
479                   goto error;
480                 }
481
482               if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
483                 {
484                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
485                   _dbus_string_free (key);
486                   _dbus_string_free (value);
487
488                   dbus_free (key);
489                   dbus_free (value);              
490                   goto error;
491                 }
492
493               if (!append_unescaped_value (value, &str, equals_pos + 1,
494                                            comma_pos - equals_pos - 1, error))
495                 {
496                   _dbus_assert (error == NULL || dbus_error_is_set (error));
497                   _dbus_string_free (key);
498                   _dbus_string_free (value);
499
500                   dbus_free (key);
501                   dbus_free (value);              
502                   goto error;
503                 }
504
505               if (!_dbus_list_append (&entry->keys, key))
506                 {
507                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
508                   _dbus_string_free (key);
509                   _dbus_string_free (value);
510
511                   dbus_free (key);
512                   dbus_free (value);              
513                   goto error;
514                 }
515
516               if (!_dbus_list_append (&entry->values, value))
517                 {
518                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
519                   _dbus_string_free (value);
520
521                   dbus_free (value);
522                   goto error;             
523                 }
524             }
525
526           pos = comma_pos + 1;
527         }
528
529       pos = end_pos + 1;
530     }
531
532   *array_len = _dbus_list_get_length (&entries);
533   
534   entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
535
536   if (!entry_array)
537     {
538       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
539       
540       goto error;
541     }
542   
543   entry_array [*array_len] = NULL;
544
545   link = _dbus_list_get_first_link (&entries);
546   i = 0;
547   while (link != NULL)
548     {
549       entry_array[i] = link->data;
550       i++;
551       link = _dbus_list_get_next_link (&entries, link);
552     }
553
554   _dbus_list_clear (&entries);
555   *entry = entry_array;
556
557   return TRUE;
558
559  error:
560   
561   link = _dbus_list_get_first_link (&entries);
562   while (link != NULL)
563     {
564       dbus_address_entry_free (link->data);
565       link = _dbus_list_get_next_link (&entries, link);
566     }
567
568   _dbus_list_clear (&entries);
569   
570   return FALSE;
571   
572 }
573
574 /**
575  * Escapes the given string as a value in a key=value pair
576  * for a D-Bus address.
577  *
578  * @param value the unescaped value
579  * @returns newly-allocated escaped value or #NULL if no memory
580  */
581 char*
582 dbus_address_escape_value (const char *value)
583 {
584   DBusString escaped;
585   DBusString unescaped;
586   char *ret;
587
588   ret = NULL;
589
590   _dbus_string_init_const (&unescaped, value);
591   
592   if (!_dbus_string_init (&escaped))
593     return NULL;
594
595   if (!_dbus_address_append_escaped (&escaped, &unescaped))
596     goto out;
597   
598   if (!_dbus_string_steal_data (&escaped, &ret))
599     goto out;
600
601  out:
602   _dbus_string_free (&escaped);
603   return ret;
604 }
605
606 /**
607  * Unescapes the given string as a value in a key=value pair
608  * for a D-Bus address. Note that dbus_address_entry_get_value()
609  * returns an already-unescaped value.
610  *
611  * @param value the escaped value
612  * @param error error to set if the unescaping fails
613  * @returns newly-allocated unescaped value or #NULL if no memory
614  */
615 char*
616 dbus_address_unescape_value (const char *value,
617                              DBusError  *error)
618 {
619   DBusString unescaped;
620   DBusString escaped;
621   char *ret;
622   
623   ret = NULL;
624
625   _dbus_string_init_const (&escaped, value);
626   
627   if (!_dbus_string_init (&unescaped))
628     return NULL;
629
630   if (!append_unescaped_value (&unescaped, &escaped,
631                                0, _dbus_string_get_length (&escaped),
632                                error))
633     goto out;
634   
635   if (!_dbus_string_steal_data (&unescaped, &ret))
636     goto out;
637
638  out:
639   if (ret == NULL && error && !dbus_error_is_set (error))
640     _DBUS_SET_OOM (error);
641
642   _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
643   
644   _dbus_string_free (&unescaped);
645   return ret;
646 }
647
648 /** @} */ /* End of public API */
649
650 #ifdef DBUS_BUILD_TESTS
651
652 #ifndef DOXYGEN_SHOULD_SKIP_THIS
653
654 #include "dbus-test.h"
655 #include <stdlib.h>
656
657 typedef struct
658 {
659   const char *escaped;
660   const char *unescaped;
661 } EscapeTest;
662
663 static const EscapeTest escape_tests[] = {
664   { "abcde", "abcde" },
665   { "", "" },
666   { "%20%20", "  " },
667   { "%24", "$" },
668   { "%25", "%" },
669   { "abc%24", "abc$" },
670   { "%24abc", "$abc" },
671   { "abc%24abc", "abc$abc" },
672   { "/", "/" },
673   { "-", "-" },
674   { "_", "_" },
675   { "A", "A" },
676   { "I", "I" },
677   { "Z", "Z" },
678   { "a", "a" },
679   { "i", "i" },
680   { "z", "z" }
681 };
682
683 static const char* invalid_escaped_values[] = {
684   "%a",
685   "%q",
686   "%az",
687   "%%",
688   "%$$",
689   "abc%a",
690   "%axyz",
691   "%",
692   "$",
693   " ",
694   "*"
695 };
696
697 dbus_bool_t
698 _dbus_address_test (void)
699 {
700   DBusAddressEntry **entries;
701   int len;  
702   DBusError error;
703   int i;
704
705   dbus_error_init (&error);
706
707   i = 0;
708   while (i < _DBUS_N_ELEMENTS (escape_tests))
709     {
710       const EscapeTest *test = &escape_tests[i];
711       char *escaped;
712       char *unescaped;
713
714       escaped = dbus_address_escape_value (test->unescaped);
715       if (escaped == NULL)
716         _dbus_assert_not_reached ("oom");
717
718       if (strcmp (escaped, test->escaped) != 0)
719         {
720           _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
721                       test->unescaped, escaped, test->escaped);
722           exit (1);
723         }
724       dbus_free (escaped);
725
726       unescaped = dbus_address_unescape_value (test->escaped, &error);
727       if (unescaped == NULL)
728         {
729           _dbus_warn ("Failed to unescape '%s': %s\n",
730                       test->escaped, error.message);
731           dbus_error_free (&error);
732           exit (1);
733         }
734
735       if (strcmp (unescaped, test->unescaped) != 0)
736         {
737           _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
738                       test->escaped, unescaped, test->unescaped);
739           exit (1);
740         }
741       dbus_free (unescaped);
742       
743       ++i;
744     }
745
746   i = 0;
747   while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
748     {
749       char *unescaped;
750
751       unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
752                                                &error);
753       if (unescaped != NULL)
754         {
755           _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
756                       invalid_escaped_values[i], unescaped);
757           dbus_free (unescaped);
758           exit (1);
759         }
760
761       _dbus_assert (dbus_error_is_set (&error));
762       dbus_error_free (&error);
763
764       ++i;
765     }
766   
767   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
768                            &entries, &len, &error))
769     _dbus_assert_not_reached ("could not parse address");
770   _dbus_assert (len == 2);
771   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
772   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
773   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
774   
775   dbus_address_entries_free (entries);
776
777   /* Different possible errors */
778   if (dbus_parse_address ("", &entries, &len, &error))
779     _dbus_assert_not_reached ("Parsed incorrect address.");
780   else
781     dbus_error_free (&error);
782
783   if (dbus_parse_address ("foo", &entries, &len, &error))
784     _dbus_assert_not_reached ("Parsed incorrect address.");
785   else
786     dbus_error_free (&error);
787   
788   if (dbus_parse_address ("foo:bar", &entries, &len, &error))
789     _dbus_assert_not_reached ("Parsed incorrect address.");
790   else
791     dbus_error_free (&error);
792   
793   if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error))
794     _dbus_assert_not_reached ("Parsed incorrect address.");
795   else
796     dbus_error_free (&error);
797   
798   if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error))
799     _dbus_assert_not_reached ("Parsed incorrect address.");
800   else
801     dbus_error_free (&error);
802   
803   if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
804     _dbus_assert_not_reached ("Parsed incorrect address.");
805   else
806     dbus_error_free (&error);
807   
808   if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
809     _dbus_assert_not_reached ("Parsed incorrect address.");
810   else
811     dbus_error_free (&error);
812   
813   if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
814     _dbus_assert_not_reached ("Parsed incorrect address.");
815   else
816     dbus_error_free (&error);
817
818   if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error))
819     _dbus_assert_not_reached ("Parsed incorrect address.");
820   else
821     dbus_error_free (&error);
822
823   return TRUE;
824 }
825
826 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
827
828 #endif