view src/core/ngx_parse.c @ 4162:fb1375e8b68c stable-1.0

Merging r4036, r4055, r4056, r4057, r4058, r4059, r4060, r4061, r4062, r4063, r4064: Ranges related fixes: The "max_ranges" directive. "max_ranges 0" disables ranges support at all, "max_ranges 1" allows the single range, etc. By default number of ranges is unlimited, to be precise, 2^31-1. If client requests more ranges than "max_ranges" permits, nginx disables ranges and returns just the source response. If total size of all ranges is greater than source response size, then nginx disables ranges and returns just the source response. This fix should not affect well-behaving applications but will defeat DoS attempts exploiting malicious byte ranges. Now unsatisfiable ranges are processed according to RFC 2616.
author Igor Sysoev <igor@sysoev.ru>
date Fri, 30 Sep 2011 14:06:08 +0000
parents 9d2bdbda4ee3
children c352c483263c
line wrap: on
line source


/*
 * Copyright (C) Igor Sysoev
 */


#include <ngx_config.h>
#include <ngx_core.h>


ssize_t
ngx_parse_size(ngx_str_t *line)
{
    u_char     unit;
    size_t     len;
    ssize_t    size;
    ngx_int_t  scale;

    len = line->len;
    unit = line->data[len - 1];

    switch (unit) {
    case 'K':
    case 'k':
        len--;
        scale = 1024;
        break;

    case 'M':
    case 'm':
        len--;
        scale = 1024 * 1024;
        break;

    default:
        scale = 1;
    }

    size = ngx_atosz(line->data, len);
    if (size == NGX_ERROR) {
        return NGX_ERROR;
    }

    size *= scale;

    return size;
}


off_t
ngx_parse_offset(ngx_str_t *line)
{
    u_char     unit;
    off_t      offset;
    size_t     len;
    ngx_int_t  scale;

    len = line->len;
    unit = line->data[len - 1];

    switch (unit) {
    case 'K':
    case 'k':
        len--;
        scale = 1024;
        break;

    case 'M':
    case 'm':
        len--;
        scale = 1024 * 1024;
        break;

    case 'G':
    case 'g':
        len--;
        scale = 1024 * 1024 * 1024;
        break;

    default:
        scale = 1;
    }

    offset = ngx_atoof(line->data, len);
    if (offset == NGX_ERROR) {
        return NGX_ERROR;
    }

    offset *= scale;

    return offset;
}


ngx_int_t
ngx_parse_time(ngx_str_t *line, ngx_uint_t sec)
{
    u_char      *p, *last;
    ngx_int_t    value, total, scale;
    ngx_uint_t   max, valid;
    enum {
        st_start = 0,
        st_year,
        st_month,
        st_week,
        st_day,
        st_hour,
        st_min,
        st_sec,
        st_msec,
        st_last
    } step;

    valid = 0;
    value = 0;
    total = 0;
    step = sec ? st_start : st_month;
    scale = sec ? 1 : 1000;

    p = line->data;
    last = p + line->len;

    while (p < last) {

        if (*p >= '0' && *p <= '9') {
            value = value * 10 + (*p++ - '0');
            valid = 1;
            continue;
        }

        switch (*p++) {

        case 'y':
            if (step > st_start) {
                return NGX_ERROR;
            }
            step = st_year;
            max = 68;
            scale = 60 * 60 * 24 * 365;
            break;

        case 'M':
            if (step > st_year) {
                return NGX_ERROR;
            }
            step = st_month;
            max = 828;
            scale = 60 * 60 * 24 * 30;
            break;

        case 'w':
            if (step > st_month) {
                return NGX_ERROR;
            }
            step = st_week;
            max = 3550;
            scale = 60 * 60 * 24 * 7;
            break;

        case 'd':
            if (step > st_week) {
                return NGX_ERROR;
            }
            step = st_day;
            max = 24855;
            scale = 60 * 60 * 24;
            break;

        case 'h':
            if (step > st_day) {
                return NGX_ERROR;
            }
            step = st_hour;
            max = 596523;
            scale = 60 * 60;
            break;

        case 'm':
            if (*p == 's') {
                if (sec || step > st_sec) {
                    return NGX_ERROR;
                }
                p++;
                step = st_msec;
                max = 2147483647;
                scale = 1;
                break;
            }

            if (step > st_hour) {
                return NGX_ERROR;
            }
            step = st_min;
            max = 35791394;
            scale = 60;
            break;

        case 's':
            if (step > st_min) {
                return NGX_ERROR;
            }
            step = st_sec;
            max = 2147483647;
            scale = 1;
            break;

        case ' ':
            if (step > st_min) {
                return NGX_ERROR;
            }
            step = st_last;
            max = 2147483647;
            scale = 1;
            break;

        default:
            return NGX_ERROR;
        }

        if (step != st_msec && !sec) {
            scale *= 1000;
            max /= 1000;
        }

        if ((ngx_uint_t) value > max) {
            return NGX_ERROR;
        }

        total += value * scale;

        if ((ngx_uint_t) total > 2147483647) {
            return NGX_ERROR;
        }

        value = 0;
        scale = sec ? 1 : 1000;

        while (p < last && *p == ' ') {
            p++;
        }
    }

    if (valid) {
        return total + value * scale;
    }

    return NGX_ERROR;
}