Annotation of embedaddon/lighttpd/src/mod_rewrite.c, revision 1.1

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

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