view src/http/ngx_http_busy_lock.c @ 5095:4fbef397c753

SNI: added restriction on requesting host other than negotiated. According to RFC 6066, client is not supposed to request a different server name at the application layer. Server implementations that rely upon these names being equal must validate that a client did not send a different name in HTTP request. Current versions of Apache HTTP server always return 400 "Bad Request" in such cases. There exist implementations however (e.g., SPDY) that rely on being able to request different host names in one connection. Given this, we only reject requests with differing host names if verification of client certificates is enabled in a corresponding server configuration. An example of configuration that might not work as expected: server { listen 433 ssl default; return 404; } server { listen 433 ssl; server_name example.org; ssl_client_certificate org.cert; ssl_verify_client on; } server { listen 433 ssl; server_name example.com; ssl_client_certificate com.cert; ssl_verify_client on; } Previously, a client was able to request example.com by presenting a certificate for example.org, and vice versa.
author Valentin Bartenev <vbart@nginx.com>
date Wed, 27 Feb 2013 17:41:34 +0000
parents 14411ee4d89f
children
line wrap: on
line source


/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


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



static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
                                             ngx_http_busy_lock_ctx_t *bc,
                                             int lock);


int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc)
{
    if (bl->busy < bl->max_busy) {
        bl->busy++;

        if (bc->time) {
            bc->time = 0;
            bl->waiting--;
        }

        return NGX_OK;
    }

    if (bc->time) {
        if (bc->time < bl->timeout) {
            ngx_add_timer(bc->event, 1000);
            return NGX_AGAIN;
        }

        bl->waiting--;
        return NGX_DONE;

    }

    if (bl->timeout == 0) {
        return NGX_DONE;
    }

    if (bl->waiting < bl->max_waiting) {
        bl->waiting++;

#if 0
        ngx_add_timer(bc->event, 1000);
        bc->event->event_handler = bc->event_handler;
#endif

        /* TODO: ngx_handle_level_read_event() */

        return NGX_AGAIN;
    }

    return NGX_ERROR;
}


int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
                                 ngx_http_busy_lock_ctx_t *bc, int lock)
{
    int  rc;

    rc = ngx_http_busy_lock_look_cacheable(bl, bc, lock);

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0,
                   "http busylock: %d w:%d mw::%d",
                   rc, bl->waiting, bl->max_waiting);

    if (rc == NGX_OK) {  /* no the same request, there's free slot */
        return NGX_OK;
    }

    if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */
        return NGX_OK;
    }

    /* rc == NGX_AGAIN:  the same request */

    if (bc->time) {
        if (bc->time < bl->timeout) {
            ngx_add_timer(bc->event, 1000);
            return NGX_AGAIN;
        }

        bl->waiting--;
        return NGX_DONE;

    }

    if (bl->timeout == 0) {
        return NGX_DONE;
    }

    if (bl->waiting < bl->max_waiting) {
#if 0
        bl->waiting++;
        ngx_add_timer(bc->event, 1000);
        bc->event->event_handler = bc->event_handler;
#endif

        /* TODO: ngx_handle_level_read_event() */

        return NGX_AGAIN;
    }

    return NGX_ERROR;
}


void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,
                          ngx_http_busy_lock_ctx_t *bc)
{
    if (bl == NULL) {
        return;
    }

    if (bl->md5) {
        bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7));
        bl->cacheable--;
    }

    bl->busy--;
}


static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
                                             ngx_http_busy_lock_ctx_t *bc,
                                             int lock)
{
    int    i, b, cacheable, free;
    u_int  mask;

    b = 0;
    cacheable = 0;
    free = -1;

#if (NGX_SUPPRESS_WARN)
    mask = 0;
#endif

    for (i = 0; i < bl->max_busy; i++) {

        if ((b & 7) == 0) {
            mask = bl->md5_mask[i / 8];
        }

        if (mask & 1) {
            if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) {
                return NGX_AGAIN;
            }
            cacheable++;

        } else if (free == -1) {
            free = i;
        }

#if 1
        if (cacheable == bl->cacheable) {
            if (free == -1 && cacheable < bl->max_busy) {
                free = i + 1;
            }

            break;
        }
#endif

        mask >>= 1;
        b++;
    }

    if (free == -1) {
        return NGX_ERROR;
    }

    if (lock) {
        if (bl->busy == bl->max_busy) {
            return NGX_ERROR;
        }

        ngx_memcpy(&bl->md5[free * 16], bc->md5, 16);
        bl->md5_mask[free / 8] |= 1 << (free & 7);
        bc->slot = free;

        bl->cacheable++;
        bl->busy++;
    }

    return NGX_OK;
}


char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,
                                  void *conf)
{
    char  *p = conf;

    ngx_uint_t             i, dup, invalid;
    ngx_str_t             *value, line;
    ngx_http_busy_lock_t  *bl, **blp;

    blp = (ngx_http_busy_lock_t **) (p + cmd->offset);
    if (*blp) {
        return "is duplicate";
    }

    /* ngx_calloc_shared() */
    bl = ngx_pcalloc(cf->pool, sizeof(ngx_http_busy_lock_t));
    if (bl == NULL) {
        return NGX_CONF_ERROR;
    }
    *blp = bl;

    /* ngx_calloc_shared() */
    bl->mutex = ngx_pcalloc(cf->pool, sizeof(ngx_event_mutex_t));
    if (bl->mutex == NULL) {
        return NGX_CONF_ERROR;
    }

    dup = 0;
    invalid = 0;
    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {

        if (value[i].data[1] != '=') {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid value \"%s\"", value[i].data);
            return NGX_CONF_ERROR;
        }

        switch (value[i].data[0]) {

        case 'b':
            if (bl->max_busy) {
                dup = 1;
                break;
            }

            bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2);
            if (bl->max_busy == NGX_ERROR) {
                invalid = 1;
                break;
            }

            continue;

        case 'w':
            if (bl->max_waiting) {
                dup = 1;
                break;
            }

            bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2);
            if (bl->max_waiting == NGX_ERROR) {
                invalid = 1;
                break;
            }

            continue;

        case 't':
            if (bl->timeout) {
                dup = 1;
                break;
            }

            line.len = value[i].len - 2;
            line.data = value[i].data + 2;

            bl->timeout = ngx_parse_time(&line, 1);
            if (bl->timeout == (time_t) NGX_ERROR) {
                invalid = 1;
                break;
            }

            continue;

        default:
            invalid = 1;
        }

        if (dup) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "duplicate value \"%s\"", value[i].data);
            return NGX_CONF_ERROR;
        }

        if (invalid) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid value \"%s\"", value[i].data);
            return NGX_CONF_ERROR;
        }
    }

    if (bl->timeout == 0 && bl->max_waiting) {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                   "busy lock waiting is useless with zero timeout, ignoring");
    }

    return NGX_CONF_OK;
}