/* Copyright (c) 2011 Dirk-Willem van Gulik, All Rights Reserved.
 *                    <dirkx(at)webweaving(dot)org>
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Quick and dirty module to foil the Range issue of CVE-2011-3192.
 *
 * Typical installation instructions (Thanks Ben):
 *
 * 	mkdir mod_rangecnt
 *      cd mod_rangecnt
 *      wget http://people.apache.org/~dirkx/mod_rangecnt.c
 *
 *      # compile
 *      apxs -c mod_rangecnt.c
 *
 *      # install and add the module to httpd.conf
 *      apxs -i -a mod_rangecnt.la
 *
 *      # restart apache to load the module
 *      apachectl restart
 *
 * You will see entries like below in your log if some one
 * tries the attack on you (loglevel warn or lower):
 *
 * [Thu Aug 25 22:22:40 2011] [warn] [client X.X.X.X]
 *    Rejected on a Range: header with  more than 5 ranges (has 1301)
 *
 * Binaries for some platforms available at
 * 	http://people.apache.org/~dirkx/BINARIES.txt
 *
 * $Id: mod_rangecnt.c 1019 2011-08-30 10:53:49Z dirkx $
 */

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h" 

#ifndef AP_MODULE_DECLARE_DATA

/* Apache 1.3 extra's */
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"

/* spoofing pre 1.3 APR entries */
#define apr_status_t int
#define apr_table_get ap_table_get
#define apr_pool_t pool
#endif

#ifndef MAXRANGEHEADERS
#define MAXRANGEHEADERS (128)
#endif

#ifdef AP_MODULE_DECLARE_DATA
module AP_MODULE_DECLARE_DATA rangecnt_module;
#else
module MODULE_VAR_EXPORT rangecnt_module;
#endif

static apr_status_t ap_rangecnt_early(request_rec *r)
{
    const char *hdrs[2] = {"Range", "Request-Range"};
    int i, c;
    for (i = 0; i < 2; i++) {
	const char *p = apr_table_get(r->headers_in, hdrs[i]);

	if (!p)
	    continue;

	for (c = 1; *p; p++) {
	    if (*p == ',')
		c++;
	};

	if (c > MAXRANGEHEADERS) {
#ifdef AP_MODULE_DECLARE_DATA
	    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
#else
	    ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
#endif
			  "Rejected on a %s: header with more than "
			  "%d ranges (has %d)", hdrs[i], MAXRANGEHEADERS, c);
	    return HTTP_RANGE_NOT_SATISFIABLE;
	};
    };

    return DECLINED;
}

#ifndef AP_MODULE_DECLARE_DATA
module MODULE_VAR_EXPORT rangecnt_module = {
    STANDARD_MODULE_STUFF,
    NULL,                       /* initializer */
    NULL,            		/* dir config creater */
    NULL,                       /* dir merger --- default is to override */
    NULL,                       /* server config */
    NULL,                       /* merge server configs */
    NULL,                       /* command table */
    NULL,                       /* handlers */
    NULL,                       /* filename translation */
    NULL,                       /* check_user_id */
    NULL,                       /* check auth */
    NULL,                       /* check access */
    NULL,                       /* type_checker */
    NULL,                       /* fixups */
    NULL,                       /* logger */
    NULL,                       /* header parser */
    NULL,                       /* child_init */
    NULL,                       /* child_exit */
    ap_rangecnt_early           /* post read-request */
};
#else
static void register_hooks(apr_pool_t * p)
{
    ap_hook_post_read_request(ap_rangecnt_early, NULL, NULL, APR_HOOK_FIRST);
}

module AP_MODULE_DECLARE_DATA rangecnt_module =
{
    STANDARD20_MODULE_STUFF,
    NULL,			/* dir config creater */
    NULL,			/* dir merger --- default is to override */
    NULL,			/* server config */
    NULL,			/* merge server configs */
    NULL,			/* command apr_table_t */
    register_hooks		/* register hooks */
};
#endif
