BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file NCDBuildProgram.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <stddef.h>
31 #include <stdlib.h>
32 #include <string.h>
33  
34 #include <misc/debug.h>
35 #include <misc/read_file.h>
36 #include <misc/strdup.h>
37 #include <misc/concat_strings.h>
38 #include <base/BLog.h>
39 #include <ncd/NCDConfigParser.h>
40  
41 #include "NCDBuildProgram.h"
42  
43 #include <generated/blog_channel_NCDBuildProgram.h>
44  
45 #define MAX_INCLUDE_DEPTH 32
46  
47 struct guard {
48 char *id_data;
49 size_t id_length;
50 struct guard *next;
51 };
52  
53 struct build_state {
54 struct guard *top_guard;
55 };
56  
57 static int add_guard (struct guard **first, const char *id_data, size_t id_length)
58 {
59 struct guard *g = malloc(sizeof(*g));
60 if (!g) {
61 goto fail0;
62 }
63  
64 if (!(g->id_data = b_strdup_bin(id_data, id_length))) {
65 goto fail1;
66 }
67  
68 g->id_length = id_length;
69  
70 g->next = *first;
71 *first = g;
72  
73 return 1;
74  
75 fail1:
76 free(g);
77 fail0:
78 return 0;
79 }
80  
81 static void prepend_guards (struct guard **first, struct guard *guards)
82 {
83 if (!guards) {
84 return;
85 }
86  
87 struct guard *last = guards;
88 while (last->next) {
89 last = last->next;
90 }
91  
92 last->next = *first;
93 *first = guards;
94 }
95  
96 static void free_guards (struct guard *g)
97 {
98 while (g) {
99 struct guard *next_g = g->next;
100 free(g->id_data);
101 free(g);
102 g = next_g;
103 }
104 }
105  
106 static int guard_exists (struct guard *top_guard, const char *id_data, size_t id_length)
107 {
108 for (struct guard *g = top_guard; g; g = g->next) {
109 if (g->id_length == id_length && !memcmp(g->id_data, id_data, id_length)) {
110 return 1;
111 }
112 }
113  
114 return 0;
115 }
116  
117 static char * make_dir_path (const char *file_path)
118 {
119 int found_slash = 0;
120 size_t last_slash = 0; // initialize to remove warning
121  
122 for (size_t i = 0; file_path[i]; i++) {
123 if (file_path[i] == '/') {
124 found_slash = 1;
125 last_slash = i;
126 }
127 }
128  
129 char *dir_path;
130  
131 if (!found_slash) {
132 if (!file_path[0]) {
133 BLog(BLOG_ERROR, "file '%s': file path must not be empty", file_path);
134 return NULL;
135 }
136 dir_path = b_strdup("");
137 } else {
138 if (!file_path[last_slash + 1]) {
139 BLog(BLOG_ERROR, "file '%s': file path must not end in a slash", file_path);
140 return NULL;
141 }
142 dir_path = b_strdup_bin(file_path, last_slash + 1);
143 }
144  
145 if (!dir_path) {
146 BLog(BLOG_ERROR, "file '%s': b_strdup/b_strdup_bin failed", file_path);
147 return NULL;
148 }
149  
150 return dir_path;
151 }
152  
153 static char * make_include_path (const char *file_path, const char *dir_path, const char *target, size_t target_len)
154 {
155 ASSERT(target_len == strlen(target))
156  
157 if (target_len == 0) {
158 BLog(BLOG_ERROR, "file '%s': include target must not be empty", file_path);
159 return NULL;
160 }
161  
162 if (target[target_len - 1] == '/') {
163 BLog(BLOG_ERROR, "file '%s': include target must not end in a slash", file_path);
164 return NULL;
165 }
166  
167 char *real_target;
168  
169 if (target[0] == '/') {
170 real_target = b_strdup(target);
171 } else {
172 real_target = concat_strings(2, dir_path, target);
173 }
174  
175 if (!real_target) {
176 BLog(BLOG_ERROR, "file '%s': b_strdup/concat_strings failed", file_path);
177 return NULL;
178 }
179  
180 return real_target;
181 }
182  
183 static int process_file (struct build_state *st, int depth, const char *file_path, NCDProgram *out_program, int *out_guarded)
184 {
185 int ret_val = 0;
186 int res;
187  
188 if (depth > MAX_INCLUDE_DEPTH) {
189 BLog(BLOG_ERROR, "file '%s': maximum include depth (%d) exceeded (include cycle?)", file_path, (int)MAX_INCLUDE_DEPTH);
190 goto fail0;
191 }
192  
193 char *dir_path = make_dir_path(file_path);
194 if (!dir_path) {
195 goto fail0;
196 }
197  
198 uint8_t *data;
199 size_t len;
200 if (!read_file(file_path, &data, &len)) {
201 BLog(BLOG_ERROR, "file '%s': failed to read contents", file_path);
202 goto fail1;
203 }
204  
205 NCDProgram program;
206 res = NCDConfigParser_Parse((char *)data, len, &program);
207 free(data);
208 if (!res) {
209 BLog(BLOG_ERROR, "file '%s': failed to parse", file_path);
210 goto fail1;
211 }
212  
213 struct guard *our_guards = NULL;
214  
215 NCDProgramElem *elem = NCDProgram_FirstElem(&program);
216 while (elem) {
217 NCDProgramElem *next_elem = NCDProgram_NextElem(&program, elem);
218 if (NCDProgramElem_Type(elem) != NCDPROGRAMELEM_INCLUDE_GUARD) {
219 elem = next_elem;
220 continue;
221 }
222  
223 const char *id_data = NCDProgramElem_IncludeGuardIdData(elem);
224 size_t id_length = NCDProgramElem_IncludeGuardIdLength(elem);
225  
226 if (guard_exists(st->top_guard, id_data, id_length)) {
227 *out_guarded = 1;
228 ret_val = 1;
229 goto fail2;
230 }
231  
232 if (!add_guard(&our_guards, id_data, id_length)) {
233 BLog(BLOG_ERROR, "file '%s': add_guard failed", file_path);
234 goto fail2;
235 }
236  
237 NCDProgram_RemoveElem(&program, elem);
238 elem = next_elem;
239 }
240  
241 prepend_guards(&st->top_guard, our_guards);
242 our_guards = NULL;
243  
244 elem = NCDProgram_FirstElem(&program);
245 while (elem) {
246 NCDProgramElem *next_elem = NCDProgram_NextElem(&program, elem);
247 if (NCDProgramElem_Type(elem) != NCDPROGRAMELEM_INCLUDE) {
248 elem = next_elem;
249 continue;
250 }
251  
252 const char *target = NCDProgramElem_IncludePathData(elem);
253 size_t target_len = NCDProgramElem_IncludePathLength(elem);
254  
255 if (strlen(target) != target_len) {
256 BLog(BLOG_ERROR, "file '%s': include path must not contain null characters", file_path);
257 goto fail2;
258 }
259  
260 char *real_target = make_include_path(file_path, dir_path, target, target_len);
261 if (!real_target) {
262 goto fail2;
263 }
264  
265 NCDProgram included_program;
266 int included_guarded;
267 res = process_file(st, depth + 1, real_target, &included_program, &included_guarded);
268 free(real_target);
269 if (!res) {
270 goto fail2;
271 }
272  
273 if (included_guarded) {
274 NCDProgram_RemoveElem(&program, elem);
275 } else {
276 if (!NCDProgram_ReplaceElemWithProgram(&program, elem, included_program)) {
277 BLog(BLOG_ERROR, "file '%s': NCDProgram_ReplaceElemWithProgram failed", file_path);
278 NCDProgram_Free(&included_program);
279 goto fail2;
280 }
281 }
282  
283 elem = next_elem;
284 }
285  
286 free(dir_path);
287  
288 *out_program = program;
289 *out_guarded = 0;
290 return 1;
291  
292 fail2:
293 free_guards(our_guards);
294 NCDProgram_Free(&program);
295 fail1:
296 free(dir_path);
297 fail0:
298 return ret_val;
299 }
300  
301 int NCDBuildProgram_Build (const char *file_path, NCDProgram *out_program)
302 {
303 ASSERT(file_path)
304 ASSERT(out_program)
305  
306 struct build_state st;
307 st.top_guard = NULL;
308  
309 int guarded;
310 int res = process_file(&st, 0, file_path, out_program, &guarded);
311  
312 ASSERT(!res || !guarded)
313  
314 free_guards(st.top_guard);
315  
316 return res;
317 }