1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define CORE_PRIVATE
18#include "httpd.h"
19#include "http_config.h"
20#include "http_request.h"
21#include "http_connection.h"
22#include "http_protocol.h"
23#include "http_log.h"
24#include "http_core.h"
25#include "util_filter.h"
26#define APR_WANT_STRFUNC
27#include "apr_strings.h"
28#include "apr_version.h"
29
30module AP_MODULE_DECLARE_DATA reqtimeout_module;
31
32typedef struct
33{
34    int header_timeout;     /* timeout for reading the req hdrs in secs */
35    int header_max_timeout; /* max timeout for req hdrs in secs */
36    int header_min_rate;    /* min rate for reading req hdrs in bytes/s */
37    apr_time_t header_rate_factor;
38    int body_timeout;       /* timeout for reading the req body in secs */
39    int body_max_timeout;   /* max timeout for req body in secs */
40    int body_min_rate;      /* timeout for reading the req body in secs */
41    apr_time_t body_rate_factor;
42} reqtimeout_srv_cfg;
43
44/* this struct is used both as conn_config and as filter context */
45typedef struct
46{
47    apr_time_t timeout_at;
48    apr_time_t max_timeout_at;
49    int min_rate;
50    int new_timeout;
51    int new_max_timeout;
52    int in_keep_alive;
53    char *type;
54    apr_socket_t *socket;
55    apr_time_t rate_factor;
56    apr_bucket_brigade *tmpbb;
57} reqtimeout_con_cfg;
58
59static const char *const reqtimeout_filter_name = "reqtimeout";
60
61static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
62{
63    apr_off_t len;
64    apr_time_t new_timeout_at;
65
66    if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
67        return;
68
69    new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
70    if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
71        ccfg->timeout_at = ccfg->max_timeout_at;
72    }
73    else {
74        ccfg->timeout_at = new_timeout_at;
75    }
76}
77
78static apr_status_t check_time_left(reqtimeout_con_cfg *ccfg,
79                                    apr_time_t *time_left_p)
80{
81    *time_left_p = ccfg->timeout_at - apr_time_now();
82    if (*time_left_p <= 0)
83        return APR_TIMEUP;
84
85    if (*time_left_p < apr_time_from_sec(1)) {
86        *time_left_p = apr_time_from_sec(1);
87    }
88    return APR_SUCCESS;
89}
90
91static apr_status_t have_lf_or_eos(apr_bucket_brigade *bb)
92{
93    apr_bucket *b = APR_BRIGADE_LAST(bb);
94
95    for ( ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_PREV(b) ) {
96    	const char *str;
97    	apr_size_t len;
98    	apr_status_t rv;
99
100        if (APR_BUCKET_IS_EOS(b))
101            return APR_SUCCESS;
102
103        if (APR_BUCKET_IS_METADATA(b))
104            continue;
105
106        rv = apr_bucket_read(b, &str, &len, APR_BLOCK_READ);
107        if (rv != APR_SUCCESS)
108            return rv;
109
110        if (len == 0)
111            continue;
112
113        if (str[len-1] == APR_ASCII_LF)
114            return APR_SUCCESS;
115    }
116    return APR_INCOMPLETE;
117}
118
119/*
120 * Append bbIn to bbOut and merge small buckets, to avoid DoS by high memory
121 * usage
122 */
123static apr_status_t brigade_append(apr_bucket_brigade *bbOut, apr_bucket_brigade *bbIn)
124{
125    while (!APR_BRIGADE_EMPTY(bbIn)) {
126        apr_bucket *e = APR_BRIGADE_FIRST(bbIn);
127        const char *str;
128        apr_size_t len;
129        apr_status_t rv;
130
131        rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
132        if (rv != APR_SUCCESS) {
133            return rv;
134        }
135
136        APR_BUCKET_REMOVE(e);
137        if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
138            APR_BRIGADE_INSERT_TAIL(bbOut, e);
139        }
140        else {
141            if (len > 0) {
142                rv = apr_brigade_write(bbOut, NULL, NULL, str, len);
143                if (rv != APR_SUCCESS) {
144                    apr_bucket_destroy(e);
145                    return rv;
146                }
147            }
148            apr_bucket_destroy(e);
149        }
150    }
151    return APR_SUCCESS;
152}
153
154
155#define MIN(x,y) ((x) < (y) ? (x) : (y))
156static apr_status_t reqtimeout_filter(ap_filter_t *f,
157                                      apr_bucket_brigade *bb,
158                                      ap_input_mode_t mode,
159                                      apr_read_type_e block,
160                                      apr_off_t readbytes)
161{
162    apr_time_t time_left;
163    apr_time_t now;
164    apr_status_t rv;
165    apr_interval_time_t saved_sock_timeout = -1;
166    reqtimeout_con_cfg *ccfg = f->ctx;
167
168    if (ccfg->in_keep_alive) {
169        /* For this read, the normal keep-alive timeout must be used */
170        ccfg->in_keep_alive = 0;
171        return ap_get_brigade(f->next, bb, mode, block, readbytes);
172    }
173
174    now = apr_time_now();
175    if (ccfg->new_timeout > 0) {
176        /* set new timeout */
177        ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
178        ccfg->new_timeout = 0;
179        if (ccfg->new_max_timeout > 0) {
180            ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
181            ccfg->new_max_timeout = 0;
182        }
183    }
184    else if (ccfg->timeout_at == 0) {
185        /* no timeout set */
186        return ap_get_brigade(f->next, bb, mode, block, readbytes);
187    }
188
189    if (!ccfg->socket) {
190        ccfg->socket = ap_get_module_config(f->c->conn_config, &core_module);
191    }
192
193    rv = check_time_left(ccfg, &time_left);
194    if (rv != APR_SUCCESS)
195        goto out;
196
197    if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
198        || mode == AP_MODE_EATCRLF) {
199        rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
200        if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
201            extend_timeout(ccfg, bb);
202        }
203        return rv;
204    }
205
206    rv = apr_socket_timeout_get(ccfg->socket, &saved_sock_timeout);
207    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
208
209    rv = apr_socket_timeout_set(ccfg->socket, MIN(time_left, saved_sock_timeout));
210    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
211
212    if (mode == AP_MODE_GETLINE) {
213        /*
214         * For a blocking AP_MODE_GETLINE read, apr_brigade_split_line()
215         * would loop until a whole line has been read. As this would make it
216         * impossible to enforce a total timeout, we only do non-blocking
217         * reads.
218         */
219        apr_off_t remaining = HUGE_STRING_LEN;
220        do {
221            apr_off_t bblen;
222#if APR_MAJOR_VERSION < 2
223            apr_int32_t nsds;
224            apr_interval_time_t poll_timeout;
225            apr_pollfd_t pollset;
226#endif
227
228            rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_NONBLOCK_READ, remaining);
229            if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
230                break;
231            }
232
233            if (!APR_BRIGADE_EMPTY(bb)) {
234                if (ccfg->min_rate > 0) {
235                    extend_timeout(ccfg, bb);
236                }
237
238                rv = have_lf_or_eos(bb);
239                if (rv != APR_INCOMPLETE) {
240                    break;
241                }
242
243                rv = apr_brigade_length(bb, 1, &bblen);
244                if (rv != APR_SUCCESS) {
245                    break;
246                }
247                remaining -= bblen;
248                if (remaining <= 0) {
249                    break;
250                }
251
252                /* Haven't got a whole line yet, save what we have ... */
253                if (!ccfg->tmpbb) {
254                    ccfg->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
255                }
256                rv = brigade_append(ccfg->tmpbb, bb);
257                if (rv != APR_SUCCESS)
258                    break;
259            }
260
261            /* ... and wait for more */
262#if APR_MAJOR_VERSION < 2
263            pollset.p = f->c->pool;
264            pollset.desc_type = APR_POLL_SOCKET;
265            pollset.reqevents = APR_POLLIN|APR_POLLHUP;
266            pollset.desc.s = ccfg->socket;
267            apr_socket_timeout_get(ccfg->socket, &poll_timeout);
268            rv = apr_poll(&pollset, 1, &nsds, poll_timeout);
269#else
270            rv = apr_socket_wait(ccfg->socket, APR_WAIT_READ);
271#endif
272            if (rv != APR_SUCCESS)
273                break;
274
275            rv = check_time_left(ccfg, &time_left);
276            if (rv != APR_SUCCESS)
277                break;
278
279            rv = apr_socket_timeout_set(ccfg->socket,
280                                   MIN(time_left, saved_sock_timeout));
281            AP_DEBUG_ASSERT(rv == APR_SUCCESS);
282
283        } while (1);
284
285        if (ccfg->tmpbb)
286            APR_BRIGADE_PREPEND(bb, ccfg->tmpbb);
287
288    }
289    else {
290        /* mode != AP_MODE_GETLINE */
291        rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
292        if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
293            extend_timeout(ccfg, bb);
294        }
295    }
296
297    apr_socket_timeout_set(ccfg->socket, saved_sock_timeout);
298
299out:
300    if (APR_STATUS_IS_TIMEUP(rv)) {
301        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
302                      "Request %s read timeout", ccfg->type);
303        /*
304         * If we allow a normal lingering close, the client may keep this
305         * process/thread busy for another 30s (MAX_SECS_TO_LINGER).
306         * Therefore we tell ap_lingering_close() to shorten this period to
307         * 2s (SECONDS_TO_LINGER).
308         */
309        apr_table_setn(f->c->notes, "short-lingering-close", "1");
310
311        /*
312         * Also, we must not allow keep-alive requests, as
313         * ap_finalize_protocol() may ignore our error status (if the timeout
314         * happened on a request body that is discarded).
315         */
316        f->c->keepalive = AP_CONN_CLOSE;
317    }
318    return rv;
319}
320
321static int reqtimeout_init(conn_rec *c)
322{
323    reqtimeout_con_cfg *ccfg;
324    reqtimeout_srv_cfg *cfg;
325
326    cfg = ap_get_module_config(c->base_server->module_config,
327                               &reqtimeout_module);
328    AP_DEBUG_ASSERT(cfg != NULL);
329    if (cfg->header_timeout <= 0 && cfg->body_timeout <= 0) {
330        /* not configured for this vhost */
331        return DECLINED;
332    }
333
334    ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
335    ccfg->new_timeout = cfg->header_timeout;
336    ccfg->new_max_timeout = cfg->header_max_timeout;
337    ccfg->type = "header";
338    ccfg->min_rate = cfg->header_min_rate;
339    ccfg->rate_factor = cfg->header_rate_factor;
340    ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
341
342    ap_add_input_filter("reqtimeout", ccfg, NULL, c);
343    /* we are not handling the connection, we just do initialization */
344    return DECLINED;
345}
346
347static int reqtimeout_after_headers(request_rec *r)
348{
349    reqtimeout_srv_cfg *cfg;
350    reqtimeout_con_cfg *ccfg =
351        ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
352
353    if (ccfg == NULL) {
354        /* not configured for this connection */
355        return OK;
356    }
357
358    cfg = ap_get_module_config(r->connection->base_server->module_config,
359                               &reqtimeout_module);
360    AP_DEBUG_ASSERT(cfg != NULL);
361
362    ccfg->timeout_at = 0;
363    ccfg->max_timeout_at = 0;
364    if (r->method_number != M_CONNECT) {
365        ccfg->new_timeout = cfg->body_timeout;
366        ccfg->new_max_timeout = cfg->body_max_timeout;
367        ccfg->min_rate = cfg->body_min_rate;
368        ccfg->rate_factor = cfg->body_rate_factor;
369        ccfg->type = "body";
370    }
371
372    return OK;
373}
374
375static int reqtimeout_after_body(request_rec *r)
376{
377    reqtimeout_srv_cfg *cfg;
378    reqtimeout_con_cfg *ccfg =
379        ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
380
381    if (ccfg == NULL) {
382        /* not configured for this connection */
383        return OK;
384    }
385
386    cfg = ap_get_module_config(r->connection->base_server->module_config,
387                               &reqtimeout_module);
388    AP_DEBUG_ASSERT(cfg != NULL);
389
390    ccfg->timeout_at = 0;
391    ccfg->max_timeout_at = 0;
392    ccfg->in_keep_alive = 1;
393    ccfg->new_timeout = cfg->header_timeout;
394    ccfg->new_max_timeout = cfg->header_max_timeout;
395    ccfg->min_rate = cfg->header_min_rate;
396    ccfg->rate_factor = cfg->header_rate_factor;
397
398    ccfg->type = "header";
399
400    return OK;
401}
402
403static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
404{
405    reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
406
407    cfg->header_timeout = -1;
408    cfg->header_max_timeout = -1;
409    cfg->header_min_rate = -1;
410    cfg->body_timeout = -1;
411    cfg->body_max_timeout = -1;
412    cfg->body_min_rate = -1;
413
414    return cfg;
415}
416
417#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == -1) ? b->val : a->val;
418static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
419{
420    reqtimeout_srv_cfg *base = base_;
421    reqtimeout_srv_cfg *add  = add_;
422    reqtimeout_srv_cfg *cfg  = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
423
424    MERGE_INT(cfg, base, add, header_timeout);
425    MERGE_INT(cfg, base, add, header_max_timeout);
426    MERGE_INT(cfg, base, add, header_min_rate);
427    MERGE_INT(cfg, base, add, body_timeout);
428    MERGE_INT(cfg, base, add, body_max_timeout);
429    MERGE_INT(cfg, base, add, body_min_rate);
430
431    cfg->header_rate_factor = (cfg->header_min_rate == -1) ? base->header_rate_factor :
432                              add->header_rate_factor;
433    cfg->body_rate_factor = (cfg->body_min_rate == -1) ? base->body_rate_factor :
434    			     add->body_rate_factor;
435
436    return cfg;
437}
438
439static const char *parse_int(apr_pool_t *p, const char *arg, int *val) {
440    char *endptr;
441    *val = strtol(arg, &endptr, 10);
442
443    if (arg == endptr) {
444        return apr_psprintf(p, "Value '%s' not numerical", endptr);
445    }
446    if (*endptr != '\0') {
447        return apr_psprintf(p, "Cannot parse '%s'", endptr);
448    }
449    if (*val < 0) {
450        return "Value must be non-negative";
451    }
452    return NULL;
453}
454
455static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
456                                      apr_pool_t *p,
457                                      const char *key,
458                                      const char *val)
459{
460    const char *ret = NULL;
461    char *rate_str = NULL, *initial_str, *max_str = NULL;
462    int rate = 0, initial = 0, max = 0;
463    enum { PARAM_HEADER, PARAM_BODY } type;
464
465    if (!strcasecmp(key, "header")) {
466        type = PARAM_HEADER;
467    }
468    else if (!strcasecmp(key, "body")) {
469        type = PARAM_BODY;
470    }
471    else {
472        return "Unknown RequestReadTimeout parameter";
473    }
474
475    if ((rate_str = ap_strcasestr(val, ",minrate="))) {
476        initial_str = apr_pstrndup(p, val, rate_str - val);
477        rate_str += strlen(",minrate=");
478        ret = parse_int(p, rate_str, &rate);
479        if (ret)
480            return ret;
481
482        if (rate == 0)
483            return "Minimum data rate must be larger than 0";
484
485        if ((max_str = strchr(initial_str, '-'))) {
486            *max_str++ = '\0';
487            ret = parse_int(p, max_str, &max);
488            if (ret)
489                return ret;
490        }
491
492        ret = parse_int(p, initial_str, &initial);
493    }
494    else {
495        if (ap_strchr_c(val, '-'))
496            return "Must set MinRate option if using timeout range";
497        ret = parse_int(p, val, &initial);
498    }
499
500    if (ret)
501        return ret;
502
503    if (max && initial >= max) {
504        return "Maximum timeout must be larger than initial timeout";
505    }
506
507    if (type == PARAM_HEADER) {
508        conf->header_timeout = initial;
509        conf->header_max_timeout = max;
510        conf->header_min_rate = rate;
511        if (rate)
512            conf->header_rate_factor = apr_time_from_sec(1) / rate;
513    }
514    else {
515        conf->body_timeout = initial;
516        conf->body_max_timeout = max;
517        conf->body_min_rate = rate;
518        if (rate)
519            conf->body_rate_factor = apr_time_from_sec(1) / rate;
520    }
521    return ret;
522}
523
524static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
525                                   const char *arg)
526{
527    reqtimeout_srv_cfg *conf =
528    ap_get_module_config(cmd->server->module_config,
529                         &reqtimeout_module);
530
531    while (*arg) {
532        char *word, *val;
533        const char *err;
534
535        word = ap_getword_conf(cmd->pool, &arg);
536        val = strchr(word, '=');
537        if (!val) {
538            return "Invalid RequestReadTimeout parameter. Parameter must be "
539            "in the form 'key=value'";
540        }
541        else
542            *val++ = '\0';
543
544        err = set_reqtimeout_param(conf, cmd->pool, word, val);
545
546        if (err)
547            return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
548                               word, val, err);
549    }
550
551    return NULL;
552
553}
554
555static void reqtimeout_hooks(apr_pool_t *pool)
556{
557    /*
558     * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
559     * be called before mod_ssl. Otherwise repeated reads during the ssl
560     * handshake can prevent the timeout from triggering.
561     */
562    ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
563                             AP_FTYPE_CONNECTION + 8);
564
565    /*
566     * mod_reqtimeout needs to be called before ap_process_http_request (which
567     * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules.
568     * This ensures that it only influences normal http connections and not
569     * e.g. mod_ftp. Also, if mod_reqtimeout used the pre_connection hook, it
570     * would be inserted on mod_proxy's backend connections.
571     */
572    ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST);
573
574    ap_hook_post_read_request(reqtimeout_after_headers, NULL, NULL,
575                              APR_HOOK_MIDDLE);
576    ap_hook_log_transaction(reqtimeout_after_body, NULL, NULL,
577                            APR_HOOK_MIDDLE);
578}
579
580static const command_rec reqtimeout_cmds[] = {
581    AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
582                     "Set various timeout parameters for reading request "
583                     "headers and body"),
584    {NULL}
585};
586
587module AP_MODULE_DECLARE_DATA reqtimeout_module = {
588    STANDARD20_MODULE_STUFF,
589    NULL,                           /* create per-dir config structures */
590    NULL,                           /* merge  per-dir config structures */
591    reqtimeout_create_srv_config,   /* create per-server config structures */
592    reqtimeout_merge_srv_config,    /* merge per-server config structures */
593    reqtimeout_cmds,                /* table of config file commands */
594    reqtimeout_hooks
595};
596