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

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       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();
1.1.1.3 ! misho     106:        buffer_copy_buffer(kvb->ptr[kvb->used]->value, value);
1.1       misho     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];
1.1.1.3 ! misho     151: 
        !           152:                        if (NULL == s) continue;
        !           153: 
1.1       misho     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);
1.1.1.3 ! misho     199:                                return HANDLER_ERROR;
1.1       misho     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 */
1.1.1.2   misho     251:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     252: #else
                    253:        UNUSED(p_d);
                    254: #endif
                    255: 
                    256:        for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho     257:                data_config const* config = (data_config const*)srv->config_context->data[i];
1.1       misho     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: 
1.1.1.3 ! misho     267:                if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1       misho     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
1.1.1.3 ! misho     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);
1.1       misho     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: 
1.1.1.3 ! misho     348: static handler_t process_rewrite_rules(server *srv, connection *con, plugin_data *p, rewrite_rule_buffer *kvb) {
1.1       misho     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: 
1.1.1.3 ! misho     365:        buffer_copy_buffer(p->match_buf, con->request.uri);
1.1       misho     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;
1.1.1.3 ! misho     378:                pattern_len = buffer_string_length(rule->value);
1.1       misho     379: 
1.1.1.3 ! misho     380:                if ((n = pcre_exec(match, NULL, CONST_BUF_LEN(p->match_buf), 0, 0, ovec, 3 * N)) < 0) {
1.1       misho     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:                        }
1.1.1.3 ! misho     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;
1.1       misho     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>