static int brigade_pflatten(lua_State*L) { apr_bucket_brigade *bb = (apr_bucket_brigade*) CHECK_BUCKETBRIGADE_OBJECT(1); request_rec *r = CHECK_REQUEST_OBJECT(2); apr_size_t len = luaL_checkint(L,3); char* c; apr_status_t rc = apr_brigade_pflatten(bb, &c, &len, r->pool); if(rc==APR_SUCCESS) { lua_pushlstring(L,c,len); }else { lua_pushnil(L); } lua_pushinteger(L,rc); return 2; }
/* Return a pool-allocated NUL-terminated line, with CRLF stripped, * read from brigade 'bbin' using 'bbout' as temporary storage. */ static char *get_line(apr_bucket_brigade *bbout, apr_bucket_brigade *bbin, conn_rec *c, apr_pool_t *p) { apr_status_t rv; apr_size_t len; char *line; apr_brigade_cleanup(bbout); rv = apr_brigade_split_line(bbout, bbin, APR_BLOCK_READ, 8192); if (rv) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01977) "failed reading line from OCSP server"); return NULL; } rv = apr_brigade_pflatten(bbout, &line, &len, p); if (rv) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01978) "failed reading line from OCSP server"); return NULL; } if (len == 0) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02321) "empty response from OCSP server"); return NULL; } if (line[len-1] != APR_ASCII_LF) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01979) "response header line too long from OCSP server"); return NULL; } line[len-1] = '\0'; if (len > 1 && line[len-2] == APR_ASCII_CR) { line[len-2] = '\0'; } return line; }
static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) { apr_size_t bytes; apr_size_t len; apr_size_t fbytes; const char *buff; const char *nl = NULL; char *bflat; apr_bucket *b; apr_bucket *tmp_b; apr_bucket_brigade *tmp_bb = NULL; apr_status_t rv; subst_dir_conf *cfg = (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config, &substitute_module); substitute_module_ctx *ctx = f->ctx; /* * First time around? Create the saved bb that we used for each pass * through. Note that we can also get here when we explicitly clear ctx, * for error handling */ if (!ctx) { f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); /* * Create all the temporary brigades we need and reuse them to avoid * creating them over and over again from r->pool which would cost a * lot of memory in some cases. */ ctx->linebb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); ctx->linesbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); ctx->pattbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* * Everything to be passed to the next filter goes in * here, our pass brigade. */ ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* Create our temporary pool only once */ apr_pool_create(&(ctx->tpool), f->r->pool); apr_table_unset(f->r->headers_out, "Content-Length"); } /* * Shortcircuit processing */ if (APR_BRIGADE_EMPTY(bb)) return APR_SUCCESS; /* * Here's the concept: * Read in the data and look for newlines. Once we * find a full "line", add it to our working brigade. * If we've finished reading the brigade and we have * any left over data (not a "full" line), store that * for the next pass. * * Note: anything stored in ctx->linebb for sure does not have * a newline char, so we don't concat that bb with the * new bb, since we would spending time searching for the newline * in data we know it doesn't exist. So instead, we simply scan * our current bb and, if we see a newline, prepend ctx->linebb * to the front of it. This makes the code much less straight- * forward (otherwise we could APR_BRIGADE_CONCAT(ctx->linebb, bb) * and just scan for newlines and not bother with needing to know * when ctx->linebb needs to be reset) but also faster. We'll take * the speed. * * Note: apr_brigade_split_line would be nice here, but we * really can't use it since we need more control and we want * to re-use already read bucket data. * * See mod_include if still confused :) */ while ((b = APR_BRIGADE_FIRST(bb)) && (b != APR_BRIGADE_SENTINEL(bb))) { if (APR_BUCKET_IS_EOS(b)) { /* * if we see the EOS, then we need to pass along everything we * have. But if the ctx->linebb isn't empty, then we need to add * that to the end of what we'll be passing. */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); if (rv != APR_SUCCESS) goto err; if (fbytes > cfg->max_line_length) { rv = APR_ENOMEM; goto err; } tmp_b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); rv = do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool); if (rv != APR_SUCCESS) goto err; APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb); apr_brigade_cleanup(ctx->linebb); } APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); } /* * No need to handle FLUSH buckets separately as we call * ap_pass_brigade anyway at the end of the loop. */ else if (APR_BUCKET_IS_METADATA(b)) { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); } else { /* * We have actual "data" so read in as much as we can and start * scanning and splitting from our read buffer */ rv = apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ); if (rv != APR_SUCCESS || bytes == 0) { apr_bucket_delete(b); } else { int num = 0; while (bytes > 0) { nl = memchr(buff, APR_ASCII_LF, bytes); if (nl) { len = (apr_size_t) (nl - buff) + 1; /* split *after* the newline */ apr_bucket_split(b, len); /* * We've likely read more data, so bypass rereading * bucket data and continue scanning through this * buffer */ bytes -= len; buff += len; /* * we need b to be updated for future potential * splitting */ tmp_b = APR_BUCKET_NEXT(b); APR_BUCKET_REMOVE(b); /* * Hey, we found a newline! Don't forget the old * stuff that needs to be added to the front. So we * add the split bucket to the end, flatten the whole * bb, morph the whole shebang into a bucket which is * then added to the tail of the newline bb. */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { APR_BRIGADE_INSERT_TAIL(ctx->linebb, b); rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); if (rv != APR_SUCCESS) goto err; if (fbytes > cfg->max_line_length) { /* Avoid pflattening further lines, we will * abort later on anyway. */ rv = APR_ENOMEM; goto err; } b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); apr_brigade_cleanup(ctx->linebb); } rv = do_pattmatch(f, b, ctx->pattbb, ctx->tpool); if (rv != APR_SUCCESS) goto err; /* * Count how many buckets we have in ctx->passbb * so far. Yes, this is correct we count ctx->passbb * and not ctx->pattbb as we do not reset num on every * iteration. */ for (b = APR_BRIGADE_FIRST(ctx->pattbb); b != APR_BRIGADE_SENTINEL(ctx->pattbb); b = APR_BUCKET_NEXT(b)) { num++; } APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb); /* * If the number of buckets in ctx->passbb reaches an * "insane" level, we consume much memory for all the * buckets as such. So lets flush them down the chain * in this case and thus clear ctx->passbb. This frees * the buckets memory for further processing. * Usually this condition should not become true, but * it is a safety measure for edge cases. */ if (num > AP_MAX_BUCKETS) { b = apr_bucket_flush_create( f->r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); num = 0; apr_pool_clear(ctx->tpool); if (rv != APR_SUCCESS) goto err; } b = tmp_b; } else { /* * no newline in whatever is left of this buffer so * tuck data away and get next bucket */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->linebb, b); bytes = 0; } } } } if (!APR_BRIGADE_EMPTY(ctx->passbb)) { rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); if (rv != APR_SUCCESS) goto err; } apr_pool_clear(ctx->tpool); } /* Anything left we want to save/setaside for the next go-around */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { /* * Provide ap_save_brigade with an existing empty brigade * (ctx->linesbb) to avoid creating a new one. */ ap_save_brigade(f, &(ctx->linesbb), &(ctx->linebb), f->r->pool); tmp_bb = ctx->linebb; ctx->linebb = ctx->linesbb; ctx->linesbb = tmp_bb; } return APR_SUCCESS; err: if (rv == APR_ENOMEM) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01328) "Line too long, URI %s", f->r->uri); apr_pool_clear(ctx->tpool); return rv; }
int h2_h2_process_conn(conn_rec* c) { apr_status_t status; h2_ctx *ctx; if (c->master) { return DECLINED; } ctx = h2_ctx_get(c, 0); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn"); if (h2_ctx_is_task(ctx)) { /* our stream pseudo connection */ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, task, declined"); return DECLINED; } if (!ctx && c->keepalives == 0) { const char *proto = ap_get_protocol(c); if (APLOGctrace1(c)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, " "new connection using protocol '%s', direct=%d, " "tls acceptable=%d", proto, h2_allows_h2_direct(c), h2_is_acceptable_connection(c, 1)); } if (!strcmp(AP_PROTOCOL_HTTP1, proto) && h2_allows_h2_direct(c) && h2_is_acceptable_connection(c, 1)) { /* Fresh connection still is on http/1.1 and H2Direct is enabled. * Otherwise connection is in a fully acceptable state. * -> peek at the first 24 incoming bytes */ apr_bucket_brigade *temp; char *s = NULL; apr_size_t slen; temp = apr_brigade_create(c->pool, c->bucket_alloc); status = ap_get_brigade(c->input_filters, temp, AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03054) "h2_h2, error reading 24 bytes speculative"); apr_brigade_destroy(temp); return DECLINED; } apr_brigade_pflatten(temp, &s, &slen, c->pool); if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, direct mode detected"); if (!ctx) { ctx = h2_ctx_get(c, 1); } h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c"); } else { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, not detected in %d bytes: %s", (int)slen, s); } apr_brigade_destroy(temp); } } if (ctx) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "process_conn"); if (!h2_ctx_session_get(ctx)) { status = h2_conn_setup(ctx, c, NULL); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c, "conn_setup"); if (status != APR_SUCCESS) { h2_ctx_clear(c); return status; } } return h2_conn_run(ctx, c); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined"); return DECLINED; }
int h2_h2_process_conn(conn_rec* c) { h2_ctx *ctx = h2_ctx_get(c); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn"); if (h2_ctx_is_task(ctx)) { /* our stream pseudo connection */ return DECLINED; } if (h2_ctx_protocol_get(c)) { /* Something has been negotiated */ } else if (!strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c)) && h2_allows_h2_direct(c) && h2_is_acceptable_connection(c, 1)) { /* connection still is on http/1.1 and H2Direct is enabled. * Otherwise connection is in a fully acceptable state. * -> peek at the first 24 incoming bytes */ apr_bucket_brigade *temp; apr_status_t status; char *s = NULL; apr_size_t slen; temp = apr_brigade_create(c->pool, c->bucket_alloc); status = ap_get_brigade(c->input_filters, temp, AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, "h2_h2, error reading 24 bytes speculative"); apr_brigade_destroy(temp); return DECLINED; } apr_brigade_pflatten(temp, &s, &slen, c->pool); if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, direct mode detected"); h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c"); } else { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, not detected in %d bytes: %s", (int)slen, s); } apr_brigade_destroy(temp); } else { /* the connection is not HTTP/1.1 or not for us, don't touch it */ return DECLINED; } /* If "h2" was selected as protocol (by whatever mechanism), take over * the connection. */ if (h2_ctx_is_active(ctx)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, connection, h2 active"); return h2_conn_process(c, NULL, ctx->server); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined"); return DECLINED; }
static apr_status_t line_edit_filter(ap_filter_t* f, apr_bucket_brigade* bb) { int i, j; unsigned int match ; unsigned int nmatch = 10 ; ap_regmatch_t pmatch[10] ; const char* bufp; const char* subs ; apr_size_t bytes ; apr_size_t fbytes ; apr_size_t offs ; const char* buf ; const char* le = NULL ; const char* le_n ; const char* le_r ; char* fbuf ; apr_bucket* b = APR_BRIGADE_FIRST(bb) ; apr_bucket* b1 ; int found = 0 ; apr_status_t rv ; apr_bucket_brigade* bbline ; line_edit_cfg* cfg = ap_get_module_config(f->r->per_dir_config, &line_edit_module) ; rewriterule* rules = (rewriterule*) cfg->rewriterules->elts ; rewriterule* newrule; line_edit_ctx* ctx = f->ctx ; if (ctx == NULL) { /* check env to see if we're wanted, to give basic control with 2.0 */ buf = apr_table_get(f->r->subprocess_env, "LineEdit"); if (buf && f->r->content_type) { char* lcbuf = apr_pstrdup(f->r->pool, buf) ; char* lctype = apr_pstrdup(f->r->pool, f->r->content_type) ; char* c ; for (c = lcbuf; *c; ++c) if (isupper(*c)) *c = tolower(*c) ; for (c = lctype; *c; ++c) if (isupper(*c)) *c = tolower(*c) ; else if (*c == ';') { *c = 0 ; break ; } if (!strstr(lcbuf, lctype)) { /* don't filter this content type */ ap_filter_t* fnext = f->next ; ap_remove_output_filter(f) ; return ap_pass_brigade(fnext, bb) ; } } ctx = f->ctx = apr_palloc(f->r->pool, sizeof(line_edit_ctx)) ; ctx->bbsave = apr_brigade_create(f->r->pool, f->c->bucket_alloc) ; /* If we have any regex matches, we'll need to copy everything, so we * have null-terminated strings to parse. That's a lot of memory if * we're streaming anything big. So we'll use (and reuse) a local * subpool. Fall back to the request pool if anything bad happens. */ ctx->lpool = f->r->pool ; for (i = 0; i < cfg->rewriterules->nelts; ++i) { if ( rules[i].flags & M_REGEX ) { if (apr_pool_create(&ctx->lpool, f->r->pool) != APR_SUCCESS) { ctx->lpool = f->r->pool ; } break ; } } /* If we have env interpolation, we'll need a private copy of * our rewrite rules with this requests env. Otherwise we can * save processing time by using the original. * * If one ENV is found, we also have to copy all previous and * subsequent rules, even those with no interpolation. */ ctx->rewriterules = cfg->rewriterules; for (i = 0; i < cfg->rewriterules->nelts; ++i) { found |= (rules[i].flags & M_ENV) ; if ( found ) { if (ctx->rewriterules == cfg->rewriterules) { ctx->rewriterules = apr_array_make(f->r->pool, cfg->rewriterules->nelts, sizeof(rewriterule)); for (j = 0; j < i; ++j) { newrule = apr_array_push (((line_edit_ctx*)ctx)->rewriterules) ; newrule->from = rules[j].from; newrule->to = rules[j].to; newrule->flags = rules[j].flags; newrule->length = rules[j].length; } } /* this rule needs to be interpolated */ newrule = apr_array_push (((line_edit_ctx*)ctx)->rewriterules) ; newrule->from = rules[i].from; if (rules[i].flags & M_ENV) { newrule->to = interpolate_env(f->r, rules[i].to); } else { newrule->to = rules[i].to ; } newrule->flags = rules[i].flags; newrule->length = rules[i].length; } } /* for back-compatibility with Apache 2.0, set some protocol stuff */ apr_table_unset(f->r->headers_out, "Content-Length") ; apr_table_unset(f->r->headers_out, "Content-MD5") ; apr_table_unset(f->r->headers_out, "Accept-Ranges") ; } /* by now our rules are in ctx->rewriterules */ rules = (rewriterule*) ctx->rewriterules->elts ; /* bbline is what goes to the next filter, * so we (can) have a new one each time. */ bbline = apr_brigade_create(f->r->pool, f->c->bucket_alloc) ; /* first ensure we have no mid-line breaks that might be in the * middle of a search string causing us to miss it! At the same * time we split into lines to avoid pattern-matching over big * chunks of memory. */ while ( b != APR_BRIGADE_SENTINEL(bb) ) { if ( !APR_BUCKET_IS_METADATA(b) ) { if ( apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS ) { if ( bytes == 0 ) { APR_BUCKET_REMOVE(b) ; } else while ( bytes > 0 ) { switch (cfg->lineend) { case LINEEND_UNIX: le = memchr(buf, '\n', bytes) ; break ; case LINEEND_MAC: le = memchr(buf, '\r', bytes) ; break ; case LINEEND_DOS: /* Edge-case issue: if a \r\n spans buckets it'll get missed. * Not a problem for present purposes, but would be an issue * if we claimed to support pattern matching on the lineends. */ found = 0 ; le = memchr(buf+1, '\n', bytes-1) ; while ( le && !found ) { if ( le[-1] == '\r' ) { found = 1 ; } else { le = memchr(le+1, '\n', bytes-1 - (le+1 - buf)) ; } } if ( !found ) le = 0 ; break; case LINEEND_ANY: case LINEEND_UNSET: /* Edge-case notabug: if a \r\n spans buckets it'll get seen as * two line-ends. It'll insert the \n as a one-byte bucket. */ le_n = memchr(buf, '\n', bytes) ; le_r = memchr(buf, '\r', bytes) ; if ( le_n != NULL ) if ( le_n == le_r + sizeof(char)) le = le_n ; else if ( (le_r < le_n) && (le_r != NULL) ) le = le_r ; else le = le_n ; else le = le_r ; break; case LINEEND_NONE: le = 0 ; break; case LINEEND_CUSTOM: le = memchr(buf, cfg->lechar, bytes) ; break; } if ( le ) { /* found a lineend in this bucket. */ offs = 1 + ((unsigned int)le-(unsigned int)buf) / sizeof(char) ; apr_bucket_split(b, offs) ; bytes -= offs ; buf += offs ; b1 = APR_BUCKET_NEXT(b) ; APR_BUCKET_REMOVE(b); /* Is there any previous unterminated content ? */ if ( !APR_BRIGADE_EMPTY(ctx->bbsave) ) { /* append this to any content waiting for a lineend */ APR_BRIGADE_INSERT_TAIL(ctx->bbsave, b) ; rv = apr_brigade_pflatten(ctx->bbsave, &fbuf, &fbytes, f->r->pool) ; /* make b a new bucket of the flattened stuff */ b = apr_bucket_pool_create(fbuf, fbytes, f->r->pool, f->r->connection->bucket_alloc) ; /* bbsave has been consumed, so clear it */ apr_brigade_cleanup(ctx->bbsave) ; } /* b now contains exactly one line */ APR_BRIGADE_INSERT_TAIL(bbline, b); b = b1 ; } else { /* no lineend found. Remember the dangling content */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->bbsave, b); bytes = 0 ; } } /* while bytes > 0 */ } else { /* bucket read failed - oops ! Let's remove it. */ APR_BUCKET_REMOVE(b); } } else if ( APR_BUCKET_IS_EOS(b) ) { /* If there's data to pass, send it in one bucket */ if ( !APR_BRIGADE_EMPTY(ctx->bbsave) ) { rv = apr_brigade_pflatten(ctx->bbsave, &fbuf, &fbytes, f->r->pool) ; b1 = apr_bucket_pool_create(fbuf, fbytes, f->r->pool, f->r->connection->bucket_alloc) ; APR_BRIGADE_INSERT_TAIL(bbline, b1); } apr_brigade_cleanup(ctx->bbsave) ; /* start again rather than segfault if a seriously buggy * filter in front of us sent a bogus EOS */ f->ctx = NULL ; /* move the EOS to the new brigade */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(bbline, b); } else { /* chop flush or unknown metadata bucket types */ apr_bucket_delete(b); } /* OK, reset pointer to what's left (since we're not in a for-loop) */ b = APR_BRIGADE_FIRST(bb) ; } /* OK, now we have a bunch of complete lines in bbline, * so we can apply our edit rules */ /* When we get a match, we split the line into before+match+after. * To flatten that back into one buf every time would be inefficient. * So we treat it as three separate bufs to apply future rules. * * We can only reasonably do that by looping over buckets *inside* * the loop over rules. * * That means concepts like one-match-per-line or start-of-line-only * won't work, except for the first rule. So we won't pretend. */ for (i = 0; i < ctx->rewriterules->nelts; ++i) { for ( b = APR_BRIGADE_FIRST(bbline) ; b != APR_BRIGADE_SENTINEL(bbline) ; b = APR_BUCKET_NEXT(b) ) { if ( !APR_BUCKET_IS_METADATA(b) && (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS)) { if ( rules[i].flags & M_REGEX ) { bufp = apr_pstrmemdup(ctx->lpool, buf, bytes) ; while ( ! ap_regexec(rules[i].from.r, bufp, nmatch, pmatch, 0) ) { match = pmatch[0].rm_so ; subs = ap_pregsub(f->r->pool, rules[i].to, bufp, nmatch, pmatch) ; apr_bucket_split(b, match) ; b1 = APR_BUCKET_NEXT(b) ; apr_bucket_split(b1, pmatch[0].rm_eo - match) ; b = APR_BUCKET_NEXT(b1) ; apr_bucket_delete(b1) ; b1 = apr_bucket_pool_create(subs, strlen(subs), f->r->pool, f->r->connection->bucket_alloc) ; APR_BUCKET_INSERT_BEFORE(b, b1) ; bufp += pmatch[0].rm_eo ; } } else { bufp = buf ; while (subs = apr_strmatch(rules[i].from.s, bufp, bytes), subs != NULL) { match = ((unsigned int)subs - (unsigned int)bufp) / sizeof(char) ; bytes -= match ; bufp += match ; apr_bucket_split(b, match) ; b1 = APR_BUCKET_NEXT(b) ; apr_bucket_split(b1, rules[i].length) ; b = APR_BUCKET_NEXT(b1) ; apr_bucket_delete(b1) ; bytes -= rules[i].length ; bufp += rules[i].length ; b1 = apr_bucket_immortal_create(rules[i].to, strlen(rules[i].to), f->r->connection->bucket_alloc) ; APR_BUCKET_INSERT_BEFORE(b, b1) ; } } } } /* If we used a local pool, clear it now */ if ( (ctx->lpool != f->r->pool) && (rules[i].flags & M_REGEX) ) { apr_pool_clear(ctx->lpool) ; } } /* now pass it down the chain */ rv = ap_pass_brigade(f->next, bbline) ; /* if we have leftover data, don't risk it going out of scope */ for ( b = APR_BRIGADE_FIRST(ctx->bbsave) ; b != APR_BRIGADE_SENTINEL(ctx->bbsave) ; b = APR_BUCKET_NEXT(b)) { apr_bucket_setaside(b, f->r->pool) ; } return rv ; }
/** * The output filter routine. This one gets called whenever a response is * generated that passes this filter. Returns APR_SUCCESS if everything works * out. * * @param f The filter definition. * @param bb The bucket brigade containing the data. */ static apr_status_t replace_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; conn_rec *c = r->connection; replace_ctx_t *ctx = f->ctx; apr_bucket *b; apr_size_t len; const char *data; const char *header; apr_status_t rv; int re_vector[RE_VECTOR_SIZE]; // 3 elements per matched pattern replace_pattern_t *next; header_replace_pattern_t *next_header; int modified = 0; // flag to determine if a replacement has // occured. if (!ctx) { /* Initialize context */ ctx = apr_pcalloc(f->r->pool, sizeof(replace_ctx_t)); f->ctx = ctx; ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc); } /* parse config settings */ /* look for the user-defined filter */ ctx->filter = find_filter_def(f->r->server, f->frec->name); if (!ctx->filter) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "couldn't find definition of filter '%s'", f->frec->name); return APR_EINVAL; } ctx->p = f->r->pool; if (ctx->filter->intype && ctx->filter->intype != INTYPE_ALL) { if (!f->r->content_type) { ctx->noop = 1; } else { const char *ctypes = f->r->content_type; const char *ctype = ap_getword(f->r->pool, &ctypes, ';'); if (strcasecmp(ctx->filter->intype, ctype)) { /* wrong IMT for us; don't mess with the output */ ctx->noop = 1; } } } /* exit immediately if there are indications that the filter shouldn't be * executed. */ if (ctx->noop == 1) { ap_pass_brigade(f->next, bb); return APR_SUCCESS; } /** * Loop through the configured header patterns. */ for (next_header = ctx->filter->header_pattern; next_header != NULL; next_header = next_header->next) { // create a separate table with the requested HTTP header entries and // unset those headers in the original request. apr_table_t *header_table; header_table = apr_table_make(r->pool, 2); // create a data structure for the callback function header_replace_cb_t *hrcb; hrcb = apr_palloc(r->pool, sizeof(header_replace_cb_t)); hrcb->header_table = header_table; hrcb->pattern = next_header->pattern; hrcb->extra = next_header->extra; hrcb->replacement = next_header->replacement; hrcb->r = r; // pass any header that is defined to be processed to the callback // function and unset those headers in the original outgoing record. apr_table_do(replace_header_cb, hrcb, r->headers_out, next_header->header, NULL); // only touch the header if the changed header table is not empty. if (!apr_is_empty_table(header_table)) { apr_table_unset(r->headers_out, next_header->header); // overlay the original header table with the new one to reintegrate // the changed headers. r->headers_out = apr_table_overlay(r->pool, r->headers_out, header_table); } } /* Not nice but neccessary: Unset the ETag , because we cannot adjust the * value correctly, because we do not know how. */ apr_table_unset(f->r->headers_out, "ETag"); int eos = 0; // flag to check if an EOS bucket is in the brigade. apr_bucket *eos_bucket; // Backup for the EOS bucket. /* Interate through the available data. Stop if there is an EOS */ for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { if (APR_BUCKET_IS_EOS(b)) { eos = 1; ap_save_brigade(f, &ctx->bb, &bb, ctx->p); APR_BUCKET_REMOVE(b); eos_bucket = b; break; } } /* If the iteration over the brigade hasn't found an EOS bucket, just save * the brigade and return. */ if (eos != 1) { ap_save_brigade(f, &ctx->bb, &bb, ctx->p); return APR_SUCCESS; } if ((rv = apr_brigade_pflatten(ctx->bb, (char **)&data, &len, ctx->p)) != APR_SUCCESS) { /* Return if the flattening didn't work. */ return rv; } else { /* Remove the original data from the bucket brigade. Otherwise it would * be passed twice (original data and the processed, flattened copy) to * the next filter. */ apr_brigade_cleanup(ctx->bb); } /* Good cast, we just tested len isn't negative or zero */ if (len > 0) { /* start checking for the regex's. */ for (next = ctx->filter->pattern; next != NULL; next = next->next) { int rc = 0; int offset = 0; /* loop through the configured patterns */ do { rc = pcre_exec(next->pattern, next->extra, data, len, offset, 0, re_vector, RE_VECTOR_SIZE); if (rc < 0 && rc != PCRE_ERROR_NOMATCH) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Matching Error %d", rc); return rc; } /* This shouldn´t happen */ if (rc == 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "PCRE output vector too small (%d)", RE_VECTOR_SIZE/3-1); } /* If the result count is greater than 0 then there are * matches in the data string. Thus we try to replace those * strings with the user provided string. */ if (rc > 0) { char *prefix; // the string before the matching part. char *postfix; // the string after the matching part. char *newdata; // the concatenated string of prefix, // the replaced string and postfix. char *replacement; // the string with the data to replace // (after the subpattern processing has // been done). char *to_replace[10]; // the string array containing the // strings that are to be replaced. int match_diff; // the difference between the matching // string and its replacement. int x; // a simple counter. char *pos; // the starting position within the // replacement string, where there is a // subpattern to replace. /* start with building the replacement string */ replacement = apr_pstrcat(ctx->p, next->replacement, NULL); /* look for the subpatterns \0 to \9 */ for (x = 0; x < rc && x < 10; x++) { /* extract the x'ths subpattern */ to_replace[x] = substr(data, re_vector[x*2], re_vector[x*2+1] - re_vector[x*2], r); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Found match: %s", to_replace[x]); /* the token ( \0 to \9) we are looking for */ char *token = apr_pstrcat(ctx->p, "\\", apr_itoa(ctx->p, x), NULL); /* allocate memory for the replacement operation */ char *tmp; if (!to_replace[x] || strlen(to_replace[x]) < 2) { tmp = malloc(strlen(replacement) + 1); } else { tmp = malloc(strlen(replacement) - 1 + strlen(to_replace[x])); } /* copy the replacement string to the new * location. */ memcpy(tmp, replacement, strlen(replacement) + 1); replacement = tmp; /* try to replace each occurence of the token with * its matched subpattern. */ pos = ap_strstr(replacement, token); while (pos) { if (!to_replace[x]) { break; } substr_replace(pos, to_replace[x], strlen(pos), strlen(to_replace[x])); if (strlen(to_replace[x]) < 2) { tmp = malloc(strlen(replacement) + 1); } else { tmp = malloc(strlen(replacement) - 1 + strlen(to_replace[x])); } memcpy(tmp, replacement, strlen(replacement) + 1); /* clean up. */ free(replacement); replacement = tmp; pos = ap_strstr(replacement, token); } } match_diff = strlen(replacement) - (re_vector[1] - re_vector[0]); /* Allocate memory for a buffer to copy the first part * of the data string up to (but not including) the * the matching pattern. */ prefix = apr_pcalloc(ctx->p, re_vector[0] + 1); if (prefix == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to allocate memory for prefix", NULL); return -1; } /* Copy the string from the offset (beginning of * pattern matching) to the first occurence of the * pattern and add a trailing \0. */ memcpy(prefix, data, (size_t)re_vector[0]); /* Copy the string from the end of the pattern to the * end of the data string itself. */ postfix = apr_pcalloc(ctx->p, len); if (postfix == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unable to allocate memory for postfix", NULL); return -1; } memcpy(postfix, (data + re_vector[1]), len - re_vector[1]); /* Create the new data string, replace the old one * and clean up. */ newdata = apr_pstrcat(ctx->p, prefix, replacement, postfix, NULL); /* update the point of the data and free the allocated * memory for the replacement string. */ data = newdata; free(replacement); /* Calculate the new offset in the data string, where * the new matching round is to begin. */ offset = re_vector[1] + match_diff; len += match_diff; modified = 1; } } while (rc > 0); } /* Adjust the real length of the processed data. */ if (apr_table_get(f->r->headers_out, "Content-Length") != NULL) { apr_table_set(f->r->headers_out, "Content-Length", apr_itoa(ctx->p, len)); } /* If an Entity Tag is set, change the mtime and generate a new ETag.*/ if (apr_table_get(f->r->headers_out, "ETag") != NULL) { r->mtime = time(NULL); ap_set_etag(r); } } /* Create a new bucket with the processed data, insert that one into our * brigade, then insert the saved EOS bucket at the end of the brigade * and pass the brigade to the next filter. */ APR_BRIGADE_INSERT_TAIL(ctx->bb, apr_bucket_transient_create(data, len, apr_bucket_alloc_create(ctx->p))); APR_BRIGADE_INSERT_TAIL(ctx->bb, eos_bucket); ap_pass_brigade(f->next, ctx->bb); return APR_SUCCESS; }
static apr_status_t google_analytics_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; google_analytics_filter_ctx *ctx = f->ctx; google_analytics_filter_config *c; apr_bucket *b = APR_BRIGADE_FIRST(bb); apr_size_t bytes; apr_size_t fbytes; apr_size_t offs; const char *buf; const char *le = NULL; const char *le_n; const char *le_r; const char *bufp; const char *subs; unsigned int match; apr_bucket *b1; char *fbuf; int found = 0; apr_status_t rv; apr_bucket_brigade *bbline; // サブリクエストならなにもしない if (r->main) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } c = ap_get_module_config(r->per_dir_config, &google_analytics_module); if (ctx == NULL) { ctx = f->ctx = apr_pcalloc(r->pool, sizeof(google_analytics_filter_ctx)); ctx->bbsave = apr_brigade_create(r->pool, f->c->bucket_alloc); } // length かわってしまうので unset で OK? apr_table_unset(r->headers_out, "Content-Length"); apr_table_unset(r->headers_out, "Content-MD5"); apr_table_unset(r->headers_out, "Accept-Ranges"); apr_table_unset(r->headers_out, "ETag"); bbline = apr_brigade_create(r->pool, f->c->bucket_alloc); // 改行毎なbucketに編成しなおす while ( b != APR_BRIGADE_SENTINEL(bb) ) { if ( !APR_BUCKET_IS_METADATA(b) ) { if ( apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS ) { if ( bytes == 0 ) { APR_BUCKET_REMOVE(b); } else { while ( bytes > 0 ) { le_n = memchr(buf, '\n', bytes); le_r = memchr(buf, '\r', bytes); if ( le_n != NULL ) { if ( le_n == le_r + sizeof(char)) { le = le_n; } else if ( (le_r < le_n) && (le_r != NULL) ) { le = le_r; } else { le = le_n; } } else { le = le_r; } if ( le ) { offs = 1 + ((unsigned int)le-(unsigned int)buf) / sizeof(char); apr_bucket_split(b, offs); bytes -= offs; buf += offs; b1 = APR_BUCKET_NEXT(b); APR_BUCKET_REMOVE(b); if ( !APR_BRIGADE_EMPTY(ctx->bbsave) ) { APR_BRIGADE_INSERT_TAIL(ctx->bbsave, b); rv = apr_brigade_pflatten(ctx->bbsave, &fbuf, &fbytes, r->pool); b = apr_bucket_pool_create(fbuf, fbytes, r->pool, r->connection->bucket_alloc); apr_brigade_cleanup(ctx->bbsave); } APR_BRIGADE_INSERT_TAIL(bbline, b); b = b1; } else { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->bbsave, b); bytes = 0; } } /* while bytes > 0 */ } } else { APR_BUCKET_REMOVE(b); } } else if ( APR_BUCKET_IS_EOS(b) ) { if ( !APR_BRIGADE_EMPTY(ctx->bbsave) ) { rv = apr_brigade_pflatten(ctx->bbsave, &fbuf, &fbytes, r->pool); b1 = apr_bucket_pool_create(fbuf, fbytes, r->pool, r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bbline, b1); } apr_brigade_cleanup(ctx->bbsave); f->ctx = NULL; APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(bbline, b); } else { apr_bucket_delete(b); } b = APR_BRIGADE_FIRST(bb); } // 改行毎なbucketをまわす for ( b = APR_BRIGADE_FIRST(bbline); b != APR_BRIGADE_SENTINEL(bbline); b = APR_BUCKET_NEXT(b) ) { if ( !APR_BUCKET_IS_METADATA(b) && (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS)) { bufp = buf; if (ap_regexec(regex_tag_exists, bufp, 0, NULL, 0) == 0) { break; } subs = apr_strmatch(pattern_body_end_tag, bufp, bytes); if (subs != NULL) { match = ((unsigned int)subs - (unsigned int)bufp) / sizeof(char); bytes -= match; bufp += match; apr_bucket_split(b, match); b1 = APR_BUCKET_NEXT(b); apr_bucket_split(b1, body_end_tag_length); b = APR_BUCKET_NEXT(b1); apr_bucket_delete(b1); bytes -= body_end_tag_length; bufp += body_end_tag_length; b1 = apr_bucket_immortal_create(c->replace, strlen(c->replace), r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, b1); } } } rv = ap_pass_brigade(f->next, bbline); for ( b = APR_BRIGADE_FIRST(ctx->bbsave); b != APR_BRIGADE_SENTINEL(ctx->bbsave); b = APR_BUCKET_NEXT(b)) { apr_bucket_setaside(b, r->pool); } return rv; }