Ejemplo n.º 1
0
/* find_code_page() is a fixup hook that decides if translation should be
 * enabled; if so, it sets up request data for use by the filter registration
 * hook so that it knows what to do
 */
static int find_code_page(request_rec *r)
{
    charset_dir_t *dc = ap_get_module_config(r->per_dir_config,
                                             &charset_lite_module);
    charset_req_t *reqinfo;
    charset_filter_ctx_t *input_ctx, *output_ctx;
    apr_status_t rv;
    const char *mime_type;

    if (dc->debug >= DBGLVL_FLOW) {
        ap_log_rerror(APLOG_MARK,APLOG_DEBUG, 0, r,
                      "uri: %s file: %s method: %d "
                      "imt: %s flags: %s%s%s %s->%s",
                      r->uri, r->filename, r->method_number,
                      r->content_type ? r->content_type : "(unknown)",
                      r->main     ? "S" : "",    /* S if subrequest */
                      r->prev     ? "R" : "",    /* R if redirect */
                      r->proxyreq ? "P" : "",    /* P if proxy */
                      dc->charset_source, dc->charset_default);
    }

    /* If we don't have a full directory configuration, bail out.
     */
    if (!dc->charset_source || !dc->charset_default) {
        if (dc->debug >= DBGLVL_PMC) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                          "incomplete configuration: src %s, dst %s",
                          dc->charset_source ? dc->charset_source : "unspecified",
                          dc->charset_default ? dc->charset_default : "unspecified");
        }
        return DECLINED;
    }

    /* catch proxy requests */
    if (r->proxyreq) return DECLINED;
    /* mod_rewrite indicators */
    if (!strncmp(r->filename, "redirect:", 9)) return DECLINED;
    if (!strncmp(r->filename, "gone:", 5)) return DECLINED;
    if (!strncmp(r->filename, "passthrough:", 12)) return DECLINED;
    if (!strncmp(r->filename, "forbidden:", 10)) return DECLINED;
    /* no translation when server and network charsets are set to the same value */
    if (!strcasecmp(dc->charset_source, dc->charset_default)) return DECLINED;

    mime_type = r->content_type ? r->content_type : ap_default_type(r);

    /* If mime type isn't text or message, bail out.
     */

/* XXX When we handle translation of the request body, watch out here as
 *     1.3 allowed additional mime types: multipart and
 *     application/x-www-form-urlencoded
 */

    if (strncasecmp(mime_type, "text/", 5) &&
#if APR_CHARSET_EBCDIC || AP_WANT_DIR_TRANSLATION
        /* On an EBCDIC machine, be willing to translate mod_autoindex-
         * generated output.  Otherwise, it doesn't look too cool.
         *
         * XXX This isn't a perfect fix because this doesn't trigger us
         * to convert from the charset of the source code to ASCII.  The
         * general solution seems to be to allow a generator to set an
         * indicator in the r specifying that the body is coded in the
         * implementation character set (i.e., the charset of the source
         * code).  This would get several different types of documents
         * translated properly: mod_autoindex output, mod_status output,
         * mod_info output, hard-coded error documents, etc.
         */
        strcmp(mime_type, DIR_MAGIC_TYPE) &&
#endif
        strncasecmp(mime_type, "message/", 8)) {
        if (dc->debug >= DBGLVL_GORY) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                          "mime type is %s; no translation selected",
                          mime_type);
        }
        /* We must not bail out here (i.e., the MIME test must be in the filter
         * itself, not in the fixup, because only then is the final MIME type known.
         * Examples for late changes to the MIME type include CGI handling (MIME
         * type is set in the Content-Type header produced by the CGI script), or
         * PHP (until PHP runs, the MIME type is set to application/x-httpd-php)
         */
    }

    if (dc->debug >= DBGLVL_GORY) {
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                      "charset_source: %s charset_default: %s",
                      dc && dc->charset_source ? dc->charset_source : "(none)",
                      dc && dc->charset_default ? dc->charset_default : "(none)");
    }

    /* Get storage for the request data and the output filter context.
     * We rarely need the input filter context, so allocate that separately.
     */
    reqinfo = (charset_req_t *)apr_pcalloc(r->pool,
                                           sizeof(charset_req_t) +
                                           sizeof(charset_filter_ctx_t));
    output_ctx = (charset_filter_ctx_t *)(reqinfo + 1);

    reqinfo->dc = dc;
    output_ctx->dc = dc;
    ap_set_module_config(r->request_config, &charset_lite_module, reqinfo);

    reqinfo->output_ctx = output_ctx;

    /* We must not open the xlation table here yet, because the final MIME
     * type is not known until we are actually called in the output filter.
     * With POST or PUT request, the case is different, because their MIME
     * type is set in the request headers, and their data are prerequisites
     * for actually calling, e.g., the CGI handler later on.
     */
    output_ctx->xlate = NULL;

    switch (r->method_number) {
    case M_PUT:
    case M_POST:
        /* Set up input translation.  Note: A request body can be included
         * with the OPTIONS method, but for now we don't set up translation
         * of it.
         */
        input_ctx = apr_pcalloc(r->pool, sizeof(charset_filter_ctx_t));
        input_ctx->bb = apr_brigade_create(r->pool,
                                           r->connection->bucket_alloc);
        input_ctx->tmp = apr_palloc(r->pool, INPUT_XLATE_BUF_SIZE);
        input_ctx->dc = dc;
        reqinfo->input_ctx = input_ctx;
        rv = apr_xlate_open(&input_ctx->xlate, dc->charset_source,
                            dc->charset_default, r->pool);
        if (rv != APR_SUCCESS) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
                          "can't open translation %s->%s",
                          dc->charset_default, dc->charset_source);
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    return DECLINED;
}
Ejemplo n.º 2
0
/* xlate_out_filter() handles (almost) arbitrary conversions from one charset
 * to another...
 * translation is determined in the fixup hook (find_code_page), which is
 * where the filter's context data is set up... the context data gives us
 * the translation handle
 */
