Bug Summary

File:lwan-mod-rewrite.c
Warning:line 639, column 5
Attempt to free released memory

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -fno-plt -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/buildbot/lwan-worker/clang-analyze/build/src/lib -resource-dir /usr/lib/clang/14.0.6 -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/python3.10 -I /usr/include/luajit-2.1 -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/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.1.1/../../../../x86_64-pc-linux-gnu/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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2022-08-14-234726-3831083-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 L. A. F. Pereira <l@tia.mat.br>
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#include <sys/stat.h>
27
28#include "lwan-private.h"
29
30#include "patterns.h"
31#include "lwan-array.h"
32#include "lwan-mod-rewrite.h"
33#include "lwan-strbuf.h"
34
35#ifdef LWAN_HAVE_LUA
36#include <lauxlib.h>
37#include <lua.h>
38#include <lualib.h>
39
40#include "lwan-lua.h"
41#endif
42
43enum pattern_flag {
44 PATTERN_HANDLE_REWRITE = 1 << 0,
45 PATTERN_HANDLE_REDIRECT = 1 << 1,
46 PATTERN_HANDLE_MASK = PATTERN_HANDLE_REWRITE | PATTERN_HANDLE_REDIRECT,
47
48 PATTERN_EXPAND_LWAN = 1 << 2,
49 PATTERN_EXPAND_LUA = 1 << 3,
50 PATTERN_EXPAND_MASK = PATTERN_EXPAND_LWAN | PATTERN_EXPAND_LUA,
51
52 PATTERN_COND_COOKIE = 1 << 4,
53 PATTERN_COND_ENV_VAR = 1 << 5,
54 PATTERN_COND_STAT = 1 << 6,
55 PATTERN_COND_QUERY_VAR = 1 << 7,
56 PATTERN_COND_POST_VAR = 1 << 8,
57 PATTERN_COND_HEADER = 1 << 9,
58 PATTERN_COND_LUA = 1 << 10,
59 PATTERN_COND_METHOD = 1 << 11,
60 PATTERN_COND_ACCEPT_ENCODING = 1 << 12,
61 PATTERN_COND_PROXIED = 1 << 13,
62 PATTERN_COND_HTTP10 = 1 << 14,
63 PATTERN_COND_HAS_QUERY_STRING = 1 << 15,
64 PATTERN_COND_HTTPS = 1 << 16,
65 PATTERN_COND_BACKREF = 1 << 17,
66 PATTERN_COND_MASK = PATTERN_COND_COOKIE | PATTERN_COND_ENV_VAR |
67 PATTERN_COND_STAT | PATTERN_COND_QUERY_VAR |
68 PATTERN_COND_POST_VAR | PATTERN_COND_HEADER |
69 PATTERN_COND_LUA | PATTERN_COND_METHOD |
70 PATTERN_COND_ACCEPT_ENCODING |
71 PATTERN_COND_PROXIED | PATTERN_COND_HTTP10 |
72 PATTERN_COND_HAS_QUERY_STRING |
73 PATTERN_COND_HTTPS | PATTERN_COND_BACKREF,
74
75 PATTERN_COND_STAT__HAS_IS_FILE = 1 << 18,
76 PATTERN_COND_STAT__HAS_IS_DIR = 1 << 19,
77 PATTERN_COND_STAT__IS_FILE = 1 << 20,
78 PATTERN_COND_STAT__IS_DIR = 1 << 21,
79
80 PATTERN_COND_STAT__FILE_CHECK =
81 PATTERN_COND_STAT__HAS_IS_FILE | PATTERN_COND_STAT__IS_FILE,
82 PATTERN_COND_STAT__DIR_CHECK =
83 PATTERN_COND_STAT__HAS_IS_DIR | PATTERN_COND_STAT__IS_DIR,
84
85 PATTERN_COND_HTTPS__IS_HTTPS = 1 << 22,
86};
87
88struct pattern {
89 char *pattern;
90 char *expand_pattern;
91 struct {
92 struct lwan_key_value cookie;
93 struct lwan_key_value env_var;
94 struct lwan_key_value query_var;
95 struct lwan_key_value post_var;
96 struct lwan_key_value header;
97 struct {
98 char *path;
99 } stat;
100 struct {
101 char *script;
102 } lua;
103 struct {
104 int index;
105 char *str;
106 } backref;
107 enum lwan_request_flags request_flags;
108 /* FIXME: Use pahole to find alignment holes? */
109 } condition;
110 enum pattern_flag flags;
111};
112
113DEFINE_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"
, 113, __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"
, 113, __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"
, 113, __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; }
114
115struct private_data {
116 struct pattern_array patterns;
117};
118
119static enum lwan_http_status module_redirect_to(struct lwan_request *request,
120 const char *url)
121{
122 const struct lwan_key_value headers[] = {
123 {"Location", coro_strdup(request->conn->coro, url)},
124 {},
125 };
126
127 request->response.headers =
128 coro_memdup(request->conn->coro, headers, sizeof(headers));
129
130 if (LIKELY(headers[0].value && request->response.headers)__builtin_expect((!!(headers[0].value && request->
response.headers)), (1))
)
131 return HTTP_MOVED_PERMANENTLY;
132
133 return HTTP_INTERNAL_ERROR;
134}
135
136static enum lwan_http_status module_rewrite_as(struct lwan_request *request,
137 const char *url)
138{
139 request->url.value = coro_strdup(request->conn->coro, url);
140
141 if (UNLIKELY(!request->url.value)__builtin_expect(((!request->url.value)), (0)))
142 return HTTP_INTERNAL_ERROR;
143
144 request->url.len = strlen(request->url.value);
145 request->original_url = request->url;
146 request->flags |= RESPONSE_URL_REWRITTEN;
147
148 return HTTP_OK;
149}
150
151#define MAX_INT_DIGITS(3 * sizeof(int)) (3 * sizeof(int))
152
153static __attribute__((noinline)) int parse_int_len(const char *s, size_t len,
154 int default_value)
155{
156 if (UNLIKELY(len > MAX_INT_DIGITS)__builtin_expect(((len > (3 * sizeof(int)))), (0)))
157 return default_value;
158
159 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);
160}
161
162static const char *expand_string(const char *expand_pattern,
163 const char *orig,
164 char buffer[static PATH_MAX4096],
165 const struct str_find *sf,
166 int captures)
167{
168 struct lwan_strbuf strbuf;
169 const char *ptr;
170
171 ptr = strchr(expand_pattern, '%');
172 if (!ptr)
173 return expand_pattern;
174
175 if (!lwan_strbuf_init_with_fixed_buffer(&strbuf, buffer, PATH_MAX4096))
176 return NULL((void*)0);
177
178 do {
179 size_t index_len = strspn(ptr + 1, "0123456789");
180
181 if (ptr > expand_pattern) {
182 const size_t len = (size_t)(ptr - expand_pattern);
183
184 if (UNLIKELY(!lwan_strbuf_append_str(&strbuf, expand_pattern, len))__builtin_expect(((!lwan_strbuf_append_str(&strbuf, expand_pattern
, len))), (0))
)
185 return NULL((void*)0);
186
187 expand_pattern += len;
188 }
189
190 if (LIKELY(index_len > 0)__builtin_expect((!!(index_len > 0)), (1))) {
191 const int index = parse_int_len(ptr + 1, index_len, -1);
192
193 if (UNLIKELY(index < 0 || index > captures)__builtin_expect(((index < 0 || index > captures)), (0)
)
)
194 return NULL((void*)0);
195
196 if (UNLIKELY(!lwan_strbuf_append_str(__builtin_expect(((!lwan_strbuf_append_str( &strbuf, orig
+ sf[index].sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so
)))), (0))
197 &strbuf, orig + sf[index].sm_so,__builtin_expect(((!lwan_strbuf_append_str( &strbuf, orig
+ sf[index].sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so
)))), (0))
198 (size_t)(sf[index].sm_eo - sf[index].sm_so)))__builtin_expect(((!lwan_strbuf_append_str( &strbuf, orig
+ sf[index].sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so
)))), (0))
)
199 return NULL((void*)0);
200
201 expand_pattern += index_len;
202 } else if (UNLIKELY(!lwan_strbuf_append_char(&strbuf, '%'))__builtin_expect(((!lwan_strbuf_append_char(&strbuf, '%')
)), (0))
) {
203 return NULL((void*)0);
204 }
205
206 expand_pattern++;
207 } while ((ptr = strchr(expand_pattern, '%')));
208
209 const size_t remaining_len = strlen(expand_pattern);
210 if (remaining_len &&
211 !lwan_strbuf_append_str(&strbuf, expand_pattern, remaining_len))
212 return NULL((void*)0);
213
214 if (UNLIKELY(!lwan_strbuf_get_length(&strbuf))__builtin_expect(((!lwan_strbuf_get_length(&strbuf))), (0
))
)
215 return NULL((void*)0);
216
217 return lwan_strbuf_get_buffer(&strbuf);
218}
219
220static ALWAYS_INLINEinline __attribute__((always_inline)) const char *expand(const struct pattern *pattern,
221 const char *orig,
222 char buffer[static PATH_MAX4096],
223 const struct str_find *sf,
224 int captures)
225{
226 return expand_string(pattern->expand_pattern, orig, buffer, sf, captures);
227}
228
229#ifdef LWAN_HAVE_LUA
230static void
231lua_close_defer(void *data)
232{
233 lua_close((lua_State *)data);
234}
235
236static const char *expand_lua(struct lwan_request *request,
237 struct pattern *pattern, const char *orig,
238 char buffer[static PATH_MAX4096],
239 const struct str_find *sf, int captures)
240{
241 const char *output;
242 size_t output_len;
243 int i;
244 lua_State *L;
245
246 L = lwan_lua_create_state(NULL((void*)0), pattern->expand_pattern);
247 if (UNLIKELY(!L)__builtin_expect(((!L)), (0)))
248 return NULL((void*)0);
249 coro_defer(request->conn->coro, lua_close_defer, L);
250
251 lua_getglobal(L, "handle_rewrite")lua_getfield(L, (-10002), ("handle_rewrite"));
252 if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) {
253 lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 255, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
254 "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"
, 255, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
255 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 255, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
;
256 return NULL((void*)0);
257 }
258
259 lwan_lua_state_push_request(L, request);
260
261 lua_createtable(L, captures, 0);
262 for (i = 0; i < captures; i++) {
263 lua_pushinteger(L, i);
264 lua_pushlstring(L, orig + sf[i].sm_so,
265 (size_t)(sf[i].sm_eo - sf[i].sm_so));
266 lua_settable(L, -3);
267 }
268
269 if (lua_pcall(L, 2, 1, 0) != 0) {
270 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"
, 271, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
271 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 271, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
;
272 return NULL((void*)0);
273 }
274
275 output = lua_tolstring(L, -1, &output_len);
276 if (output_len >= PATH_MAX4096) {
277 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"
, 278, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)"
, 4096, output_len)
278 PATH_MAX, output_len)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 278, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)"
, 4096, output_len)
;
279 return NULL((void*)0);
280 }
281
282 return memcpy(buffer, output, output_len + 1);
283}
284#endif
285
286static bool_Bool condition_matches(struct lwan_request *request,
287 const struct pattern *p,
288 const struct str_find *sf,
289 int captures,
290 char expanded_buf[static PATH_MAX4096])
291{
292 if (LIKELY(!(p->flags & PATTERN_COND_MASK))__builtin_expect((!!(!(p->flags & PATTERN_COND_MASK)))
, (1))
)
293 return true1;
294
295 const char *url = request->url.value;
296
297 if (p->flags & PATTERN_COND_METHOD) {
298 const enum lwan_request_flags method =
299 p->condition.request_flags & REQUEST_METHOD_MASK;
300 if (lwan_request_get_method(request) != method)
301 return false0;
302 }
303
304 if (p->flags & PATTERN_COND_BACKREF) {
305 assert(p->condition.backref.index > 0)((void) sizeof ((p->condition.backref.index > 0) ? 1 : 0
), __extension__ ({ if (p->condition.backref.index > 0)
; else __assert_fail ("p->condition.backref.index > 0"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 305, __extension__ __PRETTY_FUNCTION__); }))
;
306 assert(p->condition.backref.str)((void) sizeof ((p->condition.backref.str) ? 1 : 0), __extension__
({ if (p->condition.backref.str) ; else __assert_fail ("p->condition.backref.str"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 306, __extension__ __PRETTY_FUNCTION__); }))
;
307
308 if (captures > p->condition.backref.index)
309 return false0;
310
311 const struct str_find *s = &sf[p->condition.backref.index];
312 const size_t len = strlen(p->condition.backref.str);
313
314 if ((size_t)(s->sm_eo - s->sm_so) != len)
315 return false0;
316
317 if (!memcmp(request->url.value + s->sm_so, p->condition.backref.str, len))
318 return false0;
319 }
320
321 if (p->flags & PATTERN_COND_HTTPS) {
322 bool_Bool is_tls = request->conn->flags & CONN_TLS;
323 if (p->flags & PATTERN_COND_HTTPS__IS_HTTPS) {
324 if (!is_tls)
325 return false0;
326 } else if (is_tls) {
327 return false0;
328 }
329 }
330
331 if (p->flags & PATTERN_COND_ACCEPT_ENCODING) {
332 const enum lwan_request_flags accept =
333 p->condition.request_flags & REQUEST_ACCEPT_MASK;
334 if (!(lwan_request_get_accept_encoding(request) & accept))
335 return false0;
336 }
337
338 if (p->flags & PATTERN_COND_PROXIED) {
339 if (!(request->flags & p->condition.request_flags & REQUEST_PROXIED))
340 return false0;
341 }
342
343 if (p->flags & PATTERN_COND_HTTP10) {
344 if (!(request->flags & p->condition.request_flags & REQUEST_IS_HTTP_1_0))
345 return false0;
346 }
347
348 if (p->flags & PATTERN_COND_HAS_QUERY_STRING) {
349 if (!(request->flags & p->condition.request_flags & REQUEST_HAS_QUERY_STRING))
350 return false0;
351 }
352
353 if (p->flags & PATTERN_COND_COOKIE) {
354 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"
, 354, __extension__ __PRETTY_FUNCTION__); }))
;
355 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"
, 355, __extension__ __PRETTY_FUNCTION__); }))
;
356
357 const char *cookie =
358 lwan_request_get_cookie(request, p->condition.cookie.key);
359 if (!cookie)
360 return false0;
361
362 const char *val = expand_string(p->condition.cookie.value, url,
363 expanded_buf, sf, captures);
364 if (!val || !streq(val, cookie))
365 return false0;
366 }
367
368 if (p->flags & PATTERN_COND_ENV_VAR) {
369 assert(p->condition.env_var.key)((void) sizeof ((p->condition.env_var.key) ? 1 : 0), __extension__
({ if (p->condition.env_var.key) ; else __assert_fail ("p->condition.env_var.key"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 369, __extension__ __PRETTY_FUNCTION__); }))
;
370 assert(p->condition.env_var.value)((void) sizeof ((p->condition.env_var.value) ? 1 : 0), __extension__
({ if (p->condition.env_var.value) ; else __assert_fail (
"p->condition.env_var.value", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 370, __extension__ __PRETTY_FUNCTION__); }))
;
371
372 const char *env_var = secure_getenv(p->condition.env_var.key);
373 if (!env_var)
374 return false0;
375
376 const char *val = expand_string(p->condition.env_var.value, url,
377 expanded_buf, sf, captures);
378 if (!val || !streq(val, env_var))
379 return false0;
380 }
381
382 if (p->flags & PATTERN_COND_QUERY_VAR) {
383 assert(p->condition.query_var.key)((void) sizeof ((p->condition.query_var.key) ? 1 : 0), __extension__
({ if (p->condition.query_var.key) ; else __assert_fail (
"p->condition.query_var.key", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 383, __extension__ __PRETTY_FUNCTION__); }))
;
384 assert(p->condition.query_var.value)((void) sizeof ((p->condition.query_var.value) ? 1 : 0), __extension__
({ if (p->condition.query_var.value) ; else __assert_fail
("p->condition.query_var.value", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 384, __extension__ __PRETTY_FUNCTION__); }))
;
385
386 const char *query =
387 lwan_request_get_query_param(request, p->condition.query_var.key);
388
389 if (!query)
390 return false0;
391
392 const char *val = expand_string(p->condition.query_var.value, url,
393 expanded_buf, sf, captures);
394 if (!val || !streq(val, query))
395 return false0;
396 }
397
398 if (p->flags & PATTERN_COND_POST_VAR) {
399 assert(p->condition.post_var.key)((void) sizeof ((p->condition.post_var.key) ? 1 : 0), __extension__
({ if (p->condition.post_var.key) ; else __assert_fail ("p->condition.post_var.key"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 399, __extension__ __PRETTY_FUNCTION__); }))
;
400 assert(p->condition.post_var.value)((void) sizeof ((p->condition.post_var.value) ? 1 : 0), __extension__
({ if (p->condition.post_var.value) ; else __assert_fail (
"p->condition.post_var.value", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 400, __extension__ __PRETTY_FUNCTION__); }))
;
401
402 const char *post =
403 lwan_request_get_post_param(request, p->condition.post_var.key);
404 if (!post)
405 return false0;
406
407 const char *val = expand_string(p->condition.post_var.value, url,
408 expanded_buf, sf, captures);
409 if (!val || !streq(val, post))
410 return false0;
411 }
412
413 if (p->flags & PATTERN_COND_STAT) {
414 assert(p->condition.stat.path)((void) sizeof ((p->condition.stat.path) ? 1 : 0), __extension__
({ if (p->condition.stat.path) ; else __assert_fail ("p->condition.stat.path"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 414, __extension__ __PRETTY_FUNCTION__); }))
;
415
416 struct stat st;
417
418 /* FIXME: Expanding path from a user-controlled URL and use the
419 * resulting path to call stat(2) on could lead to some information
420 * disclosure vulnerability. Would require the server to be configured
421 * in a certain way, though.
422 */
423 const char *path = expand_string(p->condition.stat.path, url,
424 expanded_buf, sf, captures);
425 if (!path || stat(path, &st) < 0)
426 return false0;
427
428 if ((p->flags & PATTERN_COND_STAT__FILE_CHECK) ==
429 PATTERN_COND_STAT__FILE_CHECK &&
430 !S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000)))
431 return false0;
432 if ((p->flags & PATTERN_COND_STAT__DIR_CHECK) ==
433 PATTERN_COND_STAT__DIR_CHECK &&
434 !S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)))
435 return false0;
436 }
437
438#ifdef LWAN_HAVE_LUA
439 if (p->flags & PATTERN_COND_LUA) {
440 assert(p->condition.lua.script)((void) sizeof ((p->condition.lua.script) ? 1 : 0), __extension__
({ if (p->condition.lua.script) ; else __assert_fail ("p->condition.lua.script"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 440, __extension__ __PRETTY_FUNCTION__); }))
;
441
442 lua_State *L = lwan_lua_create_state(NULL((void*)0), p->condition.lua.script);
443 if (!L)
444 return false0;
445 coro_defer(request->conn->coro, lua_close_defer, L);
446
447 lua_getglobal(L, "matches")lua_getfield(L, (-10002), ("matches"));
448 if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) {
449 lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 451, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
450 "Could not obtain reference to `matches()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 451, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
451 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 451, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
;
452 return false0;
453 }
454
455 lwan_lua_state_push_request(L, request);
456
457 if (lua_pcall(L, 1, 1, 0) != 0) {
458 lwan_status_error("Could not execute `matches()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 459, __FUNCTION__, "Could not execute `matches()` function: %s"
, lwan_lua_state_last_error(L))
459 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 459, __FUNCTION__, "Could not execute `matches()` function: %s"
, lwan_lua_state_last_error(L))
;
460 return false0;
461 }
462
463 if (!lua_toboolean(L, -1))
464 return false0;
465 }
466#else
467 assert(!(p->flags & PATTERN_COND_LUA))((void) sizeof ((!(p->flags & PATTERN_COND_LUA)) ? 1 :
0), __extension__ ({ if (!(p->flags & PATTERN_COND_LUA
)) ; else __assert_fail ("!(p->flags & PATTERN_COND_LUA)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 467, __extension__ __PRETTY_FUNCTION__); }))
;
468#endif
469
470 return true1;
471}
472
473static enum lwan_http_status
474rewrite_handle_request(struct lwan_request *request,
475 struct lwan_response *response __attribute__((unused)),
476 void *instance)
477{
478 struct private_data *pd = instance;
479 const char *url = request->url.value;
480 char final_url[PATH_MAX4096];
481 struct pattern *p;
482
483 LWAN_ARRAY_FOREACH(&pd->patterns, p)for (p = (&pd->patterns)->base.base; p < ((typeof
(p))(&pd->patterns)->base.base + (&pd->patterns
)->base.elements); p++)
{
484 struct str_find sf[MAXCAPTURES32];
485 const char *expanded = NULL((void*)0);
486 const char *errmsg;
487 int captures;
488
489 captures = str_find(url, p->pattern, sf, MAXCAPTURES32, &errmsg);
490 if (captures <= 0)
491 continue;
492
493 if (!condition_matches(request, p, sf, captures, final_url))
494 continue;
495
496 switch (p->flags & PATTERN_EXPAND_MASK) {
497#ifdef LWAN_HAVE_LUA
498 case PATTERN_EXPAND_LUA:
499 expanded = expand_lua(request, p, url, final_url, sf, captures);
500 break;
501#endif
502 case PATTERN_EXPAND_LWAN:
503 expanded = expand(p, url, final_url, sf, captures);
504 break;
505 }
506
507 if (LIKELY(expanded)__builtin_expect((!!(expanded)), (1))) {
508 switch (p->flags & PATTERN_HANDLE_MASK) {
509 case PATTERN_HANDLE_REDIRECT:
510 return module_redirect_to(request, expanded);
511 case PATTERN_HANDLE_REWRITE:
512 return module_rewrite_as(request, expanded);
513 }
514 }
515
516 return HTTP_INTERNAL_ERROR;
517 }
518
519 return HTTP_NOT_FOUND;
520}
521
522static void *rewrite_create(const char *prefix __attribute__((unused)),
523 void *instance __attribute__((unused)))
524{
525 struct private_data *pd = malloc(sizeof(*pd));
526
527 if (!pd)
528 return NULL((void*)0);
529
530 pattern_array_init(&pd->patterns);
531
532 return pd;
533}
534
535static void rewrite_destroy(void *instance)
536{
537 struct private_data *pd = instance;
538 struct pattern *iter;
539
540 LWAN_ARRAY_FOREACH(&pd->patterns, iter)for (iter = (&pd->patterns)->base.base; iter < (
(typeof(iter))(&pd->patterns)->base.base + (&pd
->patterns)->base.elements); iter++)
{
541 free(iter->pattern);
542 free(iter->expand_pattern);
543 if (iter->flags & PATTERN_COND_COOKIE) {
544 free(iter->condition.cookie.key);
545 free(iter->condition.cookie.value);
546 }
547 if (iter->flags & PATTERN_COND_ENV_VAR) {
548 free(iter->condition.env_var.key);
549 free(iter->condition.env_var.value);
550 }
551 if (iter->flags & PATTERN_COND_QUERY_VAR) {
552 free(iter->condition.query_var.key);
553 free(iter->condition.query_var.value);
554 }
555 if (iter->flags & PATTERN_COND_POST_VAR) {
556 free(iter->condition.post_var.key);
557 free(iter->condition.post_var.value);
558 }
559 if (iter->flags & PATTERN_COND_HEADER) {
560 free(iter->condition.header.key);
561 free(iter->condition.header.value);
562 }
563 if (iter->flags & PATTERN_COND_STAT) {
564 free(iter->condition.stat.path);
565 }
566#ifdef LWAN_HAVE_LUA
567 if (iter->flags & PATTERN_COND_LUA) {
568 free(iter->condition.lua.script);
569 }
570#endif
571 if (iter->flags & PATTERN_COND_BACKREF) {
572 free(iter->condition.backref.str);
573 }
574 }
575
576 pattern_array_reset(&pd->patterns);
577 free(pd);
578}
579
580static void *rewrite_create_from_hash(const char *prefix,
581 const struct hash *hash
582 __attribute__((unused)))
583{
584 return rewrite_create(prefix, NULL((void*)0));
585}
586
587static void parse_condition_key_value(struct pattern *pattern,
588 struct lwan_key_value *key_value,
589 enum pattern_flag condition_type,
590 struct config *config,
591 const struct config_line *line)
592{
593 char *key = NULL((void*)0), *value = NULL((void*)0);
594
595 while ((line = config_read_line(config))) {
14
Loop condition is true. Entering loop body
596 switch (line->type) {
15
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 611
597 case CONFIG_LINE_TYPE_SECTION:
598 config_error(config, "Unexpected section: %s", line->key);
599 goto out;
600
601 case CONFIG_LINE_TYPE_SECTION_END:
602 if (!key || !value) {
603 config_error(config, "Key/value has not been specified");
604 goto out;
605 }
606
607 *key_value = (struct lwan_key_value){key, value};
608 pattern->flags |= condition_type & PATTERN_COND_MASK;
609 return;
610
611 case CONFIG_LINE_TYPE_LINE:
612 if (key
15.1
'key' is null
|| value
15.2
'value' is null
) {
16
Taking false branch
613 config_error(config,
614 "Can only condition on a single key/value pair. "
615 "Currently has: %s=%s",
616 key, value);
617 goto out;
618 }
619
620 key = strdup(line->key);
17
Memory is allocated
621 if (!key) {
18
Assuming 'key' is non-null
19
Taking false branch
622 config_error(config,
623 "Could not copy key while parsing condition");
624 goto out;
625 }
626
627 value = strdup(line->value);
628 if (!value) {
20
Assuming 'value' is null
21
Taking true branch
629 free(key);
22
Memory is released
630 config_error(config,
631 "Could not copy value while parsing condition");
632 goto out;
23
Control jumps to line 639
633 }
634 break;
635 }
636 }
637
638out:
639 free(key);
24
Attempt to free released memory
640 free(value);
641}
642
643static void parse_condition_stat(struct pattern *pattern,
644 struct config *config,
645 const struct config_line *line)
646{
647 char *path = NULL((void*)0);
648 bool_Bool has_is_dir = false0, is_dir = false0;
649 bool_Bool has_is_file = false0, is_file = false0;
650
651 while ((line = config_read_line(config))) {
652 switch (line->type) {
653 case CONFIG_LINE_TYPE_SECTION:
654 config_error(config, "Unexpected section: %s", line->key);
655 goto out;
656
657 case CONFIG_LINE_TYPE_SECTION_END:
658 if (!path) {
659 config_error(config, "Path not specified");
660 goto out;
661 }
662
663 pattern->condition.stat.path = path;
664 if (has_is_dir) {
665 pattern->flags |= PATTERN_COND_STAT__HAS_IS_DIR;
666 if (is_dir)
667 pattern->flags |= PATTERN_COND_STAT__IS_DIR;
668 }
669 if (has_is_file) {
670 pattern->flags |= PATTERN_COND_STAT__HAS_IS_FILE;
671 if (is_file)
672 pattern->flags |= PATTERN_COND_STAT__IS_FILE;
673 }
674 pattern->flags |= PATTERN_COND_STAT;
675 return;
676
677 case CONFIG_LINE_TYPE_LINE:
678 if (streq(line->key, "path")) {
679 if (path) {
680 config_error(config, "Path `%s` already specified", path);
681 goto out;
682 }
683 path = strdup(line->value);
684 if (!path) {
685 config_error(config, "Could not copy path");
686 goto out;
687 }
688 } else if (streq(line->key, "is_dir")) {
689 is_dir = parse_bool(line->value, false0);
690 has_is_dir = true1;
691 } else if (streq(line->key, "is_file")) {
692 is_file = parse_bool(line->value, false0);
693 has_is_file = true1;
694 } else {
695 config_error(config, "Unexpected key: %s", line->key);
696 goto out;
697 }
698
699 break;
700 }
701 }
702
703out:
704 free(path);
705}
706
707static void parse_condition_accept_encoding(struct pattern *pattern,
708 struct config *config)
709{
710 const struct config_line *line;
711
712 while ((line = config_read_line(config))) {
713 switch (line->type) {
714 case CONFIG_LINE_TYPE_SECTION:
715 config_error(config, "Unexpected section: %s", line->key);
716 return;
717
718 case CONFIG_LINE_TYPE_SECTION_END:
719 pattern->flags |= PATTERN_COND_ACCEPT_ENCODING;
720 return;
721
722 case CONFIG_LINE_TYPE_LINE:
723 if (streq(line->key, "deflate")) {
724 if (parse_bool(line->value, false0))
725 pattern->condition.request_flags |= REQUEST_ACCEPT_DEFLATE;
726 } else if (streq(line->key, "gzip")) {
727 if (parse_bool(line->value, false0))
728 pattern->condition.request_flags |= REQUEST_ACCEPT_GZIP;
729 } else if (streq(line->key, "brotli")) {
730 if (parse_bool(line->value, false0))
731 pattern->condition.request_flags |= REQUEST_ACCEPT_BROTLI;
732 } else if (streq(line->key, "zstd")) {
733 if (parse_bool(line->value, false0))
734 pattern->condition.request_flags |= REQUEST_ACCEPT_ZSTD;
735 } else if (!streq(line->key, "none")) {
736 config_error(config, "Unsupported encoding for condition: %s",
737 line->key);
738 return;
739 }
740 break;
741 }
742 }
743}
744
745static void parse_condition_backref(struct pattern *pattern,
746 struct config *config)
747{
748 const struct config_line *line;
749 int index = -1;
750 char *str = NULL((void*)0);
751
752 while ((line = config_read_line(config))) {
753 switch (line->type) {
754 case CONFIG_LINE_TYPE_SECTION:
755 config_error(config, "Unexpected section: %s", line->key);
756 goto out;
757
758 case CONFIG_LINE_TYPE_SECTION_END:
759 if (index < 0) {
760 config_error(config, "Backref index not provided");
761 goto out;
762 }
763
764 pattern->flags |= PATTERN_COND_BACKREF;
765 pattern->condition.backref.index = index;
766 pattern->condition.backref.str = str;
767 return;
768
769 case CONFIG_LINE_TYPE_LINE:
770 index = parse_int(line->key, -1);
771 if (index < 0) {
772 config_error(config, "Expecting backref index, got ``%s''", line->key);
773 goto out;
774 }
775
776 free(str);
777 str = strdup(line->value);
778 if (!str) {
779 lwan_status_critical(lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 780, __FUNCTION__, "Couldn't allocate memory for backref key"
)
780 "Couldn't allocate memory for backref key")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 780, __FUNCTION__, "Couldn't allocate memory for backref key"
)
;
781 }
782
783 break;
784 }
785 }
786
787out:
788 free(str);
789}
790
791static bool_Bool get_method_from_string(struct pattern *pattern, const char *string)
792{
793#define GENERATE_CMP(upper, lower, mask, constant, probability) \
794 if (strcaseequal_neutral(string, #upper)) { \
795 pattern->condition.request_flags |= (mask); \
796 return true1; \
797 }
798
799 FOR_EACH_REQUEST_METHOD(GENERATE_CMP)GENERATE_CMP(GET, get, (1 << 0), (((uint32_t)(('G') | (
'E') << 8 | ('T') << 16 | (' ') << 24))), 0.6
) GENERATE_CMP(POST, post, (1 << 3 | 1 << 1 | 1 <<
0), (((uint32_t)(('P') | ('O') << 8 | ('S') << 16
| ('T') << 24))), 0.2) GENERATE_CMP(HEAD, head, (1 <<
1), (((uint32_t)(('H') | ('E') << 8 | ('A') << 16
| ('D') << 24))), 0.2) GENERATE_CMP(OPTIONS, options, (
1 << 2), (((uint32_t)(('O') | ('P') << 8 | ('T') <<
16 | ('I') << 24))), 0.1) GENERATE_CMP(DELETE, delete,
(1 << 1 | 1 << 2), (((uint32_t)(('D') | ('E') <<
8 | ('L') << 16 | ('E') << 24))), 0.1) GENERATE_CMP
(PUT, put, (1 << 3 | 1 << 2 | 1 << 0), (((uint32_t
)(('P') | ('U') << 8 | ('T') << 16 | (' ') <<
24))), 0.1)
800
801#undef GENERATE_CMP
802
803 return false0;
804}
805
806static void parse_condition(struct pattern *pattern,
807 struct config *config,
808 const struct config_line *line)
809{
810 if (streq(line->value, "cookie")) {
12
Taking true branch
811 return parse_condition_key_value(pattern, &pattern->condition.cookie,
13
Calling 'parse_condition_key_value'
812 PATTERN_COND_COOKIE, config, line);
813 }
814 if (streq(line->value, "query")) {
815 return parse_condition_key_value(pattern, &pattern->condition.query_var,
816 PATTERN_COND_QUERY_VAR, config, line);
817 }
818 if (streq(line->value, "post")) {
819 return parse_condition_key_value(pattern, &pattern->condition.post_var,
820 PATTERN_COND_POST_VAR, config, line);
821 }
822 if (streq(line->value, "environment")) {
823 return parse_condition_key_value(pattern, &pattern->condition.env_var,
824 PATTERN_COND_ENV_VAR, config, line);
825 }
826 if (streq(line->value, "header")) {
827 return parse_condition_key_value(pattern, &pattern->condition.header,
828 PATTERN_COND_HEADER, config, line);
829 }
830 if (streq(line->value, "stat")) {
831 return parse_condition_stat(pattern, config, line);
832 }
833 if (streq(line->value, "encoding")) {
834 return parse_condition_accept_encoding(pattern, config);
835 }
836 if (streq(line->value, "backref")) {
837 return parse_condition_backref(pattern, config);
838 }
839
840 config_error(config, "Condition `%s' not supported", line->value);
841}
842
843static bool_Bool rewrite_parse_conf_pattern(struct private_data *pd,
844 struct config *config,
845 const struct config_line *line)
846{
847 struct pattern *pattern;
848 char *redirect_to = NULL((void*)0), *rewrite_as = NULL((void*)0);
849 bool_Bool expand_with_lua = false0;
850
851 pattern = pattern_array_append0(&pd->patterns);
852 if (!pattern
4.1
'pattern' is non-null
)
5
Taking false branch
853 goto out_no_free;
854
855 pattern->pattern = strdup(line->value);
856 if (!pattern->pattern)
6
Assuming field 'pattern' is non-null
7
Taking false branch
857 goto out;
858
859 while ((line = config_read_line(config))) {
8
Loop condition is true. Entering loop body
860 switch (line->type) {
9
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 909
861 case CONFIG_LINE_TYPE_LINE:
862 if (streq(line->key, "redirect_to")) {
863 free(redirect_to);
864
865 redirect_to = strdup(line->value);
866 if (!redirect_to)
867 goto out;
868 } else if (streq(line->key, "rewrite_as")) {
869 free(rewrite_as);
870
871 rewrite_as = strdup(line->value);
872 if (!rewrite_as)
873 goto out;
874 } else if (streq(line->key, "expand_with_lua")) {
875 expand_with_lua = parse_bool(line->value, false0);
876 } else if (streq(line->key, "condition_proxied")) {
877 if (parse_bool(line->value, false0))
878 pattern->flags |= PATTERN_COND_PROXIED;
879 } else if (streq(line->key, "condition_http_1.0")) {
880 if (parse_bool(line->value, false0))
881 pattern->flags |= PATTERN_COND_HTTP10;
882 } else if (streq(line->key, "condition_has_query_string")) {
883 if (parse_bool(line->value, false0))
884 pattern->flags |= PATTERN_COND_HAS_QUERY_STRING;
885 } else if (streq(line->key, "condition_is_https")) {
886 if (parse_bool(line->value, false0))
887 pattern->flags |= PATTERN_COND_HTTPS__IS_HTTPS;
888 pattern->flags |= PATTERN_COND_HTTPS;
889 } else if (streq(line->key, "condition_method")) {
890 if (!get_method_from_string(pattern, line->value)) {
891 config_error(config, "Unknown HTTP method: %s", line->value);
892 goto out;
893 }
894 pattern->flags |= PATTERN_COND_METHOD;
895 } else
896#ifdef LWAN_HAVE_LUA
897 if (streq(line->key, "condition_lua")) {
898 pattern->condition.lua.script = strdup(line->value);
899 if (!pattern->condition.lua.script)
900 lwan_status_critical("Couldn't copy Lua script")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 900, __FUNCTION__, "Couldn't copy Lua script")
;
901 pattern->flags |= PATTERN_COND_LUA;
902 } else
903#endif
904 {
905 config_error(config, "Unexpected key: %s", line->key);
906 goto out;
907 }
908 break;
909 case CONFIG_LINE_TYPE_SECTION:
910 if (streq(line->key, "condition")) {
10
Taking true branch
911 parse_condition(pattern, config, line);
11
Calling 'parse_condition'
912 } else {
913 config_error(config, "Unexpected section: %s", line->key);
914 }
915 break;
916 case CONFIG_LINE_TYPE_SECTION_END:
917 if (redirect_to && rewrite_as) {
918 config_error(
919 config,
920 "`redirect to` and `rewrite as` are mutually exclusive");
921 goto out;
922 }
923 if (redirect_to) {
924 pattern->expand_pattern = redirect_to;
925 pattern->flags |= PATTERN_HANDLE_REDIRECT;
926 } else if (rewrite_as) {
927 pattern->expand_pattern = rewrite_as;
928 pattern->flags |= PATTERN_HANDLE_REWRITE;
929 } else {
930 config_error(
931 config,
932 "either `redirect to` or `rewrite as` are required");
933 goto out;
934 }
935 if (expand_with_lua) {
936#ifdef LWAN_HAVE_LUA
937 pattern->flags |= PATTERN_EXPAND_LUA;
938#else
939 config_error(config, "Lwan has been built without Lua. "
940 "`expand_with_lua` is not available");
941 goto out;
942#endif
943 } else {
944 pattern->flags |= PATTERN_EXPAND_LWAN;
945 }
946
947 return true1;
948 }
949 }
950
951out:
952 free(pattern->pattern);
953 free(redirect_to);
954 free(rewrite_as);
955out_no_free:
956 config_error(config, "Could not copy pattern");
957 return false0;
958}
959
960static bool_Bool rewrite_parse_conf(void *instance, struct config *config)
961{
962 struct private_data *pd = instance;
963 const struct config_line *line;
964
965 while ((line = config_read_line(config))) {
1
Loop condition is true. Entering loop body
966 switch (line->type) {
2
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 970
967 case CONFIG_LINE_TYPE_LINE:
968 config_error(config, "Unknown option: %s", line->key);
969 break;
970 case CONFIG_LINE_TYPE_SECTION:
971 if (streq(line->key, "pattern")) {
3
Taking true branch
972 rewrite_parse_conf_pattern(pd, config, line);
4
Calling 'rewrite_parse_conf_pattern'
973 } else {
974 config_error(config, "Unknown section: %s", line->key);
975 }
976 break;
977 case CONFIG_LINE_TYPE_SECTION_END:
978 break;
979 }
980 }
981
982 return !config_last_error(config);
983}
984
985static const struct lwan_module module = {
986 .create = rewrite_create,
987 .create_from_hash = rewrite_create_from_hash,
988 .parse_conf = rewrite_parse_conf,
989 .destroy = rewrite_destroy,
990 .handle_request = rewrite_handle_request,
991 .flags = HANDLER_CAN_REWRITE_URL
992};
993
994LWAN_REGISTER_MODULE(rewrite, &module)const struct lwan_module_info __attribute__((used, section("lwan_module"
))) lwan_module_info_rewrite = {.name = "rewrite", .module = &
module}
;