/src/tor/src/lib/encoding/cstring.c
Line | Count | Source |
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 cstring.c |
9 | | * |
10 | | * \brief Decode data that has been written as a C literal. |
11 | | **/ |
12 | | |
13 | | #include "lib/encoding/cstring.h" |
14 | | #include "lib/log/log.h" |
15 | | #include "lib/log/util_bug.h" |
16 | | #include "lib/malloc/malloc.h" |
17 | | #include "lib/string/compat_ctype.h" |
18 | | |
19 | | #include <string.h> |
20 | | |
21 | 52.4M | #define TOR_ISODIGIT(c) ('0' <= (c) && (c) <= '7') |
22 | | |
23 | | /** Given a c-style double-quoted escaped string in <b>s</b>, extract and |
24 | | * decode its contents into a newly allocated string. On success, assign this |
25 | | * string to *<b>result</b>, assign its length to <b>size_out</b> (if |
26 | | * provided), and return a pointer to the position in <b>s</b> immediately |
27 | | * after the string. On failure, return NULL. |
28 | | */ |
29 | | const char * |
30 | | unescape_string(const char *s, char **result, size_t *size_out) |
31 | 5.24M | { |
32 | 5.24M | const char *cp; |
33 | 5.24M | char *out; |
34 | 5.24M | if (s[0] != '\"') |
35 | 1 | return NULL; |
36 | 5.24M | cp = s+1; |
37 | 19.7M | while (1) { |
38 | 19.7M | switch (*cp) { |
39 | 33 | case '\0': |
40 | 34 | case '\n': |
41 | 34 | return NULL; |
42 | 5.24M | case '\"': |
43 | 5.24M | goto end_of_loop; |
44 | 10.4M | case '\\': |
45 | 10.4M | if (cp[1] == 'x' || cp[1] == 'X') { |
46 | 7.54k | if (!(TOR_ISXDIGIT(cp[2]) && TOR_ISXDIGIT(cp[3]))) |
47 | 17 | return NULL; |
48 | 7.52k | cp += 4; |
49 | 10.4M | } else if (TOR_ISODIGIT(cp[1])) { |
50 | 10.4M | cp += 2; |
51 | 10.4M | if (TOR_ISODIGIT(*cp)) ++cp; |
52 | 10.4M | if (TOR_ISODIGIT(*cp)) ++cp; |
53 | 10.4M | } else if (cp[1] == 'n' || cp[1] == 'r' || cp[1] == 't' || cp[1] == '"' |
54 | 1.57k | || cp[1] == '\\' || cp[1] == '\'') { |
55 | 1.57k | cp += 2; |
56 | 1.57k | } else { |
57 | 41 | return NULL; |
58 | 41 | } |
59 | 10.4M | break; |
60 | 10.4M | default: |
61 | 3.96M | ++cp; |
62 | 3.96M | break; |
63 | 19.7M | } |
64 | 19.7M | } |
65 | 5.24M | end_of_loop: |
66 | 5.24M | out = *result = tor_malloc(cp-s + 1); |
67 | 5.24M | cp = s+1; |
68 | 18.6M | while (1) { |
69 | 18.6M | switch (*cp) |
70 | 18.6M | { |
71 | 5.24M | case '\"': |
72 | 5.24M | *out = '\0'; |
73 | 5.24M | if (size_out) *size_out = out - *result; |
74 | 5.24M | return cp+1; |
75 | | |
76 | | /* LCOV_EXCL_START -- we caught this in parse_config_from_line. */ |
77 | 0 | case '\0': |
78 | 0 | tor_fragile_assert(); |
79 | 0 | tor_free(*result); |
80 | 0 | return NULL; |
81 | | /* LCOV_EXCL_STOP */ |
82 | 10.4M | case '\\': |
83 | 10.4M | switch (cp[1]) |
84 | 10.4M | { |
85 | 194 | case 'n': *out++ = '\n'; cp += 2; break; |
86 | 261 | case 'r': *out++ = '\r'; cp += 2; break; |
87 | 258 | case 't': *out++ = '\t'; cp += 2; break; |
88 | 6.96k | case 'x': case 'X': |
89 | 6.96k | { |
90 | 6.96k | int x1, x2; |
91 | | |
92 | 6.96k | x1 = hex_decode_digit(cp[2]); |
93 | 6.96k | x2 = hex_decode_digit(cp[3]); |
94 | 6.96k | if (x1 == -1 || x2 == -1) { |
95 | | /* LCOV_EXCL_START */ |
96 | | /* we caught this above in the initial loop. */ |
97 | 0 | tor_assert_nonfatal_unreached(); |
98 | 0 | tor_free(*result); |
99 | 0 | return NULL; |
100 | | /* LCOV_EXCL_STOP */ |
101 | 0 | } |
102 | | |
103 | 6.96k | *out++ = ((x1<<4) + x2); |
104 | 6.96k | cp += 4; |
105 | 6.96k | } |
106 | 0 | break; |
107 | 10.4M | case '0': case '1': case '2': case '3': case '4': case '5': |
108 | 10.4M | case '6': case '7': |
109 | 10.4M | { |
110 | 10.4M | int n = cp[1]-'0'; |
111 | 10.4M | cp += 2; |
112 | 10.4M | if (TOR_ISODIGIT(*cp)) { n = n*8 + *cp-'0'; cp++; } |
113 | 10.4M | if (TOR_ISODIGIT(*cp)) { n = n*8 + *cp-'0'; cp++; } |
114 | 10.4M | if (n > 255) { tor_free(*result); return NULL; } |
115 | 10.4M | *out++ = (char)n; |
116 | 10.4M | } |
117 | 0 | break; |
118 | 323 | case '\'': |
119 | 588 | case '\"': |
120 | 860 | case '\\': |
121 | 860 | case '\?': |
122 | 860 | *out++ = cp[1]; |
123 | 860 | cp += 2; |
124 | 860 | break; |
125 | | |
126 | | /* LCOV_EXCL_START */ |
127 | 0 | default: |
128 | | /* we caught this above in the initial loop. */ |
129 | 0 | tor_assert_nonfatal_unreached(); |
130 | 0 | tor_free(*result); return NULL; |
131 | | /* LCOV_EXCL_STOP */ |
132 | 10.4M | } |
133 | 10.4M | break; |
134 | 10.4M | default: |
135 | 2.91M | *out++ = *cp++; |
136 | 18.6M | } |
137 | 18.6M | } |
138 | 5.24M | } |