BadVPN – Blame information for rev 1
?pathlinks?
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 | } |