File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_rewrite.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>