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

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 */
1.1.1.2 ! misho     245:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     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:                /* condition didn't match */
                    300:                if (!config_check_cond(srv, con, dc)) continue;
                    301: 
                    302:                /* merge config */
                    303:                for (j = 0; j < dc->value->used; j++) {
                    304:                        data_unset *du = dc->value->data[j];
                    305: 
                    306:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite"))) {
                    307:                                PATCH(rewrite);
                    308:                                p->conf.context = dc;
                    309:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-once"))) {
                    310:                                PATCH(rewrite);
                    311:                                p->conf.context = dc;
                    312:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat"))) {
                    313:                                PATCH(rewrite);
                    314:                                p->conf.context = dc;
                    315:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-if-not-file"))) {
                    316:                                PATCH(rewrite_NF);
                    317:                                p->conf.context_NF = dc;
                    318:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat-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-final"))) {
                    322:                                PATCH(rewrite);
                    323:                                p->conf.context = dc;
                    324:                        }
                    325:                }
                    326:        }
                    327: 
                    328:        return 0;
                    329: }
                    330: 
                    331: URIHANDLER_FUNC(mod_rewrite_con_reset) {
                    332:        plugin_data *p = p_d;
                    333: 
                    334:        UNUSED(srv);
                    335: 
                    336:        if (con->plugin_ctx[p->id]) {
                    337:                handler_ctx_free(con->plugin_ctx[p->id]);
                    338:                con->plugin_ctx[p->id] = NULL;
                    339:        }
                    340: 
                    341:        return HANDLER_GO_ON;
                    342: }
                    343: 
                    344: static int process_rewrite_rules(server *srv, connection *con, plugin_data *p, rewrite_rule_buffer *kvb) {
                    345:        size_t i;
                    346:        handler_ctx *hctx;
                    347: 
                    348:        if (con->plugin_ctx[p->id]) {
                    349:                hctx = con->plugin_ctx[p->id];
                    350: 
                    351:                if (hctx->loops++ > 100) {
                    352:                        log_error_write(srv, __FILE__, __LINE__,  "s",
                    353:                                        "ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, perhaps you want to use url.rewrite-once instead of url.rewrite-repeat");
                    354: 
                    355:                        return HANDLER_ERROR;
                    356:                }
                    357: 
                    358:                if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON;
                    359:        }
                    360: 
                    361:        buffer_copy_string_buffer(p->match_buf, con->request.uri);
                    362: 
                    363:        for (i = 0; i < kvb->used; i++) {
                    364:                pcre *match;
                    365:                const char *pattern;
                    366:                size_t pattern_len;
                    367:                int n;
                    368:                rewrite_rule *rule = kvb->ptr[i];
                    369: # define N 10
                    370:                int ovec[N * 3];
                    371: 
                    372:                match       = rule->key;
                    373:                pattern     = rule->value->ptr;
                    374:                pattern_len = rule->value->used - 1;
                    375: 
                    376:                if ((n = pcre_exec(match, NULL, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) {
                    377:                        if (n != PCRE_ERROR_NOMATCH) {
                    378:                                log_error_write(srv, __FILE__, __LINE__, "sd",
                    379:                                                "execution error while matching: ", n);
                    380:                                return HANDLER_ERROR;
                    381:                        }
                    382:                } else {
                    383:                        const char **list;
                    384:                        size_t start;
                    385:                        size_t k;
                    386: 
                    387:                        /* it matched */
                    388:                        pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list);
                    389: 
                    390:                        /* search for $[0-9] */
                    391: 
                    392:                        buffer_reset(con->request.uri);
                    393: 
                    394:                        start = 0;
                    395:                        for (k = 0; k+1 < pattern_len; k++) {
                    396:                                if (pattern[k] == '$' || pattern[k] == '%') {
                    397:                                        /* got one */
                    398: 
                    399:                                        size_t num = pattern[k + 1] - '0';
                    400: 
                    401:                                        buffer_append_string_len(con->request.uri, pattern + start, k - start);
                    402: 
                    403:                                        if (!isdigit((unsigned char)pattern[k + 1])) {
                    404:                                                /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
                    405:                                                buffer_append_string_len(con->request.uri, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2);
                    406:                                        } else if (pattern[k] == '$') {
                    407:                                                /* n is always > 0 */
                    408:                                                if (num < (size_t)n) {
                    409:                                                        buffer_append_string(con->request.uri, list[num]);
                    410:                                                }
                    411:                                        } else if (p->conf.context == NULL) {
                    412:                                                /* we have no context, we are global */
                    413:                                                log_error_write(srv, __FILE__, __LINE__, "sb",
                    414:                                                                "used a redirect containing a %[0-9]+ in the global scope, ignored:",
                    415:                                                                rule->value);
                    416: 
                    417:                                        } else {
                    418:                                                config_append_cond_match_buffer(con, p->conf.context, con->request.uri, num);
                    419:                                        }
                    420: 
                    421:                                        k++;
                    422:                                        start = k + 1;
                    423:                                }
                    424:                        }
                    425: 
                    426:                        buffer_append_string_len(con->request.uri, pattern + start, pattern_len - start);
                    427: 
                    428:                        pcre_free(list);
                    429: 
                    430:                        if (con->plugin_ctx[p->id] == NULL) {
                    431:                                hctx = handler_ctx_init();
                    432:                                con->plugin_ctx[p->id] = hctx;
                    433:                        } else {
                    434:                                hctx = con->plugin_ctx[p->id];
                    435:                        }
                    436: 
                    437:                        if (rule->once) hctx->state = REWRITE_STATE_FINISHED;
                    438: 
                    439:                        return HANDLER_COMEBACK;
                    440:                }
                    441: #undef N
                    442:        }
                    443: 
                    444:        return HANDLER_GO_ON;
                    445: }
                    446: 
                    447: URIHANDLER_FUNC(mod_rewrite_physical) {
                    448:        plugin_data *p = p_d;
                    449:        handler_t r;
                    450:        stat_cache_entry *sce;
                    451: 
                    452:        if (con->mode != DIRECT) return HANDLER_GO_ON;
                    453: 
                    454:        mod_rewrite_patch_connection(srv, con, p);
                    455:        p->conf.context = p->conf.context_NF;
                    456: 
                    457:        if (!p->conf.rewrite_NF) return HANDLER_GO_ON;
                    458: 
                    459:        /* skip if physical.path is a regular file */
                    460:        sce = NULL;
                    461:        if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
                    462:                if (S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
                    463:        }
                    464: 
                    465:        switch(r = process_rewrite_rules(srv, con, p, p->conf.rewrite_NF)) {
                    466:        case HANDLER_COMEBACK:
                    467:                buffer_reset(con->physical.path);
                    468:        default:
                    469:                return r;
                    470:        }
                    471: 
                    472:        return HANDLER_GO_ON;
                    473: }
                    474: 
                    475: URIHANDLER_FUNC(mod_rewrite_uri_handler) {
                    476:        plugin_data *p = p_d;
                    477: 
                    478:        mod_rewrite_patch_connection(srv, con, p);
                    479: 
                    480:        if (!p->conf.rewrite) return HANDLER_GO_ON;
                    481: 
                    482:        return process_rewrite_rules(srv, con, p, p->conf.rewrite);
                    483: 
                    484:        return HANDLER_GO_ON;
                    485: }
                    486: #endif
                    487: 
                    488: int mod_rewrite_plugin_init(plugin *p);
                    489: int mod_rewrite_plugin_init(plugin *p) {
                    490:        p->version     = LIGHTTPD_VERSION_ID;
                    491:        p->name        = buffer_init_string("rewrite");
                    492: 
                    493: #ifdef HAVE_PCRE_H
                    494:        p->init        = mod_rewrite_init;
                    495:        /* it has to stay _raw as we are matching on uri + querystring
                    496:         */
                    497: 
                    498:        p->handle_uri_raw = mod_rewrite_uri_handler;
                    499:        p->handle_physical = mod_rewrite_physical;
                    500:        p->cleanup     = mod_rewrite_free;
                    501:        p->connection_reset = mod_rewrite_con_reset;
                    502: #endif
                    503:        p->set_defaults = mod_rewrite_set_defaults;
                    504: 
                    505:        p->data        = NULL;
                    506: 
                    507:        return 0;
                    508: }

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