File: | lib/lwan-mod-rewrite.c |
Warning: | line 639, column 5 Attempt to free released memory |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |||||
43 | enum 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 | |||||
88 | struct 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 | |||||
113 | 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" , 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 | |||||
115 | struct private_data { | ||||
116 | struct pattern_array patterns; | ||||
117 | }; | ||||
118 | |||||
119 | static 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 | |||||
136 | static 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 | |||||
153 | static __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 | |||||
162 | static 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 | |||||
220 | static 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 | ||||
230 | static void | ||||
231 | lua_close_defer(void *data) | ||||
232 | { | ||||
233 | lua_close((lua_State *)data); | ||||
234 | } | ||||
235 | |||||
236 | static 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 | |||||
286 | static 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 (p->condition.backref.index > captures) | ||||
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 | |||||
473 | static enum lwan_http_status | ||||
474 | rewrite_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 | |||||
522 | static 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 | |||||
535 | static 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 | |||||
580 | static 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 | |||||
587 | static 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))) { | ||||
596 | switch (line->type) { | ||||
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
| ||||
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); | ||||
621 | if (!key) { | ||||
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) { | ||||
629 | free(key); | ||||
630 | config_error(config, | ||||
631 | "Could not copy value while parsing condition"); | ||||
632 | goto out; | ||||
633 | } | ||||
634 | break; | ||||
635 | } | ||||
636 | } | ||||
637 | |||||
638 | out: | ||||
639 | free(key); | ||||
| |||||
640 | free(value); | ||||
641 | } | ||||
642 | |||||
643 | static 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 | |||||
703 | out: | ||||
704 | free(path); | ||||
705 | } | ||||
706 | |||||
707 | static 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 | |||||
745 | static 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 | |||||
787 | out: | ||||
788 | free(str); | ||||
789 | } | ||||
790 | |||||
791 | static 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 | |||||
806 | static void parse_condition(struct pattern *pattern, | ||||
807 | struct config *config, | ||||
808 | const struct config_line *line) | ||||
809 | { | ||||
810 | if (streq(line->value, "cookie")) { | ||||
811 | return parse_condition_key_value(pattern, &pattern->condition.cookie, | ||||
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 | |||||
843 | static 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
| ||||
853 | goto out_no_free; | ||||
854 | |||||
855 | pattern->pattern = strdup(line->value); | ||||
856 | if (!pattern->pattern) | ||||
857 | goto out; | ||||
858 | |||||
859 | while ((line = config_read_line(config))) { | ||||
860 | switch (line->type) { | ||||
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")) { | ||||
911 | parse_condition(pattern, config, line); | ||||
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 | |||||
951 | out: | ||||
952 | free(pattern->pattern); | ||||
953 | free(redirect_to); | ||||
954 | free(rewrite_as); | ||||
955 | out_no_free: | ||||
956 | config_error(config, "Could not copy pattern"); | ||||
957 | return false0; | ||||
958 | } | ||||
959 | |||||
960 | static 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))) { | ||||
| |||||
966 | switch (line->type) { | ||||
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")) { | ||||
972 | rewrite_parse_conf_pattern(pd, config, line); | ||||
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 | |||||
985 | static 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 | |||||
994 | LWAN_REGISTER_MODULE(rewrite, &module)const struct lwan_module_info __attribute__((used, section("lwan_module" ))) lwan_module_info_rewrite = {.name = "rewrite", .module = & module}; |