static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
{
    charset_req_t *reqinfo = ap_get_module_config(f->r->request_config,
                                                  &charset_lite_module);
    charset_dir_t *dc = ap_get_module_config(f->r->per_dir_config,
                                             &charset_lite_module);
    charset_filter_ctx_t *ctx = f->ctx;
    apr_bucket *dptr, *consumed_bucket;
    const char *cur_str;
    apr_size_t cur_len, cur_avail;
    char tmp[OUTPUT_XLATE_BUF_SIZE];
    apr_size_t space_avail;
    int done;
    apr_status_t rv = APR_SUCCESS;

    if (!ctx) {
        /* this is SetOutputFilter path; grab the preallocated context,
         * if any; note that if we decided not to do anything in an earlier
         * handler, we won't even have a reqinfo
         */
        if (reqinfo) {
            ctx = f->ctx = reqinfo->output_ctx;
            reqinfo->output_ctx = NULL; /* prevent SNAFU if user coded us twice
                                         * in the filter chain; we can't have two
                                         * instances using the same context
                                         */
        }
        if (!ctx) {                   /* no idea how to translate; don't do anything */
            ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(charset_filter_ctx_t));
            ctx->dc = dc;
            ctx->noop = 1;
        }
    }

    /* Opening the output translation (this used to be done in the fixup hook,
     * but that was too early: a subsequent type modification, e.g., by a
     * CGI script, would go unnoticed. Now we do it in the filter itself.)
     */
    if (!ctx->noop && ctx->xlate == NULL)
    {
        const char *mime_type = f->r->content_type ? f->r->content_type : ap_default_type(f->r);

        /* XXX When we handle translation of the request body, watch out here as
         *     1.3 allowed additional mime types: multipart and
         *     application/x-www-form-urlencoded
         */
        if (strncasecmp(mime_type, "text/", 5) == 0 ||
#if APR_CHARSET_EBCDIC
        /* On an EBCDIC machine, be willing to translate mod_autoindex-
         * generated output.  Otherwise, it doesn't look too cool.
         *
         * XXX This isn't a perfect fix because this doesn't trigger us
         * to convert from the charset of the source code to ASCII.  The
         * general solution seems to be to allow a generator to set an
         * indicator in the r specifying that the body is coded in the
         * implementation character set (i.e., the charset of the source
         * code).  This would get several different types of documents
         * translated properly: mod_autoindex output, mod_status output,
         * mod_info output, hard-coded error documents, etc.
         */
        strcmp(mime_type, DIR_MAGIC_TYPE) == 0 ||
#endif
        strncasecmp(mime_type, "message/", 8) == 0) {

            rv = apr_xlate_open(&ctx->xlate,
                        dc->charset_default, dc->charset_source, f->r->pool);
            if (rv != APR_SUCCESS) {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
                              "can't open translation %s->%s",
                              dc->charset_source, dc->charset_default);
                ctx->noop = 1;
            }
        }
        else {
                ctx->noop = 1;
                if (dc->debug >= DBGLVL_GORY)
                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
                                  "mime type is %s; no translation selected",
                                  mime_type);
            }
    }

    if (dc->debug >= DBGLVL_GORY) {
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
                     "xlate_out_filter() - "
                     "charset_source: %s charset_default: %s",
                     dc && dc->charset_source ? dc->charset_source : "(none)",
                     dc && dc->charset_default ? dc->charset_default : "(none)");
    }

    if (!ctx->ran) {  /* filter never ran before */
        chk_filter_chain(f);
        ctx->ran = 1;
    }

    if (ctx->noop) {
        return ap_pass_brigade(f->next, bb);
    }

    dptr = APR_BRIGADE_FIRST(bb);
    done = 0;
    cur_len = 0;
    space_avail = sizeof(tmp);
    consumed_bucket = NULL;
    while (!done) {
        if (!cur_len) { /* no bytes left to process in the current bucket... */
            if (consumed_bucket) {
                apr_bucket_delete(consumed_bucket);
                consumed_bucket = NULL;
            }
            if (dptr == APR_BRIGADE_SENTINEL(bb)) {
                done = 1;
                break;
            }
            if (APR_BUCKET_IS_EOS(dptr)) {
                done = 1;
                cur_len = -1; /* XXX yuck, but that tells us to send
                                 * eos down; when we minimize our bb construction
                                 * we'll fix this crap */
                if (ctx->saved) {
                    /* Oops... we have a partial char from the previous bucket
                     * that won't be completed because there's no more data.
                     */
                    rv = APR_INCOMPLETE;
                    ctx->ees = EES_INCOMPLETE_CHAR;
                }
                break;
            }
            rv = apr_bucket_read(dptr, &cur_str, &cur_len, APR_BLOCK_READ);
            if (rv != APR_SUCCESS) {
                done = 1;
                ctx->ees = EES_BUCKET_READ;
                break;
            }
            consumed_bucket = dptr; /* for axing when we're done reading it */
            dptr = APR_BUCKET_NEXT(dptr); /* get ready for when we access the
                                          * next bucket */
        }
        /* Try to fill up our tmp buffer with translated data. */
        cur_avail = cur_len;

        if (cur_len) { /* maybe we just hit the end of a pipe (len = 0) ? */
            if (ctx->saved) {
                /* Rats... we need to finish a partial character from the previous
                 * bucket.
                 */
                char *tmp_tmp;

                tmp_tmp = tmp + sizeof(tmp) - space_avail;
                rv = finish_partial_char(ctx,
                                         &cur_str, &cur_len,
                                         &tmp_tmp, &space_avail);
            }
            else {
                rv = apr_xlate_conv_buffer(ctx->xlate,
                                           cur_str, &cur_avail,
                                           tmp + sizeof(tmp) - space_avail, &space_avail);

                /* Update input ptr and len after consuming some bytes */
                cur_str += cur_len - cur_avail;
                cur_len = cur_avail;

                if (rv == APR_INCOMPLETE) { /* partial character at end of input */
                    /* We need to save the final byte(s) for next time; we can't
                     * convert it until we look at the next bucket.
                     */
                    rv = set_aside_partial_char(ctx, cur_str, cur_len);
                    cur_len = 0;
                }
            }
        }

        if (rv != APR_SUCCESS) {
            /* bad input byte or partial char too big to store */
            done = 1;
        }

        if (space_avail < XLATE_MIN_BUFF_LEFT) {
            /* It is time to flush, as there is not enough space left in the
             * current output buffer to bother with converting more data.
             */
            rv = send_downstream(f, tmp, sizeof(tmp) - space_avail);
            if (rv != APR_SUCCESS) {
                done = 1;
            }

            /* tmp is now empty */
            space_avail = sizeof(tmp);
        }
    }

    if (rv == APR_SUCCESS) {
        if (space_avail < sizeof(tmp)) { /* gotta write out what we converted */
            rv = send_downstream(f, tmp, sizeof(tmp) - space_avail);
        }
    }
    if (rv == APR_SUCCESS) {
        if (cur_len == -1) {
            rv = send_eos(f);
        }
    }
    else {
        log_xlate_error(f, rv);
    }

    return rv;
}
static int dosdetector_handler(request_rec *r)
{
	//DEBUGLOG("dosdetector_handler is called");

    dosdetector_dir_config *cfg = (dosdetector_dir_config *) ap_get_module_config(r->per_dir_config, &dosdetector_module);
	
	if(cfg->detection) return DECLINED;
    if (!ap_is_initial_req(r)) return DECLINED;

    //char **ignore_contenttype = (char **) cfg->ignore_contenttype->elts;
	
    const char *content_type;
    const char *address_tmp;
    const char *address = NULL;
    int i;

    content_type = ap_sub_req_lookup_uri(r->uri, r, NULL)->content_type;
    if (!content_type) {
#if (AP_SERVER_MINORVERSION_NUMBER > 2)
        content_type = DefaultContentType;
#else
        content_type = ap_default_type(r);
#endif
    }

	if (cfg->forwarded){
		if ((address_tmp = apr_table_get(r->headers_in, "X-Forwarded-For")) != NULL){
			const char *i = address_tmp;
			while(*i != 0 && *i != ',')
				i++;
			address = apr_pstrndup(r->pool, address_tmp, i - address_tmp);
		}
	}
	if (address == NULL) {
#if (AP_SERVER_MINORVERSION_NUMBER > 2)
		address = r->connection->client_ip;
#else
		address = r->connection->remote_ip;
#endif
    }

    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
    ap_regex_t **contenttype_regexp = (ap_regex_t **) cfg->contenttype_regexp->elts;
	for (i = 0; i < cfg->contenttype_regexp->nelts; i++) {
		if(!ap_regexec(contenttype_regexp[i], content_type, AP_MAX_REG_MATCH, regmatch, 0)){
			//ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, 0, "ignoring content-type: %s", content_type);
			return OK;
		}
    }
	DEBUGLOG("dosdetector: processing content-type: %s", content_type);

	struct in_addr addr;
	if(!cfg->forwarded) {
#if (AP_SERVER_MINORVERSION_NUMBER > 2)
		addr = r->connection->client_addr->sa.sin.sin_addr;
#else
		addr = r->connection->remote_addr->sa.sin.sin_addr;
#endif
    }
	if(cfg->forwarded || addr.s_addr == 0){
		if (inet_aton(address, &addr) == 0) {
			TRACELOG("dosdetector: '%s' is not  a valid IP addresss", address);
			return DECLINED;
		}
	}
	if (lock) apr_global_mutex_lock(lock);
	client_t *client = get_client(client_list, addr, cfg->period);
	if (lock) apr_global_mutex_unlock(lock);

	#ifdef _DEBUG
	int last_count = client->count;
	#endif
	count_increment(client, cfg->threshold);
    DEBUGLOG("dosdetector: %s, count: %d -> %d, interval: %d", address, last_count, client->count, (int)client->interval);
    //DEBUGLOG("dosdetector: %s, count: %d -> %d, interval: %d on tid %d, pid %d", address, last_count, client->count, (int)client->interval, gettid(), getpid());

	time_t now = time((time_t *)0);
	if(client->suspected > 0 && client->suspected + cfg->ban_period > now){
		apr_table_setn(r->subprocess_env, "SuspectDoS", "1");
		//apr_table_setn(r->notes, "SuspectDoS", "1");
		DEBUGLOG("dosdetector: '%s' has been still suspected as DoS attack! (suspected %d sec ago)", address, now - client->suspected);

		if(client->count > cfg->ban_threshold){
			if(client->hard_suspected == 0)
				TRACELOG("dosdetector: '%s' is suspected as Hard DoS attack! (counter: %d)", address, client->count);
			client->hard_suspected = now;
			apr_table_setn(r->subprocess_env, "SuspectHardDoS", "1");
			//apr_table_setn(r->notes, "SuspectHardDoS", "1");
		}
	} else {
        if(client->suspected > 0){
			client->suspected = 0;
			client->hard_suspected = 0;
			client->count = 0;
		}
		//int last_count = client->count;
		//client->count = client->count - client->interval * cfg->threshold;
		//if(client->count < 0)
		//	client->count = 0;
		//client->count ++;
		//DEBUGLOG("client address: %s, count: %d -> %d, interval: %d", address, last_count, client->count, client->interval);

		if(client->count > cfg->threshold){
			client->suspected = now;
			apr_table_setn(r->subprocess_env, "SuspectDoS", "1");
			//apr_table_setn(r->notes, "SuspectDoS", "1");
			TRACELOG("dosdetector: '%s' is suspected as DoS attack! (counter: %d)", address, client->count);
		}
	}

	return DECLINED;
}