Bug Summary

File:lwan-mod-rewrite.c
Warning:line 398, column 1
Potential leak of memory pointed to by 'cookie_key'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name lwan-mod-rewrite.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -fno-plt -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/clang/12.0.1 -include /home/buildbot/lwan-worker/clang-analyze/build/lwan-build-config.h -D _FILE_OFFSET_BITS=64 -D _TIME_BITS=64 -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib/missing -I /usr/include/luajit-2.0 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -I /home/buildbot/lwan-worker/clang-analyze/build -internal-isystem /usr/local/include -internal-isystem /usr/lib/clang/12.0.1/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-free-nonheap-object -std=gnu99 -fdebug-compilation-dir /home/buildbot/lwan-worker/clang-analyze/build/src/lib -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -analyzer-output=html -faddrsig -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2021-09-16-042345-4182395-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c
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
41enum 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
54struct 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
66DEFINE_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
68struct private_data {
69 struct pattern_array patterns;
70};
71
72struct str_builder {
73 char *buffer;
74 size_t size, len;
75};
76
77static 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
94static 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
109static bool_Bool
110append_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
130static __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
139static 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
193static void
194lua_close_defer(void *data)
195{
196 lua_close((lua_State *)data);
197}
198
199static 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
249static enum lwan_http_status
250rewrite_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
309static 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
322static 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
340static 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
347static 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")) {
12
Taking false branch
354 config_error(config, "Condition `%s' not supported", line->value);
355 return;
356 }
357
358 while ((line = config_read_line(config))) {
13
Loop condition is true. Entering loop body
22
Loop condition is false. Execution continues on line 358
359 switch (line->type) {
14
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 375
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
14.1
'cookie_key' is null
|| cookie_value
14.2
'cookie_value' is null
) {
15
Taking false branch
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);
16
Memory is allocated
384 if (!cookie_key) {
17
Assuming 'cookie_key' is non-null
18
Taking false branch
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) {
19
Assuming 'cookie_value' is non-null
20
Taking false branch
391 free(cookie_key);
392 config_error(config, "Could not copy cookie value while parsing condition");
393 return;
394 }
395 break;
21
Execution continues on line 358
396 }
397 }
398}
23
Potential leak of memory pointed to by 'cookie_key'
399
400static 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
4.1
'pattern' is non-null
)
5
Taking false branch
410 goto out_no_free;
411
412 pattern->pattern = strdup(line->value);
413 if (!pattern->pattern)
6
Assuming field 'pattern' is non-null
7
Taking false branch
414 goto out;
415
416 while ((line = config_read_line(config))) {
8
Loop condition is true. Entering loop body
417 switch (line->type) {
9
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 438
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")) {
10
Taking true branch
440 parse_condition(pattern, config, line);
11
Calling 'parse_condition'
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
480out:
481 free(pattern->pattern);
482 free(redirect_to);
483 free(rewrite_as);
484out_no_free:
485 config_error(config, "Could not copy pattern");
486 return false0;
487}
488
489static 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))) {
1
Loop condition is true. Entering loop body
495 switch (line->type) {
2
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 499
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")) {
3
Taking true branch
501 rewrite_parse_conf_pattern(pd, config, line);
4
Calling 'rewrite_parse_conf_pattern'
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
514static 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
523LWAN_REGISTER_MODULE(rewrite, &module)const struct lwan_module_info __attribute__((used, section("lwan_module"
))) lwan_module_info_rewrite = {.name = "rewrite", .module = &
module}
;