• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/lighttpd-1.4.39/src/
1#include "base.h"
2#include "log.h"
3#include "buffer.h"
4
5#include "plugin.h"
6#include "stat_cache.h"
7
8#include <ctype.h>
9#include <stdlib.h>
10#include <string.h>
11
12#ifdef HAVE_PCRE_H
13typedef struct {
14	pcre *key;
15
16	buffer *value;
17
18	int once;
19} rewrite_rule;
20
21typedef struct {
22	rewrite_rule **ptr;
23
24	size_t used;
25	size_t size;
26} rewrite_rule_buffer;
27
28typedef struct {
29	rewrite_rule_buffer *rewrite;
30	rewrite_rule_buffer *rewrite_NF;
31	data_config *context, *context_NF; /* to which apply me */
32} plugin_config;
33
34typedef struct {
35	enum { REWRITE_STATE_UNSET, REWRITE_STATE_FINISHED} state;
36	int loops;
37} handler_ctx;
38
39typedef struct {
40	PLUGIN_DATA;
41	buffer *match_buf;
42
43	plugin_config **config_storage;
44
45	plugin_config conf;
46} plugin_data;
47
48static handler_ctx * handler_ctx_init(void) {
49	handler_ctx * hctx;
50
51	hctx = calloc(1, sizeof(*hctx));
52
53	hctx->state = REWRITE_STATE_UNSET;
54	hctx->loops = 0;
55
56	return hctx;
57}
58
59static void handler_ctx_free(handler_ctx *hctx) {
60	free(hctx);
61}
62
63static rewrite_rule_buffer *rewrite_rule_buffer_init(void) {
64	rewrite_rule_buffer *kvb;
65
66	kvb = calloc(1, sizeof(*kvb));
67
68	return kvb;
69}
70
71static int rewrite_rule_buffer_append(rewrite_rule_buffer *kvb, buffer *key, buffer *value, int once) {
72	size_t i;
73	const char *errptr;
74	int erroff;
75
76	if (!key) return -1;
77
78	if (kvb->size == 0) {
79		kvb->size = 4;
80		kvb->used = 0;
81
82		kvb->ptr = malloc(kvb->size * sizeof(*kvb->ptr));
83
84		for(i = 0; i < kvb->size; i++) {
85			kvb->ptr[i] = calloc(1, sizeof(**kvb->ptr));
86		}
87	} else if (kvb->used == kvb->size) {
88		kvb->size += 4;
89
90		kvb->ptr = realloc(kvb->ptr, kvb->size * sizeof(*kvb->ptr));
91
92		for(i = kvb->used; i < kvb->size; i++) {
93			kvb->ptr[i] = calloc(1, sizeof(**kvb->ptr));
94		}
95	}
96
97	if (NULL == (kvb->ptr[kvb->used]->key = pcre_compile(key->ptr,
98							    0, &errptr, &erroff, NULL))) {
99
100		return -1;
101	}
102
103	kvb->ptr[kvb->used]->value = buffer_init();
104	buffer_copy_buffer(kvb->ptr[kvb->used]->value, value);
105	kvb->ptr[kvb->used]->once = once;
106
107	kvb->used++;
108
109	return 0;
110}
111
112static void rewrite_rule_buffer_free(rewrite_rule_buffer *kvb) {
113	size_t i;
114
115	for (i = 0; i < kvb->size; i++) {
116		if (kvb->ptr[i]->key) pcre_free(kvb->ptr[i]->key);
117		if (kvb->ptr[i]->value) buffer_free(kvb->ptr[i]->value);
118		free(kvb->ptr[i]);
119	}
120
121	if (kvb->ptr) free(kvb->ptr);
122
123	free(kvb);
124}
125
126
127INIT_FUNC(mod_rewrite_init) {
128	plugin_data *p;
129
130	p = calloc(1, sizeof(*p));
131
132	p->match_buf = buffer_init();
133
134	return p;
135}
136
137FREE_FUNC(mod_rewrite_free) {
138	plugin_data *p = p_d;
139
140	UNUSED(srv);
141
142	if (!p) return HANDLER_GO_ON;
143
144	buffer_free(p->match_buf);
145	if (p->config_storage) {
146		size_t i;
147		for (i = 0; i < srv->config_context->used; i++) {
148			plugin_config *s = p->config_storage[i];
149
150			if (NULL == s) continue;
151
152			rewrite_rule_buffer_free(s->rewrite);
153			rewrite_rule_buffer_free(s->rewrite_NF);
154
155			free(s);
156		}
157		free(p->config_storage);
158	}
159
160	free(p);
161
162	return HANDLER_GO_ON;
163}
164
165static int parse_config_entry(server *srv, array *ca, rewrite_rule_buffer *kvb, const char *option, int once) {
166	data_unset *du;
167
168	if (NULL != (du = array_get_element(ca, option))) {
169		data_array *da;
170		size_t j;
171
172		if (du->type != TYPE_ARRAY) {
173			log_error_write(srv, __FILE__, __LINE__, "sss",
174					"unexpected type for key: ", option, "array of strings");
175
176			return HANDLER_ERROR;
177		}
178
179		da = (data_array *)du;
180
181		for (j = 0; j < da->value->used; j++) {
182			if (da->value->data[j]->type != TYPE_STRING) {
183				log_error_write(srv, __FILE__, __LINE__, "sssbs",
184						"unexpected type for key: ",
185						option,
186						"[", da->value->data[j]->key, "](string)");
187
188				return HANDLER_ERROR;
189			}
190
191			if (0 != rewrite_rule_buffer_append(kvb,
192							    ((data_string *)(da->value->data[j]))->key,
193							    ((data_string *)(da->value->data[j]))->value,
194							    once)) {
195				log_error_write(srv, __FILE__, __LINE__, "sb",
196						"pcre-compile failed for", da->value->data[j]->key);
197				return HANDLER_ERROR;
198			}
199		}
200	}
201
202	return 0;
203}
204#else
205static int parse_config_entry(server *srv, array *ca, const char *option) {
206	static int logged_message = 0;
207	if (logged_message) return 0;
208	if (NULL != array_get_element(ca, option)) {
209		logged_message = 1;
210		log_error_write(srv, __FILE__, __LINE__, "s",
211			"pcre support is missing, please install libpcre and the headers");
212	}
213	return 0;
214}
215#endif
216
217SETDEFAULTS_FUNC(mod_rewrite_set_defaults) {
218	size_t i = 0;
219
220	config_values_t cv[] = {
221		{ "url.rewrite-repeat",        NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
222		{ "url.rewrite-once",          NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
223
224		/* these functions only rewrite if the target is not already in the filestore
225		 *
226		 * url.rewrite-repeat-if-not-file is the equivalent of url.rewrite-repeat
227		 * url.rewrite-if-not-file is the equivalent of url.rewrite-once
228		 *
229		 */
230		{ "url.rewrite-repeat-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
231		{ "url.rewrite-if-not-file",        NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
232
233		/* old names, still supported
234		 *
235		 * url.rewrite remapped to url.rewrite-once
236		 * url.rewrite-final    is url.rewrite-once
237		 *
238		 */
239		{ "url.rewrite",               NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
240		{ "url.rewrite-final",         NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
241		{ NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
242	};
243
244#ifdef HAVE_PCRE_H
245	plugin_data *p = p_d;
246
247	if (!p) return HANDLER_ERROR;
248
249	/* 0 */
250	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
251#else
252	UNUSED(p_d);
253#endif
254
255	for (i = 0; i < srv->config_context->used; i++) {
256		data_config const* config = (data_config const*)srv->config_context->data[i];
257#ifdef HAVE_PCRE_H
258		plugin_config *s;
259
260		s = calloc(1, sizeof(plugin_config));
261		s->rewrite = rewrite_rule_buffer_init();
262		s->rewrite_NF = rewrite_rule_buffer_init();
263		p->config_storage[i] = s;
264#endif
265
266		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
267			return HANDLER_ERROR;
268		}
269
270#ifndef HAVE_PCRE_H
271# define parse_config_entry(srv, ca, x, option, y) parse_config_entry(srv, ca, option)
272#endif
273		parse_config_entry(srv, config->value, s->rewrite, "url.rewrite-once",      1);
274		parse_config_entry(srv, config->value, s->rewrite, "url.rewrite-final",     1);
275		parse_config_entry(srv, config->value, s->rewrite_NF, "url.rewrite-if-not-file",   1);
276		parse_config_entry(srv, config->value, s->rewrite_NF, "url.rewrite-repeat-if-not-file", 0);
277		parse_config_entry(srv, config->value, s->rewrite, "url.rewrite",           1);
278		parse_config_entry(srv, config->value, s->rewrite, "url.rewrite-repeat",    0);
279	}
280
281	return HANDLER_GO_ON;
282}
283
284#ifdef HAVE_PCRE_H
285
286#define PATCH(x) \
287	p->conf.x = s->x;
288static int mod_rewrite_patch_connection(server *srv, connection *con, plugin_data *p) {
289	size_t i, j;
290	plugin_config *s = p->config_storage[0];
291
292	PATCH(rewrite);
293	PATCH(rewrite_NF);
294	p->conf.context = NULL;
295	p->conf.context_NF = NULL;
296
297	/* skip the first, the global context */
298	for (i = 1; i < srv->config_context->used; i++) {
299		data_config *dc = (data_config *)srv->config_context->data[i];
300		s = p->config_storage[i];
301
302		/* condition didn't match */
303		if (!config_check_cond(srv, con, dc)) continue;
304
305		/* merge config */
306		for (j = 0; j < dc->value->used; j++) {
307			data_unset *du = dc->value->data[j];
308
309			if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite"))) {
310				PATCH(rewrite);
311				p->conf.context = dc;
312			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-once"))) {
313				PATCH(rewrite);
314				p->conf.context = dc;
315			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat"))) {
316				PATCH(rewrite);
317				p->conf.context = dc;
318			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-if-not-file"))) {
319				PATCH(rewrite_NF);
320				p->conf.context_NF = dc;
321			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat-if-not-file"))) {
322				PATCH(rewrite_NF);
323				p->conf.context_NF = dc;
324			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-final"))) {
325				PATCH(rewrite);
326				p->conf.context = dc;
327			}
328		}
329	}
330
331	return 0;
332}
333
334URIHANDLER_FUNC(mod_rewrite_con_reset) {
335	plugin_data *p = p_d;
336
337	UNUSED(srv);
338
339	if (con->plugin_ctx[p->id]) {
340		handler_ctx_free(con->plugin_ctx[p->id]);
341		con->plugin_ctx[p->id] = NULL;
342	}
343
344	return HANDLER_GO_ON;
345}
346
347static int process_rewrite_rules(server *srv, connection *con, plugin_data *p, rewrite_rule_buffer *kvb) {
348	size_t i;
349	handler_ctx *hctx;
350
351	if (con->plugin_ctx[p->id]) {
352		hctx = con->plugin_ctx[p->id];
353
354		if (hctx->loops++ > 100) {
355			log_error_write(srv, __FILE__, __LINE__,  "s",
356					"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, perhaps you want to use url.rewrite-once instead of url.rewrite-repeat");
357
358			return HANDLER_ERROR;
359		}
360
361		if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON;
362	}
363
364	buffer_copy_buffer(p->match_buf, con->request.uri);
365
366	for (i = 0; i < kvb->used; i++) {
367		pcre *match;
368		const char *pattern;
369		size_t pattern_len;
370		int n;
371		rewrite_rule *rule = kvb->ptr[i];
372# define N 10
373		int ovec[N * 3];
374
375		match       = rule->key;
376		pattern     = rule->value->ptr;
377		pattern_len = buffer_string_length(rule->value);
378
379		if ((n = pcre_exec(match, NULL, CONST_BUF_LEN(p->match_buf), 0, 0, ovec, 3 * N)) < 0) {
380			if (n != PCRE_ERROR_NOMATCH) {
381				log_error_write(srv, __FILE__, __LINE__, "sd",
382						"execution error while matching: ", n);
383				return HANDLER_ERROR;
384			}
385		} else {
386			const char **list;
387			size_t start;
388			size_t k;
389
390			/* it matched */
391			pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list);
392
393			/* search for $[0-9] */
394
395			buffer_reset(con->request.uri);
396
397			start = 0;
398			for (k = 0; k+1 < pattern_len; k++) {
399				if (pattern[k] == '$' || pattern[k] == '%') {
400					/* got one */
401
402					size_t num = pattern[k + 1] - '0';
403
404					buffer_append_string_len(con->request.uri, pattern + start, k - start);
405
406					if (!isdigit((unsigned char)pattern[k + 1])) {
407						/* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
408						buffer_append_string_len(con->request.uri, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2);
409					} else if (pattern[k] == '$') {
410						/* n is always > 0 */
411						if (num < (size_t)n) {
412							buffer_append_string(con->request.uri, list[num]);
413						}
414					} else if (p->conf.context == NULL) {
415						/* we have no context, we are global */
416						log_error_write(srv, __FILE__, __LINE__, "sb",
417								"used a redirect containing a %[0-9]+ in the global scope, ignored:",
418								rule->value);
419
420					} else {
421						config_append_cond_match_buffer(con, p->conf.context, con->request.uri, num);
422					}
423
424					k++;
425					start = k + 1;
426				}
427			}
428
429			buffer_append_string_len(con->request.uri, pattern + start, pattern_len - start);
430
431			pcre_free(list);
432
433			if (con->plugin_ctx[p->id] == NULL) {
434				hctx = handler_ctx_init();
435				con->plugin_ctx[p->id] = hctx;
436			} else {
437				hctx = con->plugin_ctx[p->id];
438			}
439
440			if (rule->once) hctx->state = REWRITE_STATE_FINISHED;
441
442			return HANDLER_COMEBACK;
443		}
444#undef N
445	}
446
447	return HANDLER_GO_ON;
448}
449
450URIHANDLER_FUNC(mod_rewrite_physical) {
451	plugin_data *p = p_d;
452	handler_t r;
453	stat_cache_entry *sce;
454
455	if (con->mode != DIRECT) return HANDLER_GO_ON;
456
457	mod_rewrite_patch_connection(srv, con, p);
458	p->conf.context = p->conf.context_NF;
459
460	if (!p->conf.rewrite_NF) return HANDLER_GO_ON;
461
462	/* skip if physical.path is a regular file */
463	sce = NULL;
464	if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
465		if (S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
466	}
467
468	switch(r = process_rewrite_rules(srv, con, p, p->conf.rewrite_NF)) {
469	case HANDLER_COMEBACK:
470		buffer_reset(con->physical.path);
471	default:
472		return r;
473	}
474
475	return HANDLER_GO_ON;
476}
477
478URIHANDLER_FUNC(mod_rewrite_uri_handler) {
479	plugin_data *p = p_d;
480
481	mod_rewrite_patch_connection(srv, con, p);
482
483	if (!p->conf.rewrite) return HANDLER_GO_ON;
484
485	return process_rewrite_rules(srv, con, p, p->conf.rewrite);
486
487	return HANDLER_GO_ON;
488}
489#endif
490
491int mod_rewrite_plugin_init(plugin *p);
492int mod_rewrite_plugin_init(plugin *p) {
493	p->version     = LIGHTTPD_VERSION_ID;
494	p->name        = buffer_init_string("rewrite");
495
496#ifdef HAVE_PCRE_H
497	p->init        = mod_rewrite_init;
498	/* it has to stay _raw as we are matching on uri + querystring
499	 */
500
501	p->handle_uri_raw = mod_rewrite_uri_handler;
502	p->handle_physical = mod_rewrite_physical;
503	p->cleanup     = mod_rewrite_free;
504	p->connection_reset = mod_rewrite_con_reset;
505#endif
506	p->set_defaults = mod_rewrite_set_defaults;
507
508	p->data        = NULL;
509
510	return 0;
511}
512