LCOV - code coverage report
Current view: top level - lib/encoding - kvline.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 98 100 98.0 %
Date: 2021-11-24 03:28:48 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2001 Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * \file kvline.c
       9             :  *
      10             :  * \brief Manipulating lines of key-value pairs.
      11             :  **/
      12             : 
      13             : #include "orconfig.h"
      14             : 
      15             : #include "lib/container/smartlist.h"
      16             : #include "lib/encoding/confline.h"
      17             : #include "lib/encoding/cstring.h"
      18             : #include "lib/encoding/kvline.h"
      19             : #include "lib/encoding/qstring.h"
      20             : #include "lib/malloc/malloc.h"
      21             : #include "lib/string/compat_ctype.h"
      22             : #include "lib/string/printf.h"
      23             : #include "lib/string/util_string.h"
      24             : #include "lib/log/escape.h"
      25             : #include "lib/log/util_bug.h"
      26             : 
      27             : #include <stdbool.h>
      28             : #include <stddef.h>
      29             : #include <string.h>
      30             : 
      31             : /** Return true iff we need to quote and escape the string <b>s</b> to encode
      32             :  * it.
      33             :  *
      34             :  * kvline_can_encode_lines() also uses this (with
      35             :  * <b>as_keyless_val</b> true) to check whether a key would require
      36             :  * quoting.
      37             :  */
      38             : static bool
      39         325 : needs_escape(const char *s, bool as_keyless_val)
      40             : {
      41         325 :   if (as_keyless_val && *s == 0)
      42             :     return true;
      43             :   /* Keyless values containing '=' need to be escaped. */
      44         325 :   if (as_keyless_val && strchr(s, '='))
      45             :     return true;
      46             : 
      47        1640 :   for (; *s; ++s) {
      48        1368 :     if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) ||
      49        1323 :         *s == '\'' || *s == '\"') {
      50             :       return true;
      51             :     }
      52             :   }
      53             :   return false;
      54             : }
      55             : 
      56             : /**
      57             :  * Return true iff the key in <b>line</b> is not set.
      58             :  **/
      59             : static bool
      60         205 : line_has_no_key(const config_line_t *line)
      61             : {
      62         205 :   return line->key == NULL || strlen(line->key) == 0;
      63             : }
      64             : 
      65             : /**
      66             :  * Return true iff the value in <b>line</b> is not set.
      67             :  **/
      68             : static bool
      69          11 : line_has_no_val(const config_line_t *line)
      70             : {
      71          11 :   return line->value == NULL || strlen(line->value) == 0;
      72             : }
      73             : 
      74             : /**
      75             :  * Return true iff the all the lines in <b>line</b> can be encoded
      76             :  * using <b>flags</b>.
      77             :  **/
      78             : static bool
      79          71 : kvline_can_encode_lines(const config_line_t *line, unsigned flags)
      80             : {
      81         204 :   for ( ; line; line = line->next) {
      82         139 :     const bool keyless = line_has_no_key(line);
      83         139 :     if (keyless && ! (flags & KV_OMIT_KEYS)) {
      84             :       /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */
      85             :       return false;
      86             :     }
      87             : 
      88         138 :     if (needs_escape(line->value, keyless) && ! (flags & (KV_QUOTED|KV_RAW))) {
      89             :       /* If both KV_QUOTED and KV_RAW are false, we can't encode a
      90             :          value that needs quotes. */
      91             :       return false;
      92             :     }
      93         135 :     if (!keyless && needs_escape(line->key, true)) {
      94             :       /* We can't handle keys that need quoting. */
      95             :       return false;
      96             :     }
      97             :   }
      98             :   return true;
      99             : }
     100             : 
     101             : /**
     102             :  * Encode a linked list of lines in <b>line</b> as a series of 'Key=Value'
     103             :  * pairs, using the provided <b>flags</b> to encode it.  Return a newly
     104             :  * allocated string on success, or NULL on failure.
     105             :  *
     106             :  * If KV_QUOTED is set in <b>flags</b>, then all values that contain
     107             :  * spaces or unusual characters are escaped and quoted.  Otherwise, such
     108             :  * values are not allowed.  Mutually exclusive with KV_RAW.
     109             :  *
     110             :  * If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are
     111             :  * allowed, and are encoded as 'Value'.  Otherwise, such pairs are not
     112             :  * allowed.
     113             :  *
     114             :  * If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is
     115             :  * encoded as 'Key', not as 'Key=' or 'Key=""'.  Mutually exclusive with
     116             :  * KV_OMIT_KEYS.
     117             :  *
     118             :  * If KV_RAW is set in <b>flags</b>, then don't apply any quoting to
     119             :  * the value, and assume that the caller has adequately quoted it.
     120             :  * (The control protocol has some quirks that make this necessary.)
     121             :  * Mutually exclusive with KV_QUOTED.
     122             :  *
     123             :  * KV_QUOTED_QSTRING is not supported.
     124             :  */
     125             : char *
     126          37 : kvline_encode(const config_line_t *line,
     127             :               unsigned flags)
     128             : {
     129          37 :   tor_assert(! (flags & KV_QUOTED_QSTRING));
     130             : 
     131          37 :   tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
     132             :              (KV_OMIT_KEYS|KV_OMIT_VALS));
     133          37 :   tor_assert((flags & (KV_QUOTED|KV_RAW)) != (KV_QUOTED|KV_RAW));
     134             : 
     135          37 :   if (!kvline_can_encode_lines(line, flags))
     136             :     return NULL;
     137             : 
     138          32 :   smartlist_t *elements = smartlist_new();
     139             : 
     140         130 :   for (; line; line = line->next) {
     141             : 
     142          66 :     const char *k = "";
     143          66 :     const char *eq = "=";
     144          66 :     const char *v = "";
     145          66 :     const bool keyless = line_has_no_key(line);
     146          66 :     bool esc = needs_escape(line->value, keyless);
     147          66 :     char *tmp = NULL;
     148             : 
     149          66 :     if (! keyless) {
     150          55 :       k = line->key;
     151             :     } else {
     152             :       eq = "";
     153             :     }
     154             : 
     155          66 :     if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
     156             :       eq = "";
     157             :       v = "";
     158          62 :     } else if (!(flags & KV_RAW) && esc) {
     159          11 :       tmp = esc_for_log(line->value);
     160          11 :       v = tmp;
     161             :     } else {
     162             :       v = line->value;
     163             :     }
     164             : 
     165          66 :     smartlist_add_asprintf(elements, "%s%s%s", k, eq, v);
     166          66 :     tor_free(tmp);
     167             :   }
     168             : 
     169          32 :   char *result = smartlist_join_strings(elements, " ", 0, NULL);
     170             : 
     171          98 :   SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
     172          32 :   smartlist_free(elements);
     173             : 
     174          32 :   return result;
     175             : }
     176             : 
     177             : /**
     178             :  * Decode a <b>line</b> containing a series of space-separated 'Key=Value'
     179             :  * pairs, using the provided <b>flags</b> to decode it.  Return a newly
     180             :  * allocated list of pairs on success, or NULL on failure.
     181             :  *
     182             :  * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are
     183             :  * allowed and handled as C strings. Otherwise, such values are not allowed.
     184             :  *
     185             :  * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are
     186             :  * allowed.  Otherwise, such values are not allowed.
     187             :  *
     188             :  * If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are
     189             :  * allowed.  Otherwise, such keys are not allowed.  Mutually exclusive with
     190             :  * KV_OMIT_KEYS.
     191             :  *
     192             :  * If KV_QUOTED_QSTRING is set in <b>flags</b>, then double-quoted values
     193             :  * are allowed and handled as QuotedStrings per qstring.c.  Do not add
     194             :  * new users of this flag.
     195             :  *
     196             :  * KV_RAW is not supported.
     197             :  */
     198             : config_line_t *
     199          37 : kvline_parse(const char *line, unsigned flags)
     200             : {
     201          37 :   tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
     202             :              (KV_OMIT_KEYS|KV_OMIT_VALS));
     203          37 :   tor_assert(!(flags & KV_RAW));
     204             : 
     205          37 :   const char *cp = line, *cplast = NULL;
     206          37 :   const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
     207          37 :   const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
     208          37 :   const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0;
     209          37 :   const bool c_quoted = (flags & (KV_QUOTED)) != 0;
     210             : 
     211          37 :   config_line_t *result = NULL;
     212          37 :   config_line_t **next_line = &result;
     213             : 
     214          37 :   char *key = NULL;
     215          37 :   char *val = NULL;
     216             : 
     217         106 :   while (*cp) {
     218          75 :     key = val = NULL;
     219             :     /* skip all spaces */
     220             :     {
     221          75 :       size_t idx = strspn(cp, " \t\r\v\n");
     222          75 :       cp += idx;
     223             :     }
     224          75 :     if (BUG(cp == cplast)) {
     225             :       /* If we didn't parse anything since the last loop, this code is
     226             :        * broken. */
     227             :       goto err; // LCOV_EXCL_LINE
     228             :     }
     229          75 :     cplast = cp;
     230          75 :     if (! *cp)
     231             :       break; /* End of string; we're done. */
     232             : 
     233             :     /* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */
     234             : 
     235             :     /* Find where the key ends */
     236          72 :     if (*cp != '\"') {
     237          69 :       size_t idx = strcspn(cp, " \t\r\v\n=");
     238             : 
     239          69 :       if (cp[idx] == '=') {
     240          59 :         key = tor_memdup_nulterm(cp, idx);
     241          59 :         cp += idx + 1;
     242          10 :       } else if (omit_vals) {
     243           8 :         key = tor_memdup_nulterm(cp, idx);
     244           8 :         cp += idx;
     245           8 :         goto commit;
     246             :       } else {
     247           2 :         if (!omit_keys)
     248           1 :           goto err;
     249             :       }
     250             :     }
     251             : 
     252          63 :     if (*cp == '\"') {
     253             :       /* The type is "V". */
     254          17 :       if (!quoted)
     255           1 :         goto err;
     256          16 :       size_t len=0;
     257          16 :       if (c_quoted) {
     258          16 :         cp = unescape_string(cp, &val, &len);
     259             :       } else {
     260           0 :         cp = decode_qstring(cp, strlen(cp), &val, &len);
     261             :       }
     262          16 :       if (cp == NULL || len != strlen(val)) {
     263             :         // The string contains a NUL or is badly coded.
     264           0 :         goto err;
     265             :       }
     266             :     } else {
     267          46 :       size_t idx = strcspn(cp, " \t\r\v\n");
     268          46 :       val = tor_memdup_nulterm(cp, idx);
     269          46 :       cp += idx;
     270             :     }
     271             : 
     272          70 :   commit:
     273          70 :     if (key && strlen(key) == 0) {
     274             :       /* We don't allow empty keys. */
     275           1 :       goto err;
     276             :     }
     277             : 
     278          69 :     *next_line = tor_malloc_zero(sizeof(config_line_t));
     279          69 :     (*next_line)->key = key ? key : tor_strdup("");
     280          69 :     (*next_line)->value = val ? val : tor_strdup("");
     281          69 :     next_line = &(*next_line)->next;
     282          69 :     key = val = NULL;
     283             :   }
     284             : 
     285          34 :   if (! (flags & KV_QUOTED_QSTRING)) {
     286          34 :     if (!kvline_can_encode_lines(result, flags)) {
     287           1 :       goto err;
     288             :     }
     289             :   }
     290          33 :   return result;
     291             : 
     292           4 :  err:
     293           4 :   tor_free(key);
     294           4 :   tor_free(val);
     295           4 :   config_free_lines(result);
     296           4 :   return NULL;
     297             : }

Generated by: LCOV version 1.14