ngx_int_t ngx_nats_buf_ensure(ngx_nats_buf_t *buf, size_t size, ngx_int_t compact) { size_t n, tail, ns, nt; u_char *bnew; if (buf->pos == buf->end && buf->pos != 0) { buf->pos = 0; buf->end = 0; } tail = buf->cap - buf->end; if (tail >= size) { return NGX_OK; } if (compact != 0 && buf->pos > 0 && (size <= (tail + buf->pos))) { ngx_memmove(buf->buf, buf->buf + buf->pos, buf->end - buf->pos); buf->end -= buf->pos; buf->pos = 0; tail = buf->cap - buf->end; } if (tail >= size) { return NGX_OK; } if (buf->cap >= NGX_NATS_MAX_MESSAGE_SIZE) { ngx_log_error(NGX_LOG_CRIT, buf->log, 0, "attempt to increase NATS buffer size to more than maximum of %i", (ngx_int_t)NGX_NATS_MAX_MESSAGE_SIZE); return NGX_ERROR; } /* realloc */ n = buf->end - buf->pos; /* current bytes */ nt = n + size; /* total need */ ns = buf->cap * 2; while(ns < nt) { ns <<= 1; /* checked above cap is limited */ } bnew = ngx_alloc(ns, buf->log); if (bnew == NULL) { return NGX_ERROR; } ngx_memcpy(bnew, buf->buf + buf->pos, n); ngx_free(buf->buf); buf->buf = bnew; buf->cap = ns; return NGX_OK; }
void ngx_nats_buf_compact(ngx_nats_buf_t *buf) { if (buf->pos == buf->end) { buf->pos = 0; buf->end = 0; return; } if (buf->pos > 0) { ngx_memmove(buf->buf, buf->buf + buf->pos, buf->end - buf->pos); buf->end -= buf->pos; buf->pos = 0; } }
static void ngx_http_parallel_header_clear_value( ngx_list_t* headers, ngx_str_t* name, ngx_uint_t hash) { ngx_list_part_t *part; ngx_table_elt_t *h; ngx_uint_t i = 0; part = &headers->part; h = part->elts; for (;;) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash != hash || h[i].key.len != name->len || ngx_memcmp(h[i].lowcase_key, name->data, name->len) != 0) { i++; continue; } ngx_memmove(h + i, h + i + 1, (part->nelts - i - 1) * sizeof(*h)); part->nelts--; } }
static ngx_int_t ngx_http_advertise_process(ngx_http_request_t *r) { u_char *location = NULL; ngx_int_t res = NGX_OK; ngx_uint_t try_times = 0; ngx_http_advertise_format *adver =NULL; ngx_http_advertise_conf_t *conf = \ ngx_http_get_module_loc_conf(r, ngx_http_advertise_module); ngx_http_advertise_ctx_t *ctx = \ ngx_http_get_module_ctx(r, ngx_http_advertise_module); if ((size_t)(ctx->last - ctx->html) < conf->min_content_len) { ngx_http_advertise_set_status(r, 7); return NGX_ERROR; } while (try_times < conf->advertise_array->nelts) { adver = ngx_http_advertise_get_random(r, (ngx_int_t)ctx->html[try_times * 13]); location = ngx_http_find_location(ctx, adver); if (location) { if (location < ctx->last) { ngx_memmove(location + adver->ad_data.len, location, ctx->last - location); } ngx_memcpy(location, adver->ad_data.data, adver->ad_data.len); ctx->last += adver->ad_data.len; ctx->target = &(adver->ad_data); break; } try_times ++; } if (try_times == conf->advertise_array->nelts) { ngx_http_advertise_set_status(r, 7); res = NGX_ERROR; } return res; }
//从配置文件中读取数据到cf->conf_file->buffer中,并解析 static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf) { u_char *start, ch, *src, *dst; off_t file_size; size_t len; ssize_t n, size; ngx_uint_t found, need_space, last_space, sharp_comment, variable; ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b; found = 0; need_space = 0; last_space = 1; sharp_comment = 0; variable = 0; quoted = 0; s_quoted = 0; d_quoted = 0; cf->args->nelts = 0; b = cf->conf_file->buffer; start = b->pos; start_line = cf->conf_file->line; file_size = ngx_file_size(&cf->conf_file->file.info); for ( ;; ) { if (b->pos >= b->last) { //如果已经解析完了读取到的数据 if (cf->conf_file->file.offset >= file_size) {//配置文件已经读完 if (cf->args->nelts > 0) {//没有碰到配置块的结束,文件已经读完则出错 if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of parameter, " "expecting \";\""); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); return NGX_ERROR; } return NGX_CONF_FILE_DONE; //否则我们返回DONE } len = b->pos - start; //len字节数据已经解析完了 if (len == NGX_CONF_BUFFER) { //如果缓冲区满了 cf->conf_file->line = start_line; if (d_quoted) { ch = '"'; } else if (s_quoted) { ch = '\''; } else {//当前参数的解析还没有结束则出错 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter \"%*s...\" started", 10, start); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter, probably " "missing terminating \"%c\" character", ch); //可能是引号不匹配 return NGX_ERROR; } if (len) {//len字节数据已经解析完成了,我们不再需要保存这些数据,将他们覆盖,以读取更多的数据 ngx_memmove(b->start, start, len); } //文件还有size大小未读取 size = (ssize_t) (file_size - cf->conf_file->file.offset); if (size > b->end - (b->start + len)) { //如果缓冲区读不完整个文件 size = b->end - (b->start + len); //则只读取缓冲区大小 } //读取文件(如果缓冲区足够大,则我们一次性读取整个文件到缓冲区中,否则我们只读取一部分) n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len;//b->pos指向我们将要解析的数据开始位置 b->last = b->pos + n; //b->last现在指向数据的结束位置 start = b->start; } //end if b->pos >= b->last //以下开始解析缓冲区中读取到的数据 ch = *b->pos++; if (ch == LF) {//碰到换行符,则line++ cf->conf_file->line++; if (sharp_comment) { sharp_comment = 0; } } if (sharp_comment) { continue; } if (quoted) { quoted = 0; continue; } if (need_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { last_space = 1; need_space = 0; continue; } if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } if (ch == ')') { last_space = 1; need_space = 0; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } } if (last_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { continue; } start = b->pos - 1; start_line = cf->conf_file->line; switch (ch) { case ';': case '{': if (cf->args->nelts == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } if (ch == '{') { return NGX_CONF_BLOCK_START; } return NGX_OK; case '}': if (cf->args->nelts != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); return NGX_ERROR; } return NGX_CONF_BLOCK_DONE; case '#': sharp_comment = 1; continue; case '\\': quoted = 1; last_space = 0; continue; case '"': start++; d_quoted = 1; last_space = 0; continue; case '\'': start++; s_quoted = 1; last_space = 0; continue; default: last_space = 0; } } else { if (ch == '{' && variable) { continue; } variable = 0; if (ch == '\\') { quoted = 1; continue; } if (ch == '$') { variable = 1; continue; } if (d_quoted) { if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; } } else if (s_quoted) { if (ch == '\'') { s_quoted = 0; need_space = 1; found = 1; } } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';' || ch == '{') { last_space = 1; found = 1; } //如果发现一条配置项,则我们将其放置到cf->args数组中 if (found) { word = ngx_array_push(cf->args); if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); if (word->data == NULL) { return NGX_ERROR; } for (dst = word->data, src = start, len = 0; src < b->pos - 1; len++) { if (*src == '\\') { switch (src[1]) { case '"': case '\'': case '\\': src++; break; case 't': *dst++ = '\t'; src += 2; continue; case 'r': *dst++ = '\r'; src += 2; continue; case 'n': *dst++ = '\n'; src += 2; continue; } } *dst++ = *src++; } *dst = '\0'; word->len = len; if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } found = 0; } } } }
static ngx_int_t ngx_http_lua_init(ngx_conf_t *cf) { ngx_int_t rc; ngx_array_t *arr; ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; ngx_http_lua_main_conf_t *lmcf; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); if (lmcf->requires_capture_filter) { rc = ngx_http_lua_capture_filter_init(cf); if (rc != NGX_OK) { return rc; } } if (lmcf->postponed_to_rewrite_phase_end == NGX_CONF_UNSET) { lmcf->postponed_to_rewrite_phase_end = 0; } cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); if (lmcf->requires_rewrite) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_lua_rewrite_handler; } if (lmcf->requires_access) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_lua_access_handler; } dd("requires log: %d", (int) lmcf->requires_log); if (lmcf->requires_log) { arr = &cmcf->phases[NGX_HTTP_LOG_PHASE].handlers; h = ngx_array_push(arr); if (h == NULL) { return NGX_ERROR; } if (arr->nelts > 1) { h = arr->elts; ngx_memmove(&h[1], h, (arr->nelts - 1) * sizeof(ngx_http_handler_pt)); } *h = ngx_http_lua_log_handler; } if (lmcf->requires_header_filter) { rc = ngx_http_lua_header_filter_init(); if (rc != NGX_OK) { return rc; } } if (lmcf->requires_body_filter) { rc = ngx_http_lua_body_filter_init(); if (rc != NGX_OK) { return rc; } } if (lmcf->lua == NULL) { dd("initializing lua vm"); if (ngx_http_lua_init_vm(cf, lmcf) != NGX_CONF_OK) { ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "failed to initialize Lua VM"); return NGX_ERROR; } if (!lmcf->requires_shm && lmcf->init_handler) { if (lmcf->init_handler(cf->log, lmcf, lmcf->lua) != 0) { /* an error happened */ return NGX_ERROR; } } dd("Lua VM initialized!"); } return NGX_OK; }
static ngx_int_t ngx_http_lua_init(ngx_conf_t *cf) { int multi_http_blocks; ngx_int_t rc; ngx_array_t *arr; ngx_http_handler_pt *h; volatile ngx_cycle_t *saved_cycle; ngx_http_core_main_conf_t *cmcf; ngx_http_lua_main_conf_t *lmcf; #ifndef NGX_LUA_NO_FFI_API ngx_pool_cleanup_t *cln; #endif lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); if (ngx_http_lua_prev_cycle != ngx_cycle) { ngx_http_lua_prev_cycle = ngx_cycle; multi_http_blocks = 0; } else { multi_http_blocks = 1; } if (multi_http_blocks || lmcf->requires_capture_filter) { rc = ngx_http_lua_capture_filter_init(cf); if (rc != NGX_OK) { return rc; } } if (lmcf->postponed_to_rewrite_phase_end == NGX_CONF_UNSET) { lmcf->postponed_to_rewrite_phase_end = 0; } if (lmcf->postponed_to_access_phase_end == NGX_CONF_UNSET) { lmcf->postponed_to_access_phase_end = 0; } cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); if (lmcf->requires_rewrite) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_lua_rewrite_handler; } if (lmcf->requires_access) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_lua_access_handler; } dd("requires log: %d", (int) lmcf->requires_log); if (lmcf->requires_log) { arr = &cmcf->phases[NGX_HTTP_LOG_PHASE].handlers; h = ngx_array_push(arr); if (h == NULL) { return NGX_ERROR; } if (arr->nelts > 1) { h = arr->elts; ngx_memmove(&h[1], h, (arr->nelts - 1) * sizeof(ngx_http_handler_pt)); } *h = ngx_http_lua_log_handler; } if (multi_http_blocks || lmcf->requires_header_filter) { rc = ngx_http_lua_header_filter_init(); if (rc != NGX_OK) { return rc; } } if (multi_http_blocks || lmcf->requires_body_filter) { rc = ngx_http_lua_body_filter_init(); if (rc != NGX_OK) { return rc; } } #ifndef NGX_LUA_NO_FFI_API /* add the cleanup of semaphores after the lua_close */ cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NGX_ERROR; } cln->data = lmcf; cln->handler = ngx_http_lua_cleanup_semaphore_mm; #endif if (lmcf->lua == NULL) { dd("initializing lua vm"); ngx_http_lua_content_length_hash = ngx_http_lua_hash_literal("content-length"); ngx_http_lua_location_hash = ngx_http_lua_hash_literal("location"); lmcf->lua = ngx_http_lua_init_vm(NULL, cf->cycle, cf->pool, lmcf, cf->log, NULL); if (lmcf->lua == NULL) { ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "failed to initialize Lua VM"); return NGX_ERROR; } if (!lmcf->requires_shm && lmcf->init_handler) { saved_cycle = ngx_cycle; ngx_cycle = cf->cycle; rc = lmcf->init_handler(cf->log, lmcf, lmcf->lua); ngx_cycle = saved_cycle; if (rc != NGX_OK) { /* an error happened */ return NGX_ERROR; } } dd("Lua VM initialized!"); } return NGX_OK; }
static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf) { u_char *start, ch, *src, *dst; off_t file_size; size_t len; ssize_t n, size; ngx_uint_t found, need_space, last_space, sharp_comment, variable; ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b; found = 0; need_space = 0; last_space = 1; sharp_comment = 0; variable = 0; quoted = 0; s_quoted = 0; d_quoted = 0; cf->args->nelts = 0; b = cf->conf_file->buffer; start = b->pos; start_line = cf->conf_file->line; //Means we have read at which line!,always start the first line when we open the file!! file_size = ngx_file_size(&cf->conf_file->file.info); for ( ;; ) { //b->pos point to the first character we need to parse //b->last point to the last character we have put into the buffer!!! if (b->pos >= b->last) { // Means the we need to read more data into the buffer!!! if (cf->conf_file->file.offset >= file_size) { //we get the config file's end!!! if (cf->args->nelts > 0) { if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of parameter, " "expecting \";\""); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); return NGX_ERROR; } return NGX_CONF_FILE_DONE; } len = b->pos - start; //At the beginning,start is zero pos,we must read a whole block or command_line!!! if (len == NGX_CONF_BUFFER) { cf->conf_file->line = start_line; if (d_quoted) { ch = '"'; } else if (s_quoted) { ch = '\''; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter \"%*s...\" started", 10, start); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter, probably " "missing terminating \"%c\" character", ch); return NGX_ERROR; } if (len) { //if the len is buffer has some config line,move the data to the b->start! ngx_memmove(b->start, start, len); } size = (ssize_t) (file_size - cf->conf_file->file.offset); if (size > b->end - (b->start + len)) { size = b->end - (b->start + len); } //size no more than buffer_size 4096 n = ngx_read_file(&cf->conf_file->file, b->start + len, size,cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len; //point to the first haven't parsed character! b->last = b->pos + n; start = b->start; //memmove,so we start a new posistion!! } ch = *b->pos++; //pos point to the first haven't parsed character!! if (ch == LF) { //judge the char whether is line feed!! cf->conf_file->line++; if (sharp_comment) { sharp_comment = 0; } } if (sharp_comment){ //Does the this line started with "#",so the subsequent char need to omit!!! continue; } if (quoted) { //If quoted mark,we continue,we need to read another quote!! quoted = 0; continue; } if (need_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { last_space = 1; need_space = 0; continue; } if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } if (ch == ')') { last_space = 1; need_space = 0; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } } if (last_space) { //At the beginning,last_space == 1,so we if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { //omit the control space!!! continue; } start = b->pos - 1;//point to the start ch!!! start_line = cf->conf_file->line; switch (ch) { case ';': case '{': if (cf->args->nelts == 0){ //";" "{}" stat the end of command or start of a block!!! ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } if (ch == '{') { //If return NGX_CONF_BLOCK_START; } return NGX_OK; case '}': if (cf->args->nelts != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); return NGX_ERROR; } return NGX_CONF_BLOCK_DONE; //"}" stat the end of the whole Block! case '#': //Means the following string are comment content util get a LF!!! sharp_comment = 1; continue; case '\\': //Means '\' use for comment between command,so we need to omit it!! quoted = 1; last_space = 0; continue; case '"': start++; d_quoted = 1; //Means we need to find another double quote!!! last_space = 0; continue; case '\'': //Means we need to find another single quote!! start++; s_quoted = 1; last_space = 0; continue; default: last_space = 0; } } else { if (ch == '{' && variable) { continue; } variable = 0; if (ch == '\\') { quoted = 1; continue; } if (ch == '$') { variable = 1; continue; } if (d_quoted) { //Means we find the another double quote!! if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; } } else if (s_quoted) {//Means we find the another single quote!! if (ch == '\'') { s_quoted = 0; need_space = 1; found = 1; } }else if(ch == ' '|| ch== '\t' || ch == CR || ch == LF || ch == ';' || ch == '{'){ //Means we get a parameter!!! //Means every parameter end with blank,tab or line feed or colon or brace! //and save the parameter to a word!!! last_space = 1; found = 1; } //last_space Means previous space,last_space Means we start with a new parameter!!! if (found) { //Here,Means we have find!! word = ngx_array_push(cf->args); //Here, Means to alloc a space! if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); if (word->data == NULL) { return NGX_ERROR; } for (dst = word->data, src = start, len = 0;src < b->pos - 1;len++) { if (*src == '\\') { switch (src[1]) { case '"': case '\'': case '\\': src++; break; case 't': *dst++ = '\t'; src += 2; continue; case 'r': *dst++ = '\r'; src += 2; continue; case 'n': *dst++ = '\n'; src += 2; continue; } } *dst++ = *src++; } *dst = '\0'; //add the terminal char word->len = len; if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } found = 0; } } } }
static ngx_int_t ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf, ngx_http_lua_block_parser_ctx_t *ctx) { enum { OVEC_SIZE = 2 }; int i, rc; int ovec[OVEC_SIZE]; u_char *start, *p, *q, ch; off_t file_size; size_t len, buf_size; ssize_t n, size; ngx_uint_t start_line; ngx_str_t *word; ngx_buf_t *b; #if nginx_version >= 1009002 ngx_buf_t *dump; #endif b = cf->conf_file->buffer; #if nginx_version >= 1009002 dump = cf->conf_file->dump; #endif start = b->pos; start_line = cf->conf_file->line; buf_size = b->end - b->start; dd("lexer start line: %d", (int) start_line); file_size = ngx_file_size(&cf->conf_file->file.info); for ( ;; ) { if (b->pos >= b->last) { if (cf->conf_file->file.offset >= file_size) { cf->conf_file->line = ctx->start_line; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, expecting " "terminating characters for lua code " "block"); return NGX_ERROR; } len = b->pos - start; if (len == buf_size) { cf->conf_file->line = start_line; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long lua code block, probably " "missing terminating characters"); return NGX_ERROR; } if (len) { ngx_memmove(b->start, start, len); } size = (ssize_t) (file_size - cf->conf_file->file.offset); if (size > b->end - (b->start + len)) { size = b->end - (b->start + len); } n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len; b->last = b->pos + n; start = b->start; #if nginx_version >= 1009002 if (dump) { dump->last = ngx_cpymem(dump->last, b->pos, size); } #endif } rc = ngx_http_lua_lex(b->pos, b->last - b->pos, ovec); if (rc < 0) { /* no match */ /* alas. the lexer does not yet support streaming processing. need * more work below */ if (cf->conf_file->file.offset >= file_size) { cf->conf_file->line = ctx->start_line; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, expecting " "terminating characters for lua code " "block"); return NGX_ERROR; } len = b->last - b->pos; if (len == buf_size) { cf->conf_file->line = start_line; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long lua code block, probably " "missing terminating characters"); return NGX_ERROR; } if (len) { ngx_memcpy(b->start, b->pos, len); } size = (ssize_t) (file_size - cf->conf_file->file.offset); if (size > b->end - (b->start + len)) { size = b->end - (b->start + len); } n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len; b->last = b->pos + n; start = b->start; continue; } if (rc == FOUND_LEFT_LBRACKET_STR || rc == FOUND_LEFT_LBRACKET_CMT) { /* we update the line numbers for best error messages when the * closing long bracket is missing */ for (i = 0; i < ovec[0]; i++) { ch = b->pos[i]; if (ch == LF) { cf->conf_file->line++; } } b->pos += ovec[0]; ovec[1] -= ovec[0]; ovec[0] = 0; if (rc == FOUND_LEFT_LBRACKET_CMT) { p = &b->pos[2]; /* we skip the leading "--" prefix */ rc = FOUND_LBRACKET_CMT; } else { p = b->pos; rc = FOUND_LBRACKET_STR; } /* we temporarily rewrite [=*[ in the input buffer to ]=*] to * construct the pattern for the corresponding closing long * bracket without additional buffers. */ ngx_http_lua_assert(p[0] == '['); p[0] = ']'; ngx_http_lua_assert(b->pos[ovec[1] - 1] == '['); b->pos[ovec[1] - 1] = ']'; /* search for the corresponding closing bracket */ dd("search pattern for the closing long bracket: \"%.*s\" (len=%d)", (int) (b->pos + ovec[1] - p), p, (int) (b->pos + ovec[1] - p)); q = ngx_http_lua_strlstrn(b->pos + ovec[1], b->last, p, b->pos + ovec[1] - p - 1); if (q == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Lua code block missing the closing " "long bracket \"%*s\"", b->pos + ovec[1] - p, p); return NGX_ERROR; } /* restore the original opening long bracket */ p[0] = '['; b->pos[ovec[1] - 1] = '['; ovec[1] = q - b->pos + b->pos + ovec[1] - p; dd("found long bracket token: \"%.*s\"", (int) (ovec[1] - ovec[0]), b->pos + ovec[0]); } for (i = 0; i < ovec[1]; i++) { ch = b->pos[i]; if (ch == LF) { cf->conf_file->line++; } } b->pos += ovec[1]; ctx->token_len = ovec[1] - ovec[0]; break; } word = ngx_array_push(cf->args); if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->temp_pool, b->pos - start); if (word->data == NULL) { return NGX_ERROR; } len = b->pos - start; ngx_memcpy(word->data, start, len); word->len = len; return rc; }
ngx_int_t ngx_add_module(ngx_conf_t *cf, ngx_str_t *file, ngx_module_t *module, char **order) { void *rv; ngx_uint_t i, m, before; ngx_core_module_t *core_module; if (cf->cycle->modules_n >= ngx_max_module) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too many modules loaded"); return NGX_ERROR; } if (module->version != nginx_version) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "module \"%V\" version %ui instead of %ui", file, module->version, (ngx_uint_t) nginx_version); return NGX_ERROR; } if (ngx_strcmp(module->signature, NGX_MODULE_SIGNATURE) != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "module \"%V\" is not binary compatible", file); return NGX_ERROR; } for (m = 0; cf->cycle->modules[m]; m++) { if (ngx_strcmp(cf->cycle->modules[m]->name, module->name) == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "module \"%s\" is already loaded", module->name); return NGX_ERROR; } } /* * if the module wasn't previously loaded, assign an index */ if (module->index == NGX_MODULE_UNSET_INDEX) { module->index = ngx_module_index(cf->cycle); if (module->index >= ngx_max_module) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too many modules loaded"); return NGX_ERROR; } } /* * put the module into the cycle->modules array */ before = cf->cycle->modules_n; if (order) { for (i = 0; order[i]; i++) { if (ngx_strcmp(order[i], module->name) == 0) { i++; break; } } for ( /* void */ ; order[i]; i++) { #if 0 ngx_log_debug2(NGX_LOG_DEBUG_CORE, cf->log, 0, "module: %s before %s", module->name, order[i]); #endif for (m = 0; m < before; m++) { if (ngx_strcmp(cf->cycle->modules[m]->name, order[i]) == 0) { ngx_log_debug3(NGX_LOG_DEBUG_CORE, cf->log, 0, "module: %s before %s:%i", module->name, order[i], m); before = m; break; } } } } /* put the module before modules[before] */ if (before != cf->cycle->modules_n) { ngx_memmove(&cf->cycle->modules[before + 1], &cf->cycle->modules[before], (cf->cycle->modules_n - before) * sizeof(ngx_module_t *)); } cf->cycle->modules[before] = module; cf->cycle->modules_n++; if (module->type == NGX_CORE_MODULE) { /* * we are smart enough to initialize core modules; * other modules are expected to be loaded before * initialization - e.g., http modules must be loaded * before http{} block */ core_module = module->ctx; if (core_module->create_conf) { rv = core_module->create_conf(cf->cycle); if (rv == NULL) { return NGX_ERROR; } cf->cycle->conf_ctx[module->index] = rv; } } return NGX_OK; }
/* * 读取配置文件的token信息 */ static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf) { u_char *start, ch, *src, *dst; off_t file_size; size_t len; ssize_t n, size; ngx_uint_t found, need_space, last_space, sharp_comment, variable; ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b; found = 0; need_space = 0; last_space = 1; sharp_comment = 0; variable = 0; quoted = 0; s_quoted = 0; d_quoted = 0; cf->args->nelts = 0; //此处相当于将配置项缓冲区给清空了 b = cf->conf_file->buffer; start = b->pos; start_line = cf->conf_file->line; file_size = ngx_file_size(&cf->conf_file->file.info); //文件大小 for ( ;; ) { if (b->pos >= b->last) { //缓冲区中是否还有数据 if (cf->conf_file->file.offset >= file_size) { if (cf->args->nelts > 0 || !last_space) { if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of parameter, " "expecting \";\""); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); return NGX_ERROR; } return NGX_CONF_FILE_DONE; } len = b->pos - start; //已读取的大小 if (len == NGX_CONF_BUFFER) { //缓冲区已满 cf->conf_file->line = start_line; if (d_quoted) { ch = '"'; } else if (s_quoted) { ch = '\''; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter \"%*s...\" started", 10, start); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter, probably " "missing terminating \"%c\" character", ch); return NGX_ERROR; } if (len) { ngx_memmove(b->start, start, len); } size = (ssize_t) (file_size - cf->conf_file->file.offset); //配置文件剩余未读取数据大小 if (size > b->end - (b->start + len)) { //确定缓冲区剩余空间大小 size = b->end - (b->start + len); } /*读取配置内容*/ n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len; b->last = b->pos + n; start = b->start; } ch = *b->pos++; if (ch == LF) { //换行 cf->conf_file->line++; if (sharp_comment) { sharp_comment = 0; } } if (sharp_comment) { //注释 continue; } if (quoted) { //引号 quoted = 0; continue; } if (need_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { last_space = 1; need_space = 0; continue; } if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } if (ch == ')') { last_space = 1; need_space = 0; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } } if (last_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { continue; } start = b->pos - 1; start_line = cf->conf_file->line; switch (ch) { case ';': case '{': if (cf->args->nelts == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } if (ch == '{') { //块开始 return NGX_CONF_BLOCK_START; } return NGX_OK; case '}': //块结束 if (cf->args->nelts != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); return NGX_ERROR; } return NGX_CONF_BLOCK_DONE; case '#': //注释 sharp_comment = 1; continue; case '\\': quoted = 1; last_space = 0; continue; case '"': start++; d_quoted = 1; last_space = 0; continue; case '\'': start++; s_quoted = 1; last_space = 0; continue; default: last_space = 0; } } else { if (ch == '{' && variable) { continue; } variable = 0; if (ch == '\\') { quoted = 1; continue; } if (ch == '$') { variable = 1; continue; } if (d_quoted) { if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; } } else if (s_quoted) { if (ch == '\'') { s_quoted = 0; need_space = 1; found = 1; } } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';' || ch == '{') //找到一个配置项 { last_space = 1; found = 1; //找到了哇 } if (found) { word = ngx_array_push(cf->args); //配置项槽 if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); //配置项名字 if (word->data == NULL) { return NGX_ERROR; } for (dst = word->data, src = start, len = 0; src < b->pos - 1; len++) { if (*src == '\\') { switch (src[1]) { case '"': case '\'': case '\\': src++; break; case 't': *dst++ = '\t'; src += 2; continue; case 'r': *dst++ = '\r'; src += 2; continue; case 'n': *dst++ = '\n'; src += 2; continue; } } *dst++ = *src++; } *dst = '\0'; word->len = len; if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } found = 0; } } } }
u_char *ngx_http_cookie_read_one_line(ngx_file_t *f, ngx_buf_t *b, off_t f_size, ssize_t *size) { u_char *start, *curr; ssize_t left, len, n; start = b->pos; curr = start; len = b->last - b->pos; left = (ssize_t)(f_size - (f->offset)); if (len) { for (curr = start; curr < b->last; curr ++){ if (*curr == '\n'){ break; } } if (curr != b->last) { *size = curr - b->pos; b->pos = curr + 1; return start; } else if (left <= 0) { *size = b->last - b->pos; return b->pos; } } if (left <= 0) { *size = 0; return NULL; } if (len) { ngx_memmove(b->start, start, len); b->last = b->start + len; b->pos = b->start; start = b->pos; curr = b->last; } if (left > b->end - b->last) { left = b->end - b->last; } n = ngx_read_file(f, b->last, left, f->offset); if (n == NGX_ERROR) { /* read failed */ *size = -1; return NULL; } b->last = b->last + n; for (; curr < b->last; curr ++) { if (*curr == '\n') { break; } } if (curr != b->last) { *size = curr - b->pos; b->pos = curr + 1; return start; } else if(f->offset >= f_size){ *size = b->last - b->pos; return b->pos; } else { /* buf have no more space */ *size = -1; return NULL; } return NULL; }
static ngx_inline ngx_int_t ngx_http_modsecurity_save_request_body(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; apr_off_t content_length; ngx_buf_t *buf; ngx_http_core_srv_conf_t *cscf; size_t size; ngx_http_connection_t *hc; ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); apr_brigade_length(ctx->brigade, 0, &content_length); if (r->header_in->end - r->header_in->last >= content_length) { /* use r->header_in */ if (ngx_buf_size(r->header_in)) { /* move to the end */ ngx_memmove(r->header_in->pos + content_length, r->header_in->pos, ngx_buf_size(r->header_in)); } if (apr_brigade_flatten(ctx->brigade, (char *)r->header_in->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { return NGX_ERROR; } apr_brigade_cleanup(ctx->brigade); r->header_in->last += content_length; return NGX_OK; } if (ngx_buf_size(r->header_in)) { /* * ngx_http_set_keepalive will reuse r->header_in if * (r->header_in != c->buffer && r->header_in.last != r->header_in.end), * so we need this code block. * see ngx_http_set_keepalive, ngx_http_alloc_large_header_buffer */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); size = ngx_max(cscf->large_client_header_buffers.size, (size_t)content_length + ngx_buf_size(r->header_in)); hc = r->http_connection; #if defined(nginx_version) && nginx_version >= 1011011 if (hc->free && size == cscf->large_client_header_buffers.size) { buf = hc->free->buf; #else if (hc->nfree && size == cscf->large_client_header_buffers.size) { buf = hc->free[--hc->nfree]; #endif ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: use http free large header buffer: %p %uz", buf->pos, buf->end - buf->last); } else if (hc->nbusy < cscf->large_client_header_buffers.num) { if (hc->busy == NULL) { hc->busy = ngx_palloc(r->connection->pool, cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); } if (hc->busy == NULL) { return NGX_ERROR; } else { buf = ngx_create_temp_buf(r->connection->pool, size); } } else { /* TODO: how to deal this case ? */ return NGX_ERROR; } } else { buf = ngx_create_temp_buf(r->pool, (size_t) content_length); } if (buf == NULL) { return NGX_ERROR; } if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { return NGX_ERROR; } apr_brigade_cleanup(ctx->brigade); buf->last += content_length; ngx_memcpy(buf->last, r->header_in->pos, ngx_buf_size(r->header_in)); buf->last += ngx_buf_size(r->header_in); r->header_in = buf; return NGX_OK; } static ngx_inline ngx_int_t ngx_http_modsecurity_load_headers_out(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; char *data; request_rec *req; ngx_http_variable_value_t *vv; ngx_list_part_t *part; ngx_table_elt_t *h; ngx_uint_t i; char *key, *value; u_char *buf = NULL; size_t size = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); req = ctx->req; req->status = r->headers_out.status; req->status_line = (char *)ngx_pstrdup0(r->pool, &r->headers_out.status_line); /* deep copy */ part = &r->headers_out.headers.part; h = part->elts; for (i = 0; ; i++) { if (i >= part->nelts) { if (part->next == NULL) break; part = part->next; h = part->elts; i = 0; } size += h[i].key.len + h[i].value.len + 2; buf = ngx_palloc(r->pool, size); if (buf == NULL) { return NGX_ERROR; } key = (char *)buf; buf = ngx_cpymem(buf, h[i].key.data, h[i].key.len); *buf++ = '\0'; value = (char *)buf; buf = ngx_cpymem(buf, h[i].value.data, h[i].value.len); *buf++ = '\0'; apr_table_addn(req->headers_out, key, value); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers out: \"%V: %V\"", &h[i].key, &h[i].value); } for (i = 0; special_headers_out[i].name; i++) { vv = ngx_http_get_variable(r, &special_headers_out[i].variable_name, ngx_hash_key(special_headers_out[i].variable_name.data, special_headers_out[i].variable_name.len)); if (vv && !vv->not_found) { data = ngx_palloc(r->pool, vv->len + 1); if (data == NULL) { return NGX_ERROR; } ngx_memcpy(data,vv->data, vv->len); data[vv->len] = '\0'; apr_table_setn(req->headers_out, special_headers_out[i].name, data); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers out: \"%s: %s\"", special_headers_out[i].name, data); } } req->content_type = apr_table_get(ctx->req->headers_out, "Content-Type"); req->content_encoding = apr_table_get(ctx->req->headers_out, "Content-Encoding"); data = (char *)apr_table_get(ctx->req->headers_out, "Content-Languages"); if(data != NULL) { ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *)); *(const char **)apr_array_push(ctx->req->content_languages) = data; } /* req->chunked = r->chunked; may be useless */ req->clength = r->headers_out.content_length_n; req->mtime = apr_time_make(r->headers_out.last_modified_time, 0); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers out done"); return NGX_OK; }
//读取token static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf) { u_char *start, ch, *src, *dst; off_t file_size; size_t len; ssize_t n, size; ngx_uint_t found, need_space, last_space, sharp_comment, variable; ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b; found = 0; need_space = 0; last_space = 1; sharp_comment = 0; variable = 0; quoted = 0; s_quoted = 0; d_quoted = 0; cf->args->nelts = 0; b = cf->conf_file->buffer; //填满一个buffer缓冲区 start = b->pos; start_line = cf->conf_file->line; file_size = ngx_file_size(&cf->conf_file->file.info); //得到文件长度 for ( ;; ) { if (b->pos >= b->last) { if (cf->conf_file->file.offset >= file_size) { if (cf->args->nelts > 0) { if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of parameter, " "expecting \";\""); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); return NGX_ERROR; } return NGX_CONF_FILE_DONE; } len = b->pos - start; //已扫描的字符数 if (len == NGX_CONF_BUFFER) { //等于整个缓存时 cf->conf_file->line = start_line; if (d_quoted) { //置为1,表示当前处于双引号字符串后 ch = '"'; } else if (s_quoted) { ch = '\''; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter \"%*s...\" started", 10, start); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter, probably " "missing terminating \"%c\" character", ch); return NGX_ERROR; } if (len) { ngx_memmove(b->start, start, len); //将已扫描的字符移动到头部 } size = (ssize_t) (file_size - cf->conf_file->file.offset); //还没有读取的字符 if (size > b->end - (b->start + len)) { size = b->end - (b->start + len); } n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); //读取 if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len; //pos指向未处理的字符的头部 b->last = b->pos + n; //指向未处理字符的尾部 start = b->start; } ch = *b->pos++; if (ch == LF) { cf->conf_file->line++; //加一行 if (sharp_comment) { sharp_comment = 0; } } if (sharp_comment) { continue; } if (quoted) { quoted = 0; continue; } if (need_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { last_space = 1; //前一个字符为空白字符(空格,回车、tab等) need_space = 0; continue; } if (ch == ';') { //;表示结束 return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } if (ch == ')') { last_space = 1; need_space = 0; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } } if (last_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { continue; } start = b->pos - 1; start_line = cf->conf_file->line; switch (ch) { case ';': case '{': if (cf->args->nelts == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } if (ch == '{') { return NGX_CONF_BLOCK_START; //开始 } return NGX_OK; case '}': if (cf->args->nelts != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); return NGX_ERROR; } return NGX_CONF_BLOCK_DONE; case '#': sharp_comment = 1; continue; case '\\': quoted = 1; last_space = 0; continue; case '"': start++; d_quoted = 1; //双引号后面 last_space = 0; continue; case '\'': start++; s_quoted = 1; //单引号后面 last_space = 0; continue; default: last_space = 0; } } else { if (ch == '{' && variable) { continue; } variable = 0; if (ch == '\\') { quoted = 1; continue; } if (ch == '$') { variable = 1; continue; } if (d_quoted) { if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; //找到一个 } } else if (s_quoted) { if (ch == '\'') { s_quoted = 0; need_space = 1; found = 1; //找到一个 } } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';' || ch == '{') { last_space = 1; found = 1; } if (found) { word = ngx_array_push(cf->args); //cf->args数组 if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); if (word->data == NULL) { return NGX_ERROR; } for (dst = word->data, src = start, len = 0; src < b->pos - 1; len++) { if (*src == '\\') { switch (src[1]) { case '"': case '\'': case '\\': src++; break; case 't': *dst++ = '\t'; src += 2; continue; case 'r': *dst++ = '\r'; src += 2; continue; case 'n': *dst++ = '\n'; src += 2; continue; } } *dst++ = *src++; } *dst = '\0'; word->len = len; if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } found = 0; } } } }
/*本函数将逐字节解析配置文件。每当发现一个单词,将这个单词(word) push 到 ngx_conf_s 的 args 数组中. * 当解析到 ; 或者是 { 时。 则进行返回 NGX_OK NGX_CONF_BLOCK_START * 若解析到文件结束或者 } 时候,则进行返回 NGX_CONF_FILE_DONE NGX_CONF_BLOCK_DONE */ static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf) { u_char *start, ch, *src, *dst; off_t file_size; size_t len; ssize_t n, size; ngx_uint_t found, need_space, last_space, sharp_comment, variable; ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b; found = 0; need_space = 0; last_space = 1; sharp_comment = 0; variable = 0; quoted = 0; s_quoted = 0; //单引号 d_quoted = 0; //双引号 cf->args->nelts = 0; b = cf->conf_file->buffer; start = b->pos; // 已解析字符和已扫描字符之间的分割点 start_line = cf->conf_file->line; file_size = ngx_file_size(&cf->conf_file->file.info); // 循环解析配置文件,逐字符 for ( ;; ) { if (b->pos >= b->last) { //读到缓存中的文件已经解析完毕,则再从文件中读入一段到缓存中继续处理 //判断配置文件是否已经解析完毕 if (cf->conf_file->file.offset >= file_size) { if (cf->args->nelts > 0) { if (cf->conf_file->file.fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of parameter, " "expecting \";\""); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); return NGX_ERROR; } return NGX_CONF_FILE_DONE; } // len 为已经扫描,但未被解析的配置文件 len = b->pos - start; if (len == NGX_CONF_BUFFER) { cf->conf_file->line = start_line; if (d_quoted) { ch = '"'; } else if (s_quoted) { ch = '\''; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter \"%*s...\" started", 10, start); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "too long parameter, probably " "missing terminating \"%c\" character", ch); return NGX_ERROR; } if (len) { // 将已扫描字符串移到buf头 ngx_memmove(b->start, start, len); } size = (ssize_t) (file_size - cf->conf_file->file.offset); if (size > b->end - (b->start + len)) { size = b->end - (b->start + len); } //读出配置文件内容到缓冲区b中 //ngx_read_file 为原子操作pread的封装 n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } if (n != size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ngx_read_file_n " returned " "only %z bytes instead of %z", n, size); return NGX_ERROR; } b->pos = b->start + len; b->last = b->pos + n; start = b->start; } ch = *b->pos++; //检查是否到行尾 LF为ASCII码,即一行的结束 if (ch == LF) { cf->conf_file->line++; // 如果前面一行是注释行,那么现在开始设置新行的标志为:非注释行 if (sharp_comment) { sharp_comment = 0; } } //注释行的话,跳过 // 类似于 #user nobody 这样的语句,在分析user nobody 这些字符时,将在此全部continue // 每次重新读取一行后,这个注释标志将会被重置 if (sharp_comment) { continue; } if (quoted) { quoted = 0; continue; } if (need_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { last_space = 1; need_space = 0; continue; } if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } if (ch == ')') { last_space = 1; need_space = 0; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } } // 类似于每个字符的默认检测规则 // 在解析到普通字符时,last_space标记必被重置 if (last_space) { if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { continue; } // 已解析字符和已读取字符之间的分界线,分解析前为已经解析的字符,可以从buf 中丢弃的。分界线后为已经读取的字符 // 因为未解析,暂时还不能废弃。 start = b->pos - 1; // 更新start_line start_line = cf->conf_file->line; switch (ch) { case ';': case '{': //解析到一个block块开始 if (cf->args->nelts == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"%c\"", ch); return NGX_ERROR; } if (ch == '{') { return NGX_CONF_BLOCK_START; } return NGX_OK; case '}': // 解析到一个block块结束 if (cf->args->nelts != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); return NGX_ERROR; } return NGX_CONF_BLOCK_DONE; case '#': sharp_comment = 1; continue; case '\\': quoted = 1; last_space = 0; continue; case '"': start++; d_quoted = 1; last_space = 0; continue; case '\'': start++; s_quoted = 1; last_space = 0; continue; default: last_space = 0; } } else { if (ch == '{' && variable) { continue; } variable = 0; if (ch == '\\') { quoted = 1; continue; } if (ch == '$') { variable = 1; continue; } if (d_quoted) { if (ch == '"') { d_quoted = 0; need_space = 1; found = 1; } } else if (s_quoted) { if (ch == '\'') { s_quoted = 0; need_space = 1; found = 1; } } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';' || ch == '{') { //空格、TAB、换行、分号,大括号等 last_space = 1; found = 1; } if (found) {//找到了一个关键字(即相应的指令) word = ngx_array_push(cf->args); if (word == NULL) { return NGX_ERROR; } word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); if (word->data == NULL) { return NGX_ERROR; } //没有直接拷贝,而是逐个字符读入,并做了转义字符的解析 //在程序启动阶段做的,性能不是最最重要的,而是配置文件的灵活性 //解析完命令后,会在字符串结尾加'\0'结尾符 for (dst = word->data, src = start, len = 0; src < b->pos - 1; len++) { if (*src == '\\') { // 若第一个字符为'\\'。则应该为一个转义字符 switch (src[1]) { case '"': case '\'': case '\\': src++; break; case 't': *dst++ = '\t'; src += 2; continue; case 'r': *dst++ = '\r'; src += 2; continue; case 'n': *dst++ = '\n'; src += 2; continue; } } *dst++ = *src++; } *dst = '\0'; word->len = len; if (ch == ';') { return NGX_OK; } if (ch == '{') { return NGX_CONF_BLOCK_START; } found = 0; } } } }
static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) { u_char *p, *last, *copy_end, ch, match; size_t looked, i; ngx_http_sub_state_e state; if (ctx->once) { ctx->copy_start = ctx->pos; ctx->copy_end = ctx->buf->last; ctx->pos = ctx->buf->last; ctx->looked.len = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once"); return NGX_AGAIN; } state = ctx->state; looked = ctx->looked.len; last = ctx->buf->last; copy_end = ctx->copy_end; for (p = ctx->pos; p < last; p++) { ch = *p; ch = ngx_tolower(ch); if (state == sub_start_state) { /* the tight loop */ match = ctx->match.data[0]; for ( ;; ) { if (ch == match) { copy_end = p; ctx->looked.data[0] = *p; looked = 1; state = sub_match_state; goto match_started; } if (++p == last) { break; } ch = *p; ch = ngx_tolower(ch); } ctx->state = state; ctx->pos = p; ctx->looked.len = looked; ctx->copy_end = p; if (ctx->copy_start == NULL) { ctx->copy_start = ctx->buf->pos; } return NGX_AGAIN; match_started: continue; } /* state == sub_match_state */ if (ch == ctx->match.data[looked]) { ctx->looked.data[looked] = *p; looked++; if (looked == ctx->match.len) { ctx->state = sub_start_state; ctx->pos = p + 1; ctx->looked.len = 0; ctx->saved.len = 0; ctx->copy_end = copy_end; if (ctx->copy_start == NULL && copy_end) { ctx->copy_start = ctx->buf->pos; } return NGX_OK; } } else { /* * check if there is another partial match in previously * matched substring to catch cases like "aab" in "aaab" */ ctx->looked.data[looked] = *p; looked++; for (i = 1; i < looked; i++) { if (ngx_strncasecmp(ctx->looked.data + i, ctx->match.data, looked - i) == 0) { break; } } if (i < looked) { if (ctx->saved.len > i) { ctx->saved.len = i; } if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) { copy_end = p + 1 - (looked - i); } ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i); looked = looked - i; } else { copy_end = p; looked = 0; state = sub_start_state; } if (ctx->saved.len) { p++; goto out; } } } ctx->saved.len = 0; out: ctx->state = state; ctx->pos = p; ctx->looked.len = looked; ctx->copy_end = (state == sub_start_state) ? p : copy_end; if (ctx->copy_start == NULL && ctx->copy_end) { ctx->copy_start = ctx->buf->pos; } return NGX_AGAIN; }
static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r) { off_t offset; ssize_t n; ngx_fd_t fd; ngx_int_t rc; ngx_err_t err; ngx_str_t pwd, realm, user_file; ngx_uint_t i, level, login, left, passwd; ngx_file_t file; ngx_http_auth_basic_ctx_t *ctx; ngx_http_auth_basic_loc_conf_t *alcf; u_char buf[NGX_HTTP_AUTH_BUF_SIZE]; enum { sw_login, sw_passwd, sw_skip } state; alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module); if (alcf->realm == NULL || alcf->user_file.value.data == NULL) { return NGX_DECLINED; } if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) { return NGX_ERROR; } if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) { return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module); if (ctx) { return ngx_http_auth_basic_crypt_handler(r, ctx, &ctx->passwd, &realm); } rc = ngx_http_auth_basic_user(r); if (rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "no user/password was provided for basic authentication"); return ngx_http_auth_basic_set_realm(r, &realm); } if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { return NGX_ERROR; } fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); if (fd == NGX_INVALID_FILE) { err = ngx_errno; if (err == NGX_ENOENT) { level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; } else { level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_log_error(level, r->connection->log, err, ngx_open_file_n " \"%s\" failed", user_file.data); return rc; } ngx_memzero(&file, sizeof(ngx_file_t)); file.fd = fd; file.name = user_file; file.log = r->connection->log; state = sw_login; passwd = 0; login = 0; left = 0; offset = 0; for ( ;; ) { i = left; n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left, offset); if (n == NGX_ERROR) { ngx_http_auth_basic_close(&file); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (n == 0) { break; } for (i = left; i < left + n; i++) { switch (state) { case sw_login: if (login == 0) { if (buf[i] == '#' || buf[i] == CR) { state = sw_skip; break; } if (buf[i] == LF) { break; } } if (buf[i] != r->headers_in.user.data[login]) { state = sw_skip; break; } if (login == r->headers_in.user.len) { state = sw_passwd; passwd = i + 1; } login++; break; case sw_passwd: if (buf[i] == LF || buf[i] == CR || buf[i] == ':') { buf[i] = '\0'; ngx_http_auth_basic_close(&file); pwd.len = i - passwd; pwd.data = &buf[passwd]; return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &realm); } break; case sw_skip: if (buf[i] == LF) { state = sw_login; login = 0; } break; } } if (state == sw_passwd) { left = left + n - passwd; ngx_memmove(buf, &buf[passwd], left); passwd = 0; } else { left = 0; } offset += n; } ngx_http_auth_basic_close(&file); if (state == sw_passwd) { pwd.len = i - passwd; pwd.data = ngx_pnalloc(r->pool, pwd.len + 1); if (pwd.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1); return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &realm); } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "user \"%V\" was not found in \"%V\"", &r->headers_in.user, &user_file); return ngx_http_auth_basic_set_realm(r, &realm); }
static ngx_int_t ngx_http_internal_redirect_handler(ngx_http_request_t *r) { u_char *p; ngx_uint_t i; ngx_str_t uri, args; ngx_http_script_code_pt code; ngx_http_script_engine_t e; ngx_http_variable_value_t stack[10]; ngx_http_internal_redirect_entry_t *redirects; ngx_http_internal_redirect_main_conf_t *imcf; ngx_http_internal_redirect_loc_conf_t *ilcf; ngx_http_core_main_conf_t *cmcf; ngx_http_phase_handler_t *ph, *cur_ph, *last_ph, tmp; imcf = ngx_http_get_module_main_conf(r, ngx_http_internal_redirect_module); if (!imcf->postponed) { imcf->postponed = 1; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; cur_ph = &ph[r->phase_handler]; last_ph = &ph[cur_ph->next - 1]; if (cur_ph < last_ph) { tmp = *cur_ph; ngx_memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) * sizeof(ngx_http_phase_handler_t)); *last_ph = tmp; r->phase_handler--; /* redo the current ph */ return NGX_DECLINED; } } ilcf = ngx_http_get_module_loc_conf(r, ngx_http_internal_redirect_module); if (ilcf->redirects == NULL) { return NGX_DECLINED; } redirects = ilcf->redirects->elts; for (i = 0; i < ilcf->redirects->nelts; i++) { ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); ngx_memzero(&stack, sizeof(stack)); e.sp = stack; e.ip = redirects[i].codes->elts; e.request = r; e.quote = 1; e.log = 1; e.status = NGX_DECLINED; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code(&e); } e.sp--; if (e.sp->len && (e.sp->len != 1 || e.sp->data[0] != '0')) { break; } } if (i == ilcf->redirects->nelts) { return NGX_DECLINED; } if (redirects[i].code) { return redirects[i].code; } if (redirects[i].lengths) { if (ngx_http_script_run(r, &uri, redirects[i].lengths->elts, 0, redirects->values->elts) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } else { uri = redirects[i].name; } if (uri.data[0] == '@') { (void) ngx_http_named_location(r, &uri); } else { if (uri.data[0] != '/') { p = ngx_pcalloc(r->pool, uri.len + 1); if (p == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } uri.len++; *p = '/'; ngx_memcpy(p + 1, uri.data, uri.len); uri.data = p; } ngx_http_split_args(r, &uri, &args); (void) ngx_http_internal_redirect(r, &uri, &args); } ngx_http_finalize_request(r, NGX_DONE); return NGX_DONE; }
static ngx_inline ngx_int_t ngx_http_modsecurity_save_request_body(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; apr_off_t content_length; ngx_buf_t *buf; ngx_http_core_srv_conf_t *cscf; size_t size; ngx_http_connection_t *hc; ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); apr_brigade_length(ctx->brigade, 0, &content_length); if (r->header_in->end - r->header_in->last >= content_length) { /* use r->header_in */ if (ngx_buf_size(r->header_in)) { /* move to the end */ ngx_memmove(r->header_in->pos + content_length, r->header_in->pos, ngx_buf_size(r->header_in)); } if (apr_brigade_flatten(ctx->brigade, (char *)r->header_in->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { return NGX_ERROR; } apr_brigade_cleanup(ctx->brigade); r->header_in->last += content_length; return NGX_OK; } if (ngx_buf_size(r->header_in)) { /* * ngx_http_set_keepalive will reuse r->header_in if * (r->header_in != c->buffer && r->header_in.last != r->header_in.end), * so we need this code block. * see ngx_http_set_keepalive, ngx_http_alloc_large_header_buffer */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); size = ngx_max(cscf->large_client_header_buffers.size, (size_t)content_length + ngx_buf_size(r->header_in)); hc = r->http_connection; if (hc->nfree && size == cscf->large_client_header_buffers.size) { buf = hc->free[--hc->nfree]; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: use http free large header buffer: %p %uz", buf->pos, buf->end - buf->last); } else if (hc->nbusy < cscf->large_client_header_buffers.num) { if (hc->busy == NULL) { hc->busy = ngx_palloc(r->connection->pool, cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); } if (hc->busy == NULL) { return NGX_ERROR; } else { buf = ngx_create_temp_buf(r->connection->pool, size); } } else { /* TODO: how to deal this case ? */ return NGX_ERROR; } } else { buf = ngx_create_temp_buf(r->pool, (size_t) content_length); } if (buf == NULL) { return NGX_ERROR; } if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { return NGX_ERROR; } apr_brigade_cleanup(ctx->brigade); buf->last += content_length; ngx_memcpy(buf->last, r->header_in->pos, ngx_buf_size(r->header_in)); buf->last += ngx_buf_size(r->header_in); r->header_in = buf; return NGX_OK; }
static ngx_int_t ngx_http_auth_digest_handler(ngx_http_request_t *r) { off_t offset; ssize_t n; ngx_fd_t fd; ngx_int_t rc; ngx_err_t err; ngx_str_t user_file, passwd_line; ngx_file_t file; ngx_uint_t i, begin, tail, idle; ngx_http_auth_digest_loc_conf_t *alcf; ngx_http_auth_digest_cred_t *auth_fields; u_char buf[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; u_char line[NGX_HTTP_AUTH_DIGEST_BUF_SIZE]; u_char *p; // if digest auth is disabled for this location, bail out immediately alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_digest_module); if (alcf->realm.len == 0 || alcf->user_file.value.len == 0) { return NGX_DECLINED; // // BUG? wait wait wait. shouldn't this be ngx_ok by default in the case // of the former and ngx_declined in the latter? // } // unpack the Authorization header (if any) and verify that it contains all // required fields. otherwise send a challenge auth_fields = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_digest_cred_t)); rc = ngx_http_auth_digest_check_credentials(r, auth_fields); if (rc==NGX_DECLINED) { return ngx_http_auth_digest_send_challenge(r, &alcf->realm, 0); }else if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // check for the existence of a passwd file and attempt to open it if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { return NGX_ERROR; } fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); if (fd == NGX_INVALID_FILE) { ngx_uint_t level; err = ngx_errno; if (err == NGX_ENOENT) { level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; } else { level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_log_error(level, r->connection->log, err, ngx_open_file_n " \"%s\" failed", user_file.data); return rc; } ngx_memzero(&file, sizeof(ngx_file_t)); file.fd = fd; file.name = user_file; file.log = r->connection->log; // step through the passwd file and find the individual lines, then pass them off // to be compared against the values in the authorization header passwd_line.data = line; offset = begin = tail = 0; idle = 1; ngx_memzero(buf, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); ngx_memzero(passwd_line.data, NGX_HTTP_AUTH_DIGEST_BUF_SIZE); while (1){ n = ngx_read_file(&file, buf+tail, NGX_HTTP_AUTH_DIGEST_BUF_SIZE-tail, offset); if (n == NGX_ERROR) { ngx_http_auth_digest_close(&file); return NGX_HTTP_INTERNAL_SERVER_ERROR; } begin = 0; for (i=0; i<n+tail; i++){ if (buf[i] == '\n' || buf[i] == '\r'){ if (!idle && i-begin>36){ // 36 is the min length with a single-char name and realm p = ngx_cpymem(passwd_line.data, &buf[begin], i-begin); p[0] = '\0'; passwd_line.len = i-begin; rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); if (rc != NGX_DECLINED){ ngx_http_auth_digest_close(&file); return rc; } } idle = 1; begin = i; }else if(idle){ idle = 0; begin = i; } } if (!idle){ tail = n + tail - begin; if (n==0 && tail>36){ p = ngx_cpymem(passwd_line.data, &buf[begin], tail); p[0] = '\0'; passwd_line.len = i-begin; rc = ngx_http_auth_digest_verify_user(r, auth_fields, &passwd_line); if (rc != NGX_DECLINED){ ngx_http_auth_digest_close(&file); return rc; } }else{ ngx_memmove(buf, &buf[begin], tail); } } if (n==0){ break; } offset += n; } ngx_http_auth_digest_close(&file); // since no match was found based on the fields in the authorization header, // send a new challenge and let the client retry return ngx_http_auth_digest_send_challenge(r, &alcf->realm, auth_fields->stale); }