File: | lwan-mod-rewrite.c |
Warning: | line 398, column 1 Potential leak of memory pointed to by 'cookie_key' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | ||||
2 | * lwan - simple web server | ||||
3 | * Copyright (c) 2015 Leandro A. F. Pereira <leandro@hardinfo.org> | ||||
4 | * | ||||
5 | * This program is free software; you can redistribute it and/or | ||||
6 | * modify it under the terms of the GNU General Public License | ||||
7 | * as published by the Free Software Foundation; either version 2 | ||||
8 | * of the License, or any later version. | ||||
9 | * | ||||
10 | * This program is distributed in the hope that it will be useful, | ||||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
13 | * GNU General Public License for more details. | ||||
14 | * | ||||
15 | * You should have received a copy of the GNU General Public License | ||||
16 | * along with this program; if not, write to the Free Software | ||||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, | ||||
18 | * USA. | ||||
19 | */ | ||||
20 | |||||
21 | #define _GNU_SOURCE | ||||
22 | #include <ctype.h> | ||||
23 | #include <limits.h> | ||||
24 | #include <stdlib.h> | ||||
25 | #include <string.h> | ||||
26 | |||||
27 | #include "lwan-private.h" | ||||
28 | |||||
29 | #include "lwan-array.h" | ||||
30 | #include "lwan-mod-rewrite.h" | ||||
31 | #include "patterns.h" | ||||
32 | |||||
33 | #ifdef HAVE_LUA | ||||
34 | #include <lauxlib.h> | ||||
35 | #include <lua.h> | ||||
36 | #include <lualib.h> | ||||
37 | |||||
38 | #include "lwan-lua.h" | ||||
39 | #endif | ||||
40 | |||||
41 | enum pattern_flag { | ||||
42 | PATTERN_HANDLE_REWRITE = 1<<0, | ||||
43 | PATTERN_HANDLE_REDIRECT = 1<<1, | ||||
44 | PATTERN_HANDLE_MASK = PATTERN_HANDLE_REWRITE | PATTERN_HANDLE_REDIRECT, | ||||
45 | |||||
46 | PATTERN_EXPAND_LWAN = 1<<2, | ||||
47 | PATTERN_EXPAND_LUA = 1<<3, | ||||
48 | PATTERN_EXPAND_MASK = PATTERN_EXPAND_LWAN | PATTERN_EXPAND_LUA, | ||||
49 | |||||
50 | PATTERN_COND_COOKIE = 1<<4, | ||||
51 | PATTERN_COND_MASK = PATTERN_COND_COOKIE, | ||||
52 | }; | ||||
53 | |||||
54 | struct pattern { | ||||
55 | char *pattern; | ||||
56 | char *expand_pattern; | ||||
57 | struct { | ||||
58 | struct { | ||||
59 | char *key; | ||||
60 | char *value; | ||||
61 | } cookie; | ||||
62 | } condition; | ||||
63 | enum pattern_flag flags; | ||||
64 | }; | ||||
65 | |||||
66 | DEFINE_ARRAY_TYPE(pattern_array, struct pattern)struct pattern_array { struct lwan_array base; }; __attribute__ ((unused)) static inline struct pattern *pattern_array_append ( struct pattern_array *array) { return (struct pattern *)lwan_array_append_heap (&array->base, sizeof(struct pattern)); } __attribute__ ((unused)) static inline struct pattern_array *coro_pattern_array_new (struct coro *coro) { return (struct pattern_array *)coro_lwan_array_new (coro, 0); } __attribute__((unused)) static inline struct pattern *pattern_array_get_array(struct pattern_array *array) { return (struct pattern *)array->base.base; } __attribute__((unused )) __attribute__((nonnull(1))) static inline void pattern_array_init ( struct pattern_array *array) { array->base = (struct lwan_array ){.base = ((void*)0), .elements = 0}; } __attribute__((unused )) static inline int pattern_array_reset( struct pattern_array *array) { return lwan_array_reset(&array->base, ((void *)0)); } __attribute__((unused)) static inline struct pattern *pattern_array_append0(struct pattern_array *array) { struct pattern *element = pattern_array_append(array); if (element) memset(element, 0, sizeof(*element)); return element; } __attribute__ ((unused)) static inline void pattern_array_sort( struct pattern_array *array, int (*cmp)(const void *a, const void *b)) { lwan_array_sort (&array->base, sizeof(struct pattern), cmp); } __attribute__ ((unused)) static inline size_t pattern_array_get_elem_index( const struct pattern_array *array, struct pattern *elem) { ( (void) sizeof ((elem >= (struct pattern *)array->base.base ) ? 1 : 0), __extension__ ({ if (elem >= (struct pattern * )array->base.base) ; else __assert_fail ("elem >= (struct pattern *)array->base.base" , "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 66, __extension__ __PRETTY_FUNCTION__); })); ((void) sizeof ((elem < (struct pattern *)array->base.base + array-> base.elements) ? 1 : 0), __extension__ ({ if (elem < (struct pattern *)array->base.base + array->base.elements) ; else __assert_fail ("elem < (struct pattern *)array->base.base + array->base.elements" , "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 66, __extension__ __PRETTY_FUNCTION__); })); return (size_t )(elem - (struct pattern *)array->base.base); } __attribute__ ((unused)) static inline struct pattern *pattern_array_get_elem (const struct pattern_array *array, size_t index) { ((void) sizeof ((index <= array->base.elements) ? 1 : 0), __extension__ ({ if (index <= array->base.elements) ; else __assert_fail ("index <= array->base.elements", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 66, __extension__ __PRETTY_FUNCTION__); })); return &(( struct pattern *)array->base.base)[index]; } __attribute__ ((unused)) static inline size_t pattern_array_len( const struct pattern_array *array) { return array->base.elements; } | ||||
67 | |||||
68 | struct private_data { | ||||
69 | struct pattern_array patterns; | ||||
70 | }; | ||||
71 | |||||
72 | struct str_builder { | ||||
73 | char *buffer; | ||||
74 | size_t size, len; | ||||
75 | }; | ||||
76 | |||||
77 | static enum lwan_http_status module_redirect_to(struct lwan_request *request, | ||||
78 | const char *url) | ||||
79 | { | ||||
80 | const struct lwan_key_value headers[] = { | ||||
81 | {"Location", coro_strdup(request->conn->coro, url)}, | ||||
82 | {}, | ||||
83 | }; | ||||
84 | |||||
85 | request->response.headers = | ||||
86 | coro_memdup(request->conn->coro, headers, sizeof(headers)); | ||||
87 | |||||
88 | if (LIKELY(headers[0].value && request->response.headers)__builtin_expect((!!(headers[0].value && request-> response.headers)), (1))) | ||||
89 | return HTTP_MOVED_PERMANENTLY; | ||||
90 | |||||
91 | return HTTP_INTERNAL_ERROR; | ||||
92 | } | ||||
93 | |||||
94 | static enum lwan_http_status module_rewrite_as(struct lwan_request *request, | ||||
95 | const char *url) | ||||
96 | { | ||||
97 | request->url.value = coro_strdup(request->conn->coro, url); | ||||
98 | |||||
99 | if (UNLIKELY(!request->url.value)__builtin_expect(((!request->url.value)), (0))) | ||||
100 | return HTTP_INTERNAL_ERROR; | ||||
101 | |||||
102 | request->url.len = strlen(request->url.value); | ||||
103 | request->original_url = request->url; | ||||
104 | request->flags |= RESPONSE_URL_REWRITTEN; | ||||
105 | |||||
106 | return HTTP_OK; | ||||
107 | } | ||||
108 | |||||
109 | static bool_Bool | ||||
110 | append_str(struct str_builder *builder, const char *src, size_t src_len) | ||||
111 | { | ||||
112 | size_t total_size; | ||||
113 | char *dest; | ||||
114 | |||||
115 | if (UNLIKELY(__builtin_add_overflow(builder->len, src_len, &total_size))__builtin_expect(((__builtin_add_overflow(builder->len, src_len , &total_size))), (0))) | ||||
116 | return false0; | ||||
117 | |||||
118 | if (UNLIKELY(total_size >= builder->size)__builtin_expect(((total_size >= builder->size)), (0))) | ||||
119 | return false0; | ||||
120 | |||||
121 | dest = mempcpy(builder->buffer + builder->len, src, src_len); | ||||
122 | *dest = '\0'; | ||||
123 | builder->len = total_size; | ||||
124 | |||||
125 | return true1; | ||||
126 | } | ||||
127 | |||||
128 | #define MAX_INT_DIGITS(3 * sizeof(int)) (3 * sizeof(int)) | ||||
129 | |||||
130 | static __attribute__((noinline)) int parse_int_len(const char *s, size_t len, | ||||
131 | int default_value) | ||||
132 | { | ||||
133 | if (UNLIKELY(len > MAX_INT_DIGITS)__builtin_expect(((len > (3 * sizeof(int)))), (0))) | ||||
134 | return default_value; | ||||
135 | |||||
136 | return parse_int(strndupa(s, len)(__extension__ ({ const char *__old = (s); size_t __len = strnlen (__old, (len)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })), default_value); | ||||
137 | } | ||||
138 | |||||
139 | static const char *expand(struct pattern *pattern, const char *orig, | ||||
140 | char buffer[static PATH_MAX4096], | ||||
141 | const struct str_find *sf, int captures) | ||||
142 | { | ||||
143 | const char *expand_pattern = pattern->expand_pattern; | ||||
144 | struct str_builder builder = {.buffer = buffer, .size = PATH_MAX4096}; | ||||
145 | const char *ptr; | ||||
146 | |||||
147 | ptr = strchr(expand_pattern, '%'); | ||||
148 | if (!ptr) | ||||
149 | return expand_pattern; | ||||
150 | |||||
151 | do { | ||||
152 | size_t index_len = strspn(ptr + 1, "0123456789"); | ||||
153 | |||||
154 | if (ptr > expand_pattern) { | ||||
155 | const size_t len = (size_t)(ptr - expand_pattern); | ||||
156 | |||||
157 | if (UNLIKELY(!append_str(&builder, expand_pattern, len))__builtin_expect(((!append_str(&builder, expand_pattern, len ))), (0))) | ||||
158 | return NULL((void*)0); | ||||
159 | |||||
160 | expand_pattern += len; | ||||
161 | } | ||||
162 | |||||
163 | if (LIKELY(index_len > 0)__builtin_expect((!!(index_len > 0)), (1))) { | ||||
164 | const int index = parse_int_len(ptr + 1, index_len, -1); | ||||
165 | |||||
166 | if (UNLIKELY(index < 0 || index > captures)__builtin_expect(((index < 0 || index > captures)), (0) )) | ||||
167 | return NULL((void*)0); | ||||
168 | |||||
169 | if (UNLIKELY(__builtin_expect(((!append_str(&builder, orig + sf[index] .sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so)))), (0)) | ||||
170 | !append_str(&builder, orig + sf[index].sm_so,__builtin_expect(((!append_str(&builder, orig + sf[index] .sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so)))), (0)) | ||||
171 | (size_t)(sf[index].sm_eo - sf[index].sm_so)))__builtin_expect(((!append_str(&builder, orig + sf[index] .sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so)))), (0))) | ||||
172 | return NULL((void*)0); | ||||
173 | |||||
174 | expand_pattern += index_len; | ||||
175 | } else if (UNLIKELY(!append_str(&builder, "%", 1))__builtin_expect(((!append_str(&builder, "%", 1))), (0))) { | ||||
176 | return NULL((void*)0); | ||||
177 | } | ||||
178 | |||||
179 | expand_pattern++; | ||||
180 | } while ((ptr = strchr(expand_pattern, '%'))); | ||||
181 | |||||
182 | const size_t remaining_len = strlen(expand_pattern); | ||||
183 | if (remaining_len && !append_str(&builder, expand_pattern, remaining_len)) | ||||
184 | return NULL((void*)0); | ||||
185 | |||||
186 | if (UNLIKELY(!builder.len)__builtin_expect(((!builder.len)), (0))) | ||||
187 | return NULL((void*)0); | ||||
188 | |||||
189 | return builder.buffer; | ||||
190 | } | ||||
191 | |||||
192 | #ifdef HAVE_LUA | ||||
193 | static void | ||||
194 | lua_close_defer(void *data) | ||||
195 | { | ||||
196 | lua_close((lua_State *)data); | ||||
197 | } | ||||
198 | |||||
199 | static const char *expand_lua(struct lwan_request *request, | ||||
200 | struct pattern *pattern, const char *orig, | ||||
201 | char buffer[static PATH_MAX4096], | ||||
202 | const struct str_find *sf, int captures) | ||||
203 | { | ||||
204 | const char *output; | ||||
205 | size_t output_len; | ||||
206 | int i; | ||||
207 | lua_State *L; | ||||
208 | |||||
209 | L = lwan_lua_create_state(NULL((void*)0), pattern->expand_pattern); | ||||
210 | if (UNLIKELY(!L)__builtin_expect(((!L)), (0))) | ||||
211 | return NULL((void*)0); | ||||
212 | coro_defer(request->conn->coro, lua_close_defer, L); | ||||
213 | |||||
214 | lua_getglobal(L, "handle_rewrite")lua_getfield(L, (-10002), ("handle_rewrite")); | ||||
215 | if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) { | ||||
216 | lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 218, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s" , lwan_lua_state_last_error(L)) | ||||
217 | "Could not obtain reference to `handle_rewrite()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 218, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s" , lwan_lua_state_last_error(L)) | ||||
218 | lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 218, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s" , lwan_lua_state_last_error(L)); | ||||
219 | return NULL((void*)0); | ||||
220 | } | ||||
221 | |||||
222 | lwan_lua_state_push_request(L, request); | ||||
223 | |||||
224 | lua_createtable(L, captures, 0); | ||||
225 | for (i = 0; i < captures; i++) { | ||||
226 | lua_pushinteger(L, i); | ||||
227 | lua_pushlstring(L, orig + sf[i].sm_so, | ||||
228 | (size_t)(sf[i].sm_eo - sf[i].sm_so)); | ||||
229 | lua_settable(L, -3); | ||||
230 | } | ||||
231 | |||||
232 | if (lua_pcall(L, 2, 1, 0) != 0) { | ||||
233 | lwan_status_error("Could not execute `handle_rewrite()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 234, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s" , lwan_lua_state_last_error(L)) | ||||
234 | lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 234, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s" , lwan_lua_state_last_error(L)); | ||||
235 | return NULL((void*)0); | ||||
236 | } | ||||
237 | |||||
238 | output = lua_tolstring(L, -1, &output_len); | ||||
239 | if (output_len >= PATH_MAX4096) { | ||||
240 | lwan_status_error("Rewritten URL exceeds %d bytes (got %zu bytes)",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 241, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)" , 4096, output_len) | ||||
241 | PATH_MAX, output_len)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 241, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)" , 4096, output_len); | ||||
242 | return NULL((void*)0); | ||||
243 | } | ||||
244 | |||||
245 | return memcpy(buffer, output, output_len + 1); | ||||
246 | } | ||||
247 | #endif | ||||
248 | |||||
249 | static enum lwan_http_status | ||||
250 | rewrite_handle_request(struct lwan_request *request, | ||||
251 | struct lwan_response *response __attribute__((unused)), | ||||
252 | void *instance) | ||||
253 | { | ||||
254 | struct private_data *pd = instance; | ||||
255 | const char *url = request->url.value; | ||||
256 | char final_url[PATH_MAX4096]; | ||||
257 | struct pattern *p; | ||||
258 | |||||
259 | LWAN_ARRAY_FOREACH(&pd->patterns, p)for (p = (&pd->patterns)->base.base; p < ((typeof (p))(&pd->patterns)->base.base + (&pd->patterns )->base.elements); p++) { | ||||
260 | struct str_find sf[MAXCAPTURES32]; | ||||
261 | const char *expanded = NULL((void*)0); | ||||
262 | const char *errmsg; | ||||
263 | int captures; | ||||
264 | |||||
265 | captures = str_find(url, p->pattern, sf, MAXCAPTURES32, &errmsg); | ||||
266 | if (captures <= 0) | ||||
267 | continue; | ||||
268 | |||||
269 | if (p->flags & PATTERN_COND_COOKIE) { | ||||
270 | assert(p->condition.cookie.key)((void) sizeof ((p->condition.cookie.key) ? 1 : 0), __extension__ ({ if (p->condition.cookie.key) ; else __assert_fail ("p->condition.cookie.key" , "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 270, __extension__ __PRETTY_FUNCTION__); })); | ||||
271 | assert(p->condition.cookie.value)((void) sizeof ((p->condition.cookie.value) ? 1 : 0), __extension__ ({ if (p->condition.cookie.value) ; else __assert_fail ("p->condition.cookie.value" , "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c" , 271, __extension__ __PRETTY_FUNCTION__); })); | ||||
272 | |||||
273 | const char *cookie_val = | ||||
274 | lwan_request_get_cookie(request, p->condition.cookie.key); | ||||
275 | |||||
276 | if (!cookie_val) | ||||
277 | continue; | ||||
278 | |||||
279 | if (!streq(cookie_val, p->condition.cookie.value)) | ||||
280 | continue; | ||||
281 | } | ||||
282 | |||||
283 | switch (p->flags & PATTERN_EXPAND_MASK) { | ||||
284 | #ifdef HAVE_LUA | ||||
285 | case PATTERN_EXPAND_LUA: | ||||
286 | expanded = expand_lua(request, p, url, final_url, sf, captures); | ||||
287 | break; | ||||
288 | #endif | ||||
289 | case PATTERN_EXPAND_LWAN: | ||||
290 | expanded = expand(p, url, final_url, sf, captures); | ||||
291 | break; | ||||
292 | } | ||||
293 | |||||
294 | if (LIKELY(expanded)__builtin_expect((!!(expanded)), (1))) { | ||||
295 | switch (p->flags & PATTERN_HANDLE_MASK) { | ||||
296 | case PATTERN_HANDLE_REDIRECT: | ||||
297 | return module_redirect_to(request, expanded); | ||||
298 | case PATTERN_HANDLE_REWRITE: | ||||
299 | return module_rewrite_as(request, expanded); | ||||
300 | } | ||||
301 | } | ||||
302 | |||||
303 | return HTTP_INTERNAL_ERROR; | ||||
304 | } | ||||
305 | |||||
306 | return HTTP_NOT_FOUND; | ||||
307 | } | ||||
308 | |||||
309 | static void *rewrite_create(const char *prefix __attribute__((unused)), | ||||
310 | void *instance __attribute__((unused))) | ||||
311 | { | ||||
312 | struct private_data *pd = malloc(sizeof(*pd)); | ||||
313 | |||||
314 | if (!pd) | ||||
315 | return NULL((void*)0); | ||||
316 | |||||
317 | pattern_array_init(&pd->patterns); | ||||
318 | |||||
319 | return pd; | ||||
320 | } | ||||
321 | |||||
322 | static void rewrite_destroy(void *instance) | ||||
323 | { | ||||
324 | struct private_data *pd = instance; | ||||
325 | struct pattern *iter; | ||||
326 | |||||
327 | LWAN_ARRAY_FOREACH(&pd->patterns, iter)for (iter = (&pd->patterns)->base.base; iter < ( (typeof(iter))(&pd->patterns)->base.base + (&pd ->patterns)->base.elements); iter++) { | ||||
328 | free(iter->pattern); | ||||
329 | free(iter->expand_pattern); | ||||
330 | if (iter->flags & PATTERN_COND_COOKIE) { | ||||
331 | free(iter->condition.cookie.key); | ||||
332 | free(iter->condition.cookie.value); | ||||
333 | } | ||||
334 | } | ||||
335 | |||||
336 | pattern_array_reset(&pd->patterns); | ||||
337 | free(pd); | ||||
338 | } | ||||
339 | |||||
340 | static void *rewrite_create_from_hash(const char *prefix, | ||||
341 | const struct hash *hash | ||||
342 | __attribute__((unused))) | ||||
343 | { | ||||
344 | return rewrite_create(prefix, NULL((void*)0)); | ||||
345 | } | ||||
346 | |||||
347 | static void parse_condition(struct pattern *pattern, | ||||
348 | struct config *config, | ||||
349 | const struct config_line *line) | ||||
350 | { | ||||
351 | char *cookie_key = NULL((void*)0), *cookie_value = NULL((void*)0); | ||||
352 | |||||
353 | if (!streq(line->value, "cookie")) { | ||||
354 | config_error(config, "Condition `%s' not supported", line->value); | ||||
355 | return; | ||||
356 | } | ||||
357 | |||||
358 | while ((line = config_read_line(config))) { | ||||
359 | switch (line->type) { | ||||
360 | case CONFIG_LINE_TYPE_SECTION: | ||||
361 | config_error(config, "Unexpected section: %s", line->key); | ||||
362 | return; | ||||
363 | |||||
364 | case CONFIG_LINE_TYPE_SECTION_END: | ||||
365 | if (!cookie_key || !cookie_value) { | ||||
366 | config_error(config, "Cookie key/value has not been specified"); | ||||
367 | return; | ||||
368 | } | ||||
369 | |||||
370 | pattern->flags |= PATTERN_COND_COOKIE; | ||||
371 | pattern->condition.cookie.key = cookie_key; | ||||
372 | pattern->condition.cookie.value = cookie_value; | ||||
373 | return; | ||||
374 | |||||
375 | case CONFIG_LINE_TYPE_LINE: | ||||
376 | if (cookie_key
| ||||
377 | config_error(config, "Can only condition on a single cookie. Currently has: %s=%s", cookie_key, cookie_value); | ||||
378 | free(cookie_key); | ||||
379 | free(cookie_value); | ||||
380 | return; | ||||
381 | } | ||||
382 | |||||
383 | cookie_key = strdup(line->key); | ||||
384 | if (!cookie_key) { | ||||
385 | config_error(config, "Could not copy cookie key while parsing condition"); | ||||
386 | return; | ||||
387 | } | ||||
388 | |||||
389 | cookie_value = strdup(line->value); | ||||
390 | if (!cookie_value) { | ||||
391 | free(cookie_key); | ||||
392 | config_error(config, "Could not copy cookie value while parsing condition"); | ||||
393 | return; | ||||
394 | } | ||||
395 | break; | ||||
396 | } | ||||
397 | } | ||||
398 | } | ||||
| |||||
399 | |||||
400 | static bool_Bool rewrite_parse_conf_pattern(struct private_data *pd, | ||||
401 | struct config *config, | ||||
402 | const struct config_line *line) | ||||
403 | { | ||||
404 | struct pattern *pattern; | ||||
405 | char *redirect_to = NULL((void*)0), *rewrite_as = NULL((void*)0); | ||||
406 | bool_Bool expand_with_lua = false0; | ||||
407 | |||||
408 | pattern = pattern_array_append0(&pd->patterns); | ||||
409 | if (!pattern
| ||||
410 | goto out_no_free; | ||||
411 | |||||
412 | pattern->pattern = strdup(line->value); | ||||
413 | if (!pattern->pattern) | ||||
414 | goto out; | ||||
415 | |||||
416 | while ((line = config_read_line(config))) { | ||||
417 | switch (line->type) { | ||||
418 | case CONFIG_LINE_TYPE_LINE: | ||||
419 | if (streq(line->key, "redirect_to")) { | ||||
420 | free(redirect_to); | ||||
421 | |||||
422 | redirect_to = strdup(line->value); | ||||
423 | if (!redirect_to) | ||||
424 | goto out; | ||||
425 | } else if (streq(line->key, "rewrite_as")) { | ||||
426 | free(rewrite_as); | ||||
427 | |||||
428 | rewrite_as = strdup(line->value); | ||||
429 | if (!rewrite_as) | ||||
430 | goto out; | ||||
431 | } else if (streq(line->key, "expand_with_lua")) { | ||||
432 | expand_with_lua = parse_bool(line->value, false0); | ||||
433 | } else { | ||||
434 | config_error(config, "Unexpected key: %s", line->key); | ||||
435 | goto out; | ||||
436 | } | ||||
437 | break; | ||||
438 | case CONFIG_LINE_TYPE_SECTION: | ||||
439 | if (streq(line->key, "condition")) { | ||||
440 | parse_condition(pattern, config, line); | ||||
441 | } else { | ||||
442 | config_error(config, "Unexpected section: %s", line->key); | ||||
443 | } | ||||
444 | break; | ||||
445 | case CONFIG_LINE_TYPE_SECTION_END: | ||||
446 | if (redirect_to && rewrite_as) { | ||||
447 | config_error( | ||||
448 | config, | ||||
449 | "`redirect to` and `rewrite as` are mutually exclusive"); | ||||
450 | goto out; | ||||
451 | } | ||||
452 | if (redirect_to) { | ||||
453 | pattern->expand_pattern = redirect_to; | ||||
454 | pattern->flags |= PATTERN_HANDLE_REDIRECT; | ||||
455 | } else if (rewrite_as) { | ||||
456 | pattern->expand_pattern = rewrite_as; | ||||
457 | pattern->flags |= PATTERN_HANDLE_REWRITE; | ||||
458 | } else { | ||||
459 | config_error( | ||||
460 | config, | ||||
461 | "either `redirect to` or `rewrite as` are required"); | ||||
462 | goto out; | ||||
463 | } | ||||
464 | if (expand_with_lua) { | ||||
465 | #ifdef HAVE_LUA | ||||
466 | pattern->flags |= PATTERN_EXPAND_LUA; | ||||
467 | #else | ||||
468 | config_error(config, "Lwan has been built without Lua. " | ||||
469 | "`expand_with_lua` is not available"); | ||||
470 | goto out; | ||||
471 | #endif | ||||
472 | } else { | ||||
473 | pattern->flags |= PATTERN_EXPAND_LWAN; | ||||
474 | } | ||||
475 | |||||
476 | return true1; | ||||
477 | } | ||||
478 | } | ||||
479 | |||||
480 | out: | ||||
481 | free(pattern->pattern); | ||||
482 | free(redirect_to); | ||||
483 | free(rewrite_as); | ||||
484 | out_no_free: | ||||
485 | config_error(config, "Could not copy pattern"); | ||||
486 | return false0; | ||||
487 | } | ||||
488 | |||||
489 | static bool_Bool rewrite_parse_conf(void *instance, struct config *config) | ||||
490 | { | ||||
491 | struct private_data *pd = instance; | ||||
492 | const struct config_line *line; | ||||
493 | |||||
494 | while ((line = config_read_line(config))) { | ||||
| |||||
495 | switch (line->type) { | ||||
496 | case CONFIG_LINE_TYPE_LINE: | ||||
497 | config_error(config, "Unknown option: %s", line->key); | ||||
498 | break; | ||||
499 | case CONFIG_LINE_TYPE_SECTION: | ||||
500 | if (streq(line->key, "pattern")) { | ||||
501 | rewrite_parse_conf_pattern(pd, config, line); | ||||
502 | } else { | ||||
503 | config_error(config, "Unknown section: %s", line->key); | ||||
504 | } | ||||
505 | break; | ||||
506 | case CONFIG_LINE_TYPE_SECTION_END: | ||||
507 | break; | ||||
508 | } | ||||
509 | } | ||||
510 | |||||
511 | return !config_last_error(config); | ||||
512 | } | ||||
513 | |||||
514 | static const struct lwan_module module = { | ||||
515 | .create = rewrite_create, | ||||
516 | .create_from_hash = rewrite_create_from_hash, | ||||
517 | .parse_conf = rewrite_parse_conf, | ||||
518 | .destroy = rewrite_destroy, | ||||
519 | .handle_request = rewrite_handle_request, | ||||
520 | .flags = HANDLER_CAN_REWRITE_URL | ||||
521 | }; | ||||
522 | |||||
523 | LWAN_REGISTER_MODULE(rewrite, &module)const struct lwan_module_info __attribute__((used, section("lwan_module" ))) lwan_module_info_rewrite = {.name = "rewrite", .module = & module}; |