//garbage-collecting slab allocator void * ngx_http_push_slab_alloc_locked(size_t size) { void *p; if((p = ngx_slab_alloc_locked(ngx_http_push_shpool, size))==NULL) { ngx_http_push_channel_queue_t *ccur, *cnext; ngx_uint_t collected = 0; //failed. emergency garbage sweep, then. //collect channels ngx_queue_init(&channel_gc_sentinel.queue); ngx_http_push_walk_rbtree(ngx_http_push_channel_collector); for(ccur=(ngx_http_push_channel_queue_t *)ngx_queue_next(&channel_gc_sentinel.queue); ccur != &channel_gc_sentinel; ccur=cnext) { cnext = (ngx_http_push_channel_queue_t *)ngx_queue_next(&ccur->queue); ngx_http_push_delete_channel_locked(ccur->channel); ngx_free(ccur); collected++; } //todo: collect worker messages maybe ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, "push module: out of shared memory. emergency garbage collection deleted %ui unused channels.", collected); return ngx_slab_alloc_locked(ngx_http_push_shpool, size); } return p; }
// find a channel by id. if channel not found, make one, insert it, and return that. static ngx_http_push_stream_channel_t * ngx_http_push_stream_get_channel(ngx_str_t *id, ngx_log_t *log, ngx_http_push_stream_loc_conf_t *cf) { ngx_http_push_stream_main_conf_t *mcf = ngx_http_push_stream_module_main_conf; ngx_http_push_stream_shm_data_t *data = (ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data; ngx_http_push_stream_channel_t *channel; ngx_slab_pool_t *shpool = (ngx_slab_pool_t *) ngx_http_push_stream_shm_zone->shm.addr; ngx_flag_t is_broadcast_channel = 0; channel = ngx_http_push_stream_find_channel(id, log); if (channel != NULL) { // we found our channel return channel; } ngx_shmtx_lock(&shpool->mutex); // check again to see if any other worker didn't create the channel channel = ngx_http_push_stream_find_channel(id, log); if (channel != NULL) { // we found our channel ngx_shmtx_unlock(&shpool->mutex); return channel; } if ((mcf->broadcast_channel_prefix.len > 0) && (ngx_strncmp(id->data, mcf->broadcast_channel_prefix.data, mcf->broadcast_channel_prefix.len) == 0)) { is_broadcast_channel = 1; } if (((!is_broadcast_channel) && (mcf->max_number_of_channels != NGX_CONF_UNSET_UINT) && (mcf->max_number_of_channels == data->channels)) || ((is_broadcast_channel) && (mcf->max_number_of_broadcast_channels != NGX_CONF_UNSET_UINT) && (mcf->max_number_of_broadcast_channels == data->broadcast_channels))) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_PUSH_STREAM_NUMBER_OF_CHANNELS_EXCEEDED; } if ((channel = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_push_stream_channel_t))) == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NULL; } if ((channel->id.data = ngx_slab_alloc_locked(shpool, id->len + 1)) == NULL) { ngx_slab_free_locked(shpool, channel); ngx_shmtx_unlock(&shpool->mutex); return NULL; } channel->id.len = id->len; ngx_memcpy(channel->id.data, id->data, channel->id.len); channel->id.data[channel->id.len] = '\0'; channel->broadcast = is_broadcast_channel; ngx_http_push_stream_initialize_channel(channel); // initialize workers_with_subscribers queues only when a channel is created ngx_queue_init(&channel->workers_with_subscribers); ngx_shmtx_unlock(&shpool->mutex); return channel; }
static ngx_http_push_stream_pid_queue_t * ngx_http_push_stream_create_worker_subscriber_channel_sentinel_locked(ngx_slab_pool_t *shpool, ngx_str_t *channel_id, ngx_log_t *log) { ngx_http_push_stream_pid_queue_t *worker_sentinel; ngx_http_push_stream_channel_t *channel; // check if channel still exists if ((channel = ngx_http_push_stream_find_channel(channel_id, log)) == NULL) { ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: something goes very wrong, arrived on ngx_http_push_stream_subscriber_assign_channel without created channel %s", channel_id->data); return NULL; } if ((worker_sentinel = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_push_stream_pid_queue_t))) == NULL) { ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: unable to allocate worker subscriber queue marker in shared memory"); return NULL; } // initialize ngx_queue_insert_tail(&channel->workers_with_subscribers, &worker_sentinel->queue); worker_sentinel->pid = ngx_pid; worker_sentinel->slot = ngx_process_slot; ngx_queue_init(&worker_sentinel->subscribers_sentinel.queue); return worker_sentinel; }
static ngx_btt_peer_t * ngx_btt_get_peer(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx, ngx_rbtree_t *tree, ngx_queue_t *queue) { ngx_queue_t *q; ngx_btt_peer_t *p; p = NULL; if (!ngx_queue_empty(&bcf->btt->free_peers)) { q = ngx_queue_head(&bcf->btt->free_peers); ngx_queue_remove(q); p = ngx_queue_data(q, ngx_btt_peer_t, queue); } if (p == NULL) { p = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_peer_t)); if (p == NULL) { return NULL; } } /* TODO */ ngx_memcpy(&p->connection_id, &ctx->connection_id, sizeof(ctx->connection_id)); p->node.key = ngx_crc32_short((u_char *) &ctx->connection_id, sizeof(ctx->connection_id)); ngx_rbtree_insert(tree, &p->node); ngx_queue_insert_head(queue, &p->queue); return p; }
static ngx_http_cloudrouter_remote_t* ngx_http_cloudrouter_add_remote_locked(ngx_http_cloudrouter_node_t* e) { ngx_http_cloudrouter_remote_t *remote = NULL; if (e->remote == NULL) { e->remote = ngx_slab_alloc_locked(ngx_http_cloudrouter_shpool, sizeof(ngx_http_cloudrouter_remote_t)); remote = e->remote; } else { remote = e->remote; while (remote->next != NULL) { remote = remote->next; } remote->next = ngx_slab_alloc_locked(ngx_http_cloudrouter_shpool, sizeof(ngx_http_cloudrouter_remote_t)); remote = remote->next; } ngx_memzero(remote, sizeof(ngx_http_cloudrouter_remote_t)); return remote; }
void *ngx_status_get_momerybyhost_brtree(ngx_http_request_t *r) { ssize_t n,len; uint32_t hash; ngx_tcp_io *io_ret; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node, *o_node; ngx_status_ex_host_ctx_t ctx; if (r == NULL || r->headers_in.host == NULL) return NULL; if (status_ex_shm_zone == NULL || status_ex_shm_zone->data == NULL) return NULL; if (r->headers_in.host->value.len > URL_LEN) return NULL; ctx.rbtree = *(ngx_rbtree_t **)status_ex_shm_zone->data; hash = ngx_crc32_short(r->headers_in.host->value.data, r->headers_in.host->value.len); node = ngx_host_list_lookup(ctx.rbtree, &r->headers_in.host->value, hash); /*==NULL insert new node*/ if (node == NULL) { len = r->headers_in.host->value.len; shpool = (ngx_slab_pool_t *)status_ex_shm_zone->shm.addr; n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_tcp_io, host) + len; node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { return NULL; } io_ret = (ngx_tcp_io *) &node->data; node->key = hash; io_ret->host_len= len; ngx_memcpy(io_ret->host, r->headers_in.host->value.data, r->headers_in.host->value.len); ngx_shmtx_lock(&shpool->mutex); /*secend find*/ o_node = ngx_host_list_lookup(ctx.rbtree, &r->headers_in.host->value, hash); if (o_node == NULL) { ngx_rbtree_insert(ctx.rbtree, node); statu_url_io_array->ngx_tcp_io[statu_url_io_array->number] = io_ret; statu_url_io_array->number++; } ngx_shmtx_unlock(&shpool->mutex); } return (ngx_tcp_io *) &node->data; }
static ngx_int_t ngx_http_vhost_traffic_status_dump_restore_add_node(ngx_event_t *ev, ngx_http_vhost_traffic_status_node_t *ovtsn, ngx_str_t *key) { size_t size; uint32_t hash; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node; ngx_http_vhost_traffic_status_ctx_t *ctx; ngx_http_vhost_traffic_status_node_t *vtsn; ctx = ev->data; if (key->len == 0) { return NGX_ERROR; } shpool = (ngx_slab_pool_t *) ctx->shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); /* find node */ hash = ngx_crc32_short(key->data, key->len); node = ngx_http_vhost_traffic_status_node_lookup(ctx->rbtree, key, hash); /* copy node */ if (node == NULL) { size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_vhost_traffic_status_node_t, data) + key->len; node = ngx_slab_alloc_locked(shpool, size); if (node == NULL) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "dump_restore_add_node::ngx_slab_alloc_locked() failed"); ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; } vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; node->key = hash; *vtsn = *ovtsn; ngx_memcpy(vtsn->data, key->data, key->len); ngx_rbtree_insert(ctx->rbtree, node); } ngx_shmtx_unlock(&shpool->mutex); return NGX_OK; }
/* * 添加缓存文件到红黑树和队列中,通过 ngx_http_file_cache_t 结构管理此节点 */ static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_http_file_cache_node_t *fcn; ngx_shmtx_lock(&cache->shpool->mutex); // 根据key在二叉树中查找,没有找到添加一个节点到cache中 fcn = ngx_http_file_cache_lookup(cache, c->key); if (fcn == NULL) { fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_ERROR; } // 设置node结点key ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); // 当前结点插入到二叉树中 ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); fcn->uses = 1; fcn->count = 0; fcn->valid_msec = 0; fcn->error = 0; fcn->exists = 1; fcn->updating = 0; fcn->deleting = 0; fcn->uniq = 0; fcn->valid_sec = 0; fcn->body_start = 0; fcn->fs_size = c->fs_size; cache->sh->size += c->fs_size; } else { ngx_queue_remove(&fcn->queue); } // 设置过期时间(当前时间+不活跃时间) fcn->expire = ngx_time() + cache->inactive; // 插入到队列头部,先插入的在队列的尾部 ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_OK; }
void * ngx_http_session_shm_alloc_nolock(size_t size) { void *p; ngx_http_session_list_t *session_list; session_list = ngx_http_session_shm_zone->data; p = ngx_slab_alloc_locked(session_list->shpool, size); return p; }
void * ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size) { void *p; ngx_shmtx_lock(&pool->mutex); p = ngx_slab_alloc_locked(pool, size); ngx_shmtx_unlock(&pool->mutex); return p; }
static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_http_file_cache_node_t *fcn; ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_http_file_cache_lookup(cache, c->key); if (fcn == NULL) { fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_ERROR; } ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); fcn->uses = 1; fcn->count = 0; fcn->valid_msec = c->valid_msec; fcn->error = 0; fcn->exists = 1; fcn->updating = 0; fcn->deleting = 0; fcn->uniq = c->uniq; fcn->valid_sec = c->valid_sec; fcn->body_start = c->body_start; fcn->fs_size = c->fs_size; cache->sh->size += c->fs_size; } else { ngx_queue_remove(&fcn->queue); } fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_OK; }
void * ngx_http_session_shm_alloc(size_t size) { ngx_http_session_list_t *session_list; void *p; session_list = ngx_http_session_shm_zone->data; ngx_shmtx_lock(&session_list->shpool->mutex); p = ngx_slab_alloc_locked(session_list->shpool, size); ngx_shmtx_unlock(&session_list->shpool->mutex); return p; }
static ngx_int_t setup_default_servers(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { ngx_http_upstream_available_capacity_srv_conf_t *conf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_available_capacity_module); if (conf->server_list) return NGX_OK; ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)us->shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); ngx_http_upstream_available_capacity_server_t *prev_server_conf = NULL; size_t i = 0; for (i = 0; i < us->servers->nelts; ++i) { ngx_http_upstream_server_t *servers = us->servers->elts; ngx_http_upstream_available_capacity_server_t *server_conf = ngx_slab_calloc_locked(shpool, sizeof(ngx_http_upstream_available_capacity_server_t)); server_conf->server = &servers[i]; server_conf->capacity = 1; ngx_str_t server_address = ngx_string(server_conf->server->name.data); ngx_url_t url; ngx_memzero(&url, sizeof(ngx_url_t)); size_t server_address_size = strlen((char *)server_address.data); url.url.len = server_address_size; url.url.data = ngx_slab_alloc_locked(shpool, server_address_size); url.default_port = 80; ngx_cpystrn(url.url.data, server_address.data, server_address_size + 1); if (ngx_parse_url_slab(shpool, &url) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "cannot parse url : %s", server_address.data); return NGX_DECLINED; } server_conf->addr = *url.addrs; server_conf->next = NULL; if (prev_server_conf) { prev_server_conf->next = server_conf; } else { conf->server_list = server_conf; } prev_server_conf = server_conf; } conf->server_num = us->servers->nelts; conf->search_start_peer = conf->server_list; ngx_shmtx_unlock(&shpool->mutex); return NGX_OK; }
ngx_int_t ngx_http_tfs_parse_tair_server_addr_info(ngx_http_tfs_tair_server_addr_info_t *info, u_char *addr, uint32_t len, void *pool, uint8_t shared_memory) { u_char *temp, *p; ssize_t info_size; ngx_int_t i; p = addr; for (i = 0; i < NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT; i++) { temp = ngx_strlchr(p, p + len, ';'); if (temp == NULL) { return NGX_ERROR; } info_size = temp - p; if (shared_memory) { info->server[i].data = ngx_slab_alloc_locked((ngx_slab_pool_t *)pool, info_size); } else { info->server[i].data = ngx_pcalloc((ngx_pool_t *)pool, info_size); } if (info->server[i].data == NULL) { return NGX_ERROR; } info->server[i].len = info_size; memcpy(info->server[i].data, p, info_size); p += info_size + 1; len -= (info_size + 1); if (len <= 0) { return NGX_ERROR; } } info->area = ngx_atoi(p, len); if (info->area == NGX_ERROR) { return NGX_ERROR; } return NGX_OK; }
static ngx_btt_peer_info_t * ngx_btt_get_peer_info(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx) { ngx_queue_t *q; ngx_btt_peer_info_t *pi; if (!ngx_queue_empty(&bcf->btt->free_peer_infos)) { q = ngx_queue_head(&bcf->btt->free_peer_infos); ngx_queue_remove(q); pi = ngx_queue_data(q, ngx_btt_peer_info_t, queue); return pi; } pi = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_peer_info_t)); if (pi == NULL) { return NULL; } return pi; }
static ngx_int_t ngx_http_push_stream_send_worker_message_locked(ngx_http_push_stream_channel_t *channel, ngx_http_push_stream_queue_elem_t *subscribers_sentinel, ngx_pid_t pid, ngx_int_t worker_slot, ngx_http_push_stream_msg_t *msg, ngx_flag_t *queue_was_empty, ngx_log_t *log) { ngx_slab_pool_t *shpool = (ngx_slab_pool_t *) ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_worker_data_t *workers_data = ((ngx_http_push_stream_shm_data_t *) ngx_http_push_stream_shm_zone->data)->ipc; ngx_http_push_stream_worker_data_t *thisworker_data = workers_data + worker_slot; ngx_http_push_stream_worker_msg_t *newmessage; if ((newmessage = ngx_slab_alloc_locked(shpool, sizeof(ngx_http_push_stream_worker_msg_t))) == NULL) { ngx_log_error(NGX_LOG_ERR, log, 0, "push stream module: unable to allocate worker message, pid: %P, slot: %d", pid, worker_slot); return NGX_ERROR; } msg->workers_ref_count++; newmessage->msg = msg; newmessage->pid = pid; newmessage->subscribers_sentinel = subscribers_sentinel; newmessage->channel = channel; *queue_was_empty = ngx_queue_empty(&thisworker_data->messages_queue->queue); ngx_queue_insert_tail(&thisworker_data->messages_queue->queue, &newmessage->queue); return NGX_OK; }
static ngx_btt_torrent_t * ngx_btt_get_torrent(ngx_btt_conf_t *bcf, ngx_btt_ctx_t *ctx, ngx_rbtree_t *tree, ngx_queue_t *queue) { ngx_queue_t *q; ngx_btt_torrent_t *t; t = NULL; if (!ngx_queue_empty(&bcf->btt->free_torrents)) { q = ngx_queue_head(&bcf->btt->free_torrents); ngx_queue_remove(q); t = ngx_queue_data(q, ngx_btt_torrent_t, queue); } if (t == NULL) { t = ngx_slab_alloc_locked(bcf->pool, sizeof(ngx_btt_torrent_t)); if (t == NULL) { return NULL; } } /* TODO */ ngx_rbtree_init(&t->peers_rbtree, &t->peers_sentinel, ngx_btt_insert_peer); ngx_queue_init(&t->peers_queue); ngx_memcpy(t->info_hash, ctx->info_hash, sizeof(ctx->info_hash)); t->node.key = ngx_crc32_short(ctx->info_hash, sizeof(ctx->info_hash)); ngx_rbtree_insert(tree, &t->node); ngx_queue_insert_head(queue, &t->queue); return t; }
static ngx_int_t ngx_http_req_status_handler(ngx_http_request_t *r) { size_t len; uint32_t hash; ngx_str_t key; ngx_uint_t i; ngx_shm_zone_t **pzone; ngx_pool_cleanup_t *cln; ngx_http_req_status_ctx_t *r_ctx; ngx_http_req_status_zone_t *ctx; ngx_http_req_status_node_t *ssn; ngx_http_req_status_loc_conf_t *rlcf; ngx_http_req_status_zone_node_t *pzn; r_ctx = ngx_http_get_module_ctx(r, ngx_http_req_status_module); rlcf = ngx_http_get_module_loc_conf(r, ngx_http_req_status_module); do { pzone = rlcf->req_zones.elts; for (i = 0; i < rlcf->req_zones.nelts; i++) { ctx = pzone[i]->data; if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { continue; } if (key.len == 0) { continue; } if (key.len > 65535) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "req-status, the value of the \"%V\" variable " "is more than 65535 bytes: \"%v\"", &ctx->key.value, &key); continue; } if (r_ctx == NULL) { r_ctx = ngx_palloc(r->pool, sizeof(ngx_http_req_status_ctx_t)); if (r_ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_array_init(&r_ctx->req_zones, r->pool, 2, sizeof(ngx_http_req_status_zone_node_t)) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_http_req_status_cleanup; cln->data = r_ctx; ngx_http_set_ctx(r, r_ctx, ngx_http_req_status_module); } hash = ngx_crc32_short(key.data, key.len); ngx_shmtx_lock(&ctx->shpool->mutex); ssn = ngx_http_req_status_lookup(ctx, hash, &key); if (ssn == NULL) { len = sizeof(ngx_http_req_status_node_t) + key.len + 1; ssn = ngx_slab_alloc_locked(ctx->shpool, len); if (ssn == NULL) { ngx_http_req_status_expire(ctx); ssn = ngx_slab_alloc_locked(ctx->shpool, len); if (ssn == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "req-status, slab alloc fail, zone = \"%V\", " "key = \"%V\", size = %uz", &ctx->shm_zone->shm.name, &key, len); ngx_shmtx_unlock(&ctx->shpool->mutex); continue; } } ssn->node.key = hash; ssn->len = key.len; ssn->count = 1; ngx_memzero(&ssn->data, sizeof(ssn->data)); ngx_memcpy(ssn->key, key.data, key.len); ssn->key[key.len] = '\0'; ssn->last_traffic_update = 0; ngx_rbtree_insert(&ctx->sh->rbtree, &ssn->node); } ssn->data.requests ++; ssn->active ++; if (ssn->active > ssn->data.max_active) { ssn->data.max_active = ssn->active; } ngx_queue_insert_head(&ctx->sh->queue, &ssn->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); pzn = ngx_array_push(&r_ctx->req_zones); if (pzn == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } pzn->node = ssn; pzn->zone = ctx; } rlcf = rlcf->parent; } while (rlcf); return NGX_DECLINED; }
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account) { size_t size; ngx_int_t rc, excess; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; now = ngx_current_msec; ctx = limit->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req_node_t *) &node->color; rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len); if (rc == 0) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } if (account) { lr->excess = excess; lr->last = now; return NGX_OK; } lr->count++; ctx->node = lr; return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; } *ep = 0; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + key->len; ngx_http_limit_req_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "could not allocate node%s", ctx->shpool->log_ctx); return NGX_ERROR; } } node->key = hash; lr = (ngx_http_limit_req_node_t *) &node->color; lr->len = (u_short) key->len; lr->excess = 0; ngx_memcpy(lr->data, key->data, key->len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); if (account) { lr->last = now; lr->count = 0; return NGX_OK; } lr->last = 0; lr->count = 1; ctx->node = lr; return NGX_AGAIN; }
ngx_http_reqstat_rbnode_t * ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val) { size_t size, len; uint32_t hash; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_reqstat_ctx_t *ctx; ngx_http_reqstat_rbnode_t *rs; ctx = shm_zone->data; hash = ngx_murmur_hash2(val->data, val->len); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ngx_shmtx_lock(&ctx->shpool->mutex); while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ rs = (ngx_http_reqstat_rbnode_t *) &node->color; /* len < node->len */ if (val->len < (size_t) rs->len) { node = node->left; continue; } rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len); if (rc == 0) { ms = (ngx_msec_int_t) (now - rs->last_visit); rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000 + 1000; rs->last_visit = now; if (rs->excess > 0) { ngx_queue_remove(&rs->visit); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); } ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup exist: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; } node = (rc < 0) ? node->left : node->right; } rc = 0; node = NULL; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_reqstat_rbnode_t, data) + ctx->key_len; if (ctx->alloc_already_fail == 0) { node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ctx->alloc_already_fail = 1; } } if (node == NULL) { /* try to free a vacant node */ q = ngx_queue_last(&ctx->sh->visit); rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit); ms = (ngx_msec_int_t) (now - rs->last_visit); excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000; ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess); if (excess < 0) { rc = 1; node = (ngx_rbtree_node_t *) ((char *) rs - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_queue_remove(&rs->visit); ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup recycle: %*s", rs->len, rs->data); rs->conn_total = 0; ngx_memzero((void *) &rs->bytes_in, size - offsetof(ngx_rbtree_node_t, color) - offsetof(ngx_http_reqstat_rbnode_t, bytes_in)); } else { ngx_shmtx_unlock(&ctx->shpool->mutex); return NULL; } } node->key = hash; rs = (ngx_http_reqstat_rbnode_t *) &node->color; len = ngx_min(ctx->key_len, (ssize_t) val->len); ngx_memcpy(rs->data, val->data, len); rs->len = len; ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); if (!rc) { ngx_queue_insert_head(&ctx->sh->queue, &rs->queue); } rs->last_visit = now; rs->excess = 1000; ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; }
static ngx_int_t ngx_http_limit_speed_handler(ngx_http_request_t *r) { size_t len, n; uint32_t hash; ngx_int_t rc; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node, *sentinel; ngx_pool_cleanup_t *cln; ngx_http_variable_value_t *vv; ngx_http_limit_speed_ctx_t *ctx; ngx_http_limit_speed_node_t *ls; ngx_http_limit_speed_conf_t *lscf; ngx_http_limit_speed_cleanup_t *lscln; ngx_http_limit_speed_req_ctx_t *rctx; if (r->main->limit_rate) { return NGX_DECLINED; } lscf = ngx_http_get_module_loc_conf(r, ngx_http_limit_speed_module); if (lscf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lscf->shm_zone->data; vv = ngx_http_get_indexed_variable(r, ctx->index); if (vv == NULL || vv->not_found) { return NGX_DECLINED; } len = vv->len; if (len == 0) { return NGX_DECLINED; } if (lscf->var_max_len) { len = len > lscf->var_max_len ? lscf->var_max_len : len; } hash = ngx_crc32_short(vv->data, len); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_speed_cleanup_t)); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } shpool = (ngx_slab_pool_t *) lscf->shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); node = ctx->rbtree->root; sentinel = ctx->rbtree->sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ do { ls = (ngx_http_limit_speed_node_t *) &node->color; rc = ngx_memn2cmp(vv->data, ls->data, len, (size_t) ls->len); if (rc == 0) { ls->conn++; goto done; } node = (rc < 0) ? node->left : node->right; } while (node != sentinel && hash == node->key); break; } n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_speed_node_t, data) + len; node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_DECLINED; } ls = (ngx_http_limit_speed_node_t *) &node->color; node->key = hash; ls->len = (u_char) len; ls->conn = 1; ngx_memcpy(ls->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); done: r->main->limit_rate = lscf->speed / ls->conn; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit speed zone: %08XD conn=%d, speed=%d", node->key, ls->conn, r->main->limit_rate); ngx_shmtx_unlock(&shpool->mutex); cln->handler = ngx_http_limit_speed_cleanup; lscln = cln->data; lscln->shm_zone = lscf->shm_zone; lscln->node = node; if (ngx_http_limit_speed_get_ctx(r) != NGX_OK) { return NGX_DECLINED; } rctx = ngx_http_get_module_ctx(r, ngx_http_limit_speed_module); rctx->speed = lscf->speed; rctx->ls = ls; return NGX_DECLINED; }
static ngx_int_t ngx_http_session_store_by_key(ngx_http_request_t *r) { u_char *last; size_t n, len; uint32_t hash; ngx_int_t rc; ngx_uint_t i, index; ngx_rbtree_node_t *node; ngx_http_variable_value_t *v, *vv; ngx_http_session_store_ctx_t *ctx; ngx_http_session_store_node_t *ssn; ngx_http_session_store_loc_conf_t *sslcf; sslcf = ngx_http_get_module_loc_conf(r, ngx_http_session_store_module); if (sslcf->store_shm_zone == NULL) { return NGX_DECLINED; } ctx = sslcf->store_shm_zone->data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session ctx: %p", ctx); v = ngx_http_get_indexed_variable(r, ctx->index); if (v == NULL || v->not_found) { return NGX_DECLINED; } len = v->len; if (len == 0) { return NGX_DECLINED; } if (len > 65535) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 65535 bytes: \"%v\"", &ctx->var, v); return NGX_ERROR; } hash = ngx_crc32_short(v->data, len); ngx_shmtx_lock(&ctx->shpool->mutex); ngx_http_session_store_expire(ctx, 1); rc = ngx_http_session_store_lookup(ctx, hash, v->data, len, &ssn); /* Find, delete the old one */ if (ssn) { ngx_http_session_store_delete(ctx, ssn); }; /* Not find */ n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_session_store_node_t, data) + len; for (i = 0; i < 8; i++) { index = sslcf->store_index[i]; if (index == NGX_CONF_UNSET_UINT) { break; } vv = ngx_http_get_indexed_variable(r, index); if (vv == NULL || vv->not_found) { break; } n += vv->len; } node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_http_session_store_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "not enough share memory for zone \"%V\"", &sslcf->store_shm_zone->shm.name); return NGX_ERROR; } } ssn = (ngx_http_session_store_node_t *) &node->color; node->key = hash; ssn->key_len = (u_char) len; ssn->expire = ngx_time() + sslcf->expire; last = ssn->data; ngx_memcpy(last, v->data, len); last += len; for (i = 0; i < 8; i++) { index = sslcf->store_index[i]; if (index == NGX_CONF_UNSET_UINT) { break; } vv = ngx_http_get_indexed_variable(r, index); if (vv == NULL || vv->not_found) { break; } ssn->record[i].start = last - ssn->data; ssn->record[i].length = vv->len; ngx_memcpy(last, vv->data, vv->len); last += vv->len; } ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &ssn->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; }
static void ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, void *data) { ngx_stream_upstream_rr_peer_data_t *rrp = data; ngx_ssl_session_t *old_ssl_session, *ssl_session; ngx_stream_upstream_rr_peer_t *peer; #if (NGX_STREAM_UPSTREAM_ZONE) int len; u_char *p; ngx_stream_upstream_rr_peers_t *peers; u_char buf[NGX_SSL_MAX_SESSION_SIZE]; #endif #if (NGX_STREAM_UPSTREAM_ZONE) peers = rrp->peers; if (peers->shpool) { ssl_session = SSL_get0_session(pc->connection->ssl->connection); if (ssl_session == NULL) { return; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "save session: %p", ssl_session); len = i2d_SSL_SESSION(ssl_session, NULL); /* do not cache too big session */ if (len > NGX_SSL_MAX_SESSION_SIZE) { return; } p = buf; (void) i2d_SSL_SESSION(ssl_session, &p); peer = rrp->current; ngx_stream_upstream_rr_peers_rlock(peers); ngx_stream_upstream_rr_peer_lock(peers, peer); if (len > peer->ssl_session_len) { ngx_shmtx_lock(&peers->shpool->mutex); if (peer->ssl_session) { ngx_slab_free_locked(peers->shpool, peer->ssl_session); } peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len); ngx_shmtx_unlock(&peers->shpool->mutex); if (peer->ssl_session == NULL) { peer->ssl_session_len = 0; ngx_stream_upstream_rr_peer_unlock(peers, peer); ngx_stream_upstream_rr_peers_unlock(peers); return; } peer->ssl_session_len = len; } ngx_memcpy(peer->ssl_session, buf, len); ngx_stream_upstream_rr_peer_unlock(peers, peer); ngx_stream_upstream_rr_peers_unlock(peers); return; } #endif ssl_session = ngx_ssl_get_session(pc->connection); if (ssl_session == NULL) { return; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "save session: %p", ssl_session); peer = rrp->current; old_ssl_session = peer->ssl_session; peer->ssl_session = ssl_session; if (old_ssl_session) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, "old session: %p", old_ssl_session); /* TODO: may block */ ngx_ssl_free_session(old_ssl_session); } }
int ngx_tcp_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, size_t key_len, int value_type, u_char *str_value_buf, size_t str_value_len, double num_value, int exptime, int user_flags, char **errmsg, int *forcible) { int i, n; u_char c, *p; uint32_t hash; ngx_int_t rc; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_tcp_lua_shdict_ctx_t *ctx; ngx_tcp_lua_shdict_node_t *sd; if (zone == NULL) { return NGX_ERROR; } dd("exptime: %d", exptime); ctx = zone->data; *forcible = 0; hash = ngx_crc32_short(key, key_len); switch (value_type) { case LUA_TSTRING: /* do nothing */ break; case LUA_TNUMBER: dd("num value: %lf", num_value); str_value_buf = (u_char *) &num_value; str_value_len = sizeof(double); break; case LUA_TBOOLEAN: c = num_value ? 1 : 0; str_value_buf = &c; str_value_len = sizeof(u_char); break; case LUA_TNIL: if (op & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) { *errmsg = "attempt to add or replace nil values"; return NGX_ERROR; } str_value_buf = NULL; str_value_len = 0; break; default: *errmsg = "unsupported value type"; return NGX_ERROR; } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_tcp_lua_shdict_expire(ctx, 1); #endif rc = ngx_tcp_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("lookup returns %d", (int) rc); if (op & NGX_TCP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "not found"; return NGX_DECLINED; } /* rc == NGX_OK */ goto replace; } if (op & NGX_TCP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "exists"; return NGX_DECLINED; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (str_value_buf && str_value_len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry bug value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_tcp_lua_shdict_node_t, data) + key_len + str_value_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (op & NGX_TCP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%*s\"", key_len, key); for (i = 0; i < 30; i++) { if (ngx_tcp_lua_shdict_expire(ctx, 0) == 0) { break; } *forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } allocated: sd = (ngx_tcp_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; }
/* * ngx_http_ip_blacklist_update * * This function updates the blacklisting count according to @addr. * If the count reaches max threshold, which is indicated by @max, the source * IP address will be added to the real blacklist. * * @addr: IP address of the request * @max: threshold of failure number * @module: ngx_module_t of the calling module * * Return values: * 0: not blacklisted * 1: blacklisted * -1: error occured */ ngx_int_t ngx_http_ip_blacklist_update(ngx_http_request_t *r, ngx_str_t *addr, ngx_int_t max, ngx_module_t *module) { ngx_http_ip_blacklist_main_conf_t *imcf; ngx_http_ip_blacklist_loc_conf_t *ilcf; ngx_http_ip_blacklist_t *node; ngx_http_ip_blacklist_tree_t *blacklist; uint32_t hash; ngx_int_t i, sys = 0, ret = 0; imcf = ngx_http_get_module_main_conf(r, ngx_http_ip_blacklist_module); ilcf = ngx_http_get_module_loc_conf(r, ngx_http_ip_blacklist_module); if (!imcf->enabled) { return 0; } if (addr->len > NGX_HTTP_IP_BLACKLIST_ADDR_LEN) { return -1; } blacklist = ngx_http_ip_blacklist_shm_zone->data; ngx_shmtx_lock(&blacklist->shpool->mutex); if (r->ip_blacklist_node) { node = r->ip_blacklist_node; } else { /* maybe other requests set the node, so let's do a lookup */ hash = ngx_crc32_short(addr->data, addr->len); node = ngx_http_ip_blacklist_lookup(&blacklist->blacklist, addr, hash); if (node == NULL) { /* add new rbtree item to record this addr */ node = ngx_slab_alloc_locked(blacklist->shpool, sizeof(ngx_http_ip_blacklist_t)); if (node == NULL) { ngx_shmtx_unlock(&blacklist->shpool->mutex); return -1; } memset(node, 0, sizeof(ngx_http_ip_blacklist_t)); memcpy(node->addr, addr->data, addr->len); node->len = (u_short)addr->len; node->timeout = ngx_time() + imcf->timeout; for (i = 0; i < NGX_HTTP_IP_BLACKLIST_MOD_NUM; i++) { if (ngx_http_ip_blacklist_modules[i] != NULL) { node->counts[i].module = ngx_http_ip_blacklist_modules[i]; if (ngx_http_ip_blacklist_modules[i] == module) { node->counts[i].count++; break; } } } node->node.key = hash; ngx_rbtree_insert(&blacklist->blacklist, &node->node); ngx_queue_insert_head(&blacklist->garbage, &node->queue); r->ip_blacklist_node = node; node->ref++; ngx_http_ip_blacklist_request_cleanup_init(r); ngx_shmtx_unlock(&blacklist->shpool->mutex); return 0; } } if (node->blacklist) { /* Perhaps other workers set this IP addr to match with max count */ ngx_shmtx_unlock(&blacklist->shpool->mutex); return 1; } if (node->timed) { /* node is timed out, reuse this node, reset the count */ node->timed = 0; for (i = 0; i < NGX_HTTP_IP_BLACKLIST_MOD_NUM; i++) { node->counts[i].count = 0; } } /* otherwise, increase the count and check if it matches with max count */ for (i = 0; i < NGX_HTTP_IP_BLACKLIST_MOD_NUM; i++) { if (node->counts[i].module == module) { if (++(node->counts[i].count) == max) { if (imcf->mode == NGX_HTTP_BLACKLIST_MODE_LOCAL) { node->blacklist = 1; } else { sys = 1; } ngx_shmtx_unlock(&blacklist->shpool->mutex); if (ilcf->log_enabled) { ngx_http_ip_blacklist_write_attack_log(r, addr, sys); } if (sys == 0) { /* in local mode, just return */ return 1; } /* in sys mode */ if (imcf->syscmd.len != 0) { /* build up a command and call system */ memset(imcf->buf, 0, imcf->buf_len); ngx_snprintf(imcf->buf, imcf->buf_len, (char *)imcf->syscmd.data, addr); /* TODO: fix this */ ret = system((char *)imcf->buf); return (ret == 0) ? 1 : -1; } /* no sys comamnd provided */ return 0; } break; } } ngx_shmtx_unlock(&blacklist->shpool->mutex); return 0; }
static ngx_int_t ngx_http_ip_behavior_lookup(ngx_http_request_t *r, ngx_uint_t hash, ngx_http_ip_behavior_ctx_t *ctx, ngx_http_ip_behavior_conf_t *ilcf) { size_t size; ngx_int_t rc; ngx_time_t *tp; ngx_msec_t now; ngx_rbtree_node_t *node, *sentinel; ngx_http_ip_behavior_node_t *ibn; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; rc = -1; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ ibn = (ngx_http_ip_behavior_node_t *) &node->color; rc = ngx_memn2cmp(r->connection->addr_text.data, ibn->addr, r->connection->addr_text.len, (size_t) ibn->len); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior lookup is : %i", rc); if (rc == 0) { /* found the node */ /* check expire */ if (ngx_abs((ngx_msec_int_t)(now - ibn->last)) >= ctx->sample_cycle) { /* node is expired, clear counters */ ibn->insensitive = 0; ibn->total = 0; ibn->bad_response = 0; } ngx_queue_remove(&ibn->queue); ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue); ibn->last = now; if (ilcf->type & TYPE_SENSITIVE_URL) { if (!ilcf->sensitive) { ibn->insensitive++; } } else if (ilcf->type & TYPE_BAD_RESPONSE) { /* TODO: support bad response behavior */ } else { /* should never be here */ } /* total can be zero when it grows to big, * so need to reset all the counters */ if (++ibn->total == 0) { ibn->insensitive = 0; ibn->bad_response = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior node reset: %V", &r->connection->addr_text); } if (ibn->total >= (ngx_uint_t)ctx->sample_base) { r->insensitive_percent = (float)ibn->insensitive / (float)ibn->total * 100; r->bad_response_percent = (float)ibn->bad_response / (float)ibn->total * 100; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior find node: %V, total: %i, insens: %i," " percent: %i", &r->connection->addr_text, ibn->total, ibn->insensitive, r->insensitive_percent); return NGX_OK; } node = (rc < 0) ? node->left : node->right; } /* create a new node */ size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_ip_behavior_node_t, addr) + r->connection->addr_text.len; node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_ip_behavior_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { return NGX_ERROR; } } node->key = hash; ibn = (ngx_http_ip_behavior_node_t *) &node->color; ibn->len = (u_char) r->connection->addr_text.len; ngx_memcpy(ibn->addr, r->connection->addr_text.data, r->connection->addr_text.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue); ibn->last = now; ibn->total = 1; ibn->insensitive = !ilcf->sensitive; ibn->bad_response = 0; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior new node: %V, total: %i, insens: %i," " percent: %i", &r->connection->addr_text, ibn->total, ibn->insensitive, r->insensitive_percent); return NGX_OK; }
static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_int_t rc; ngx_http_file_cache_node_t *fcn; ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_http_file_cache_lookup(cache, c->key); if (fcn) { ngx_queue_remove(&fcn->queue); if (fcn->error) { if (fcn->valid_sec < ngx_time()) { goto renew; } rc = NGX_OK; goto done; } fcn->uses++; fcn->count++; if (fcn->exists) { c->exists = fcn->exists; c->body_start = fcn->body_start; rc = NGX_OK; goto done; } if (fcn->uses >= c->min_uses) { c->exists = fcn->exists; c->body_start = fcn->body_start; rc = NGX_OK; } else { rc = NGX_AGAIN; } goto done; } fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { ngx_shmtx_unlock(&cache->shpool->mutex); (void) ngx_http_file_cache_forced_expire(cache); ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { rc = NGX_ERROR; goto failed; } } ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); renew: rc = NGX_DECLINED; fcn->uses = 1; fcn->count = 1; fcn->valid_msec = 0; fcn->error = 0; fcn->exists = 0; fcn->valid_sec = 0; fcn->uniq = 0; fcn->body_start = 0; fcn->length = 0; done: fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); c->uniq = fcn->uniq; c->error = fcn->error; c->node = fcn; failed: ngx_shmtx_unlock(&cache->shpool->mutex); return rc; }
static ngx_int_t ngx_http_limit_zone_handler(ngx_http_request_t *r) { size_t len, n; uint32_t hash; ngx_int_t rc; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *node, *sentinel; ngx_pool_cleanup_t *cln; ngx_http_variable_value_t *vv; ngx_http_limit_zone_ctx_t *ctx; ngx_http_limit_zone_node_t *lz; ngx_http_limit_zone_conf_t *lzcf; ngx_http_limit_zone_cleanup_t *lzcln; if (r->main->limit_zone_set) { return NGX_DECLINED; } lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module); if (lzcf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lzcf->shm_zone->data; vv = ngx_http_get_indexed_variable(r, ctx->index); if (vv == NULL || vv->not_found) { return NGX_DECLINED; } len = vv->len; if (len == 0) { return NGX_DECLINED; } if (len > 255) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 255 bytes: \"%v\"", &ctx->var, vv); return NGX_DECLINED; } r->main->limit_zone_set = 1; hash = ngx_crc32_short(vv->data, len); cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t)); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } shpool = (ngx_slab_pool_t *) lzcf->shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); node = ctx->rbtree->root; sentinel = ctx->rbtree->sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ do { lz = (ngx_http_limit_zone_node_t *) &node->color; rc = ngx_memn2cmp(vv->data, lz->data, len, (size_t) lz->len); if (rc == 0) { if ((ngx_uint_t) lz->conn < lzcf->conn) { lz->conn++; goto done; } ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "limiting connections by zone \"%V\"", &lzcf->shm_zone->shm.name); return NGX_HTTP_SERVICE_UNAVAILABLE; } node = (rc < 0) ? node->left : node->right; } while (node != sentinel && hash == node->key); break; } n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_zone_node_t, data) + len; node = ngx_slab_alloc_locked(shpool, n); if (node == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } lz = (ngx_http_limit_zone_node_t *) &node->color; node->key = hash; lz->len = (u_char) len; lz->conn = 1; ngx_memcpy(lz->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit zone: %08XD %d", node->key, lz->conn); ngx_shmtx_unlock(&shpool->mutex); cln->handler = ngx_http_limit_zone_cleanup; lzcln = cln->data; lzcln->shm_zone = lzcf->shm_zone; lzcln->node = node; return NGX_DECLINED; }
static int ngx_tcp_lua_shdict_set_helper(lua_State *L, int flags) { int i, n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_tcp_lua_shdict_ctx_t *ctx; ngx_tcp_lua_shdict_node_t *sd; ngx_str_t value; int value_type; double num; u_char c; lua_Number exptime = 0; u_char *p; ngx_rbtree_node_t *node; ngx_time_t *tp; ngx_shm_zone_t *zone; int forcible = 0; /* indicates whether to foricibly override other * valid entries */ int32_t user_flags = 0; n = lua_gettop(L); if (n != 3 && n != 4 && n != 5) { return luaL_error(L, "expecting 3, 4 or 5 arguments, " "but only seen %d", n); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad \"zone\" argument"); } ctx = zone->data; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); value_type = lua_type(L, 3); switch (value_type) { case LUA_TSTRING: value.data = (u_char *) lua_tolstring(L, 3, &value.len); break; case LUA_TNUMBER: value.len = sizeof(double); num = lua_tonumber(L, 3); value.data = (u_char *) # break; case LUA_TBOOLEAN: value.len = sizeof(u_char); c = lua_toboolean(L, 3) ? 1 : 0; value.data = &c; break; case LUA_TNIL: if (flags & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) { lua_pushnil(L); lua_pushliteral(L, "attempt to add or replace nil values"); return 2; } ngx_str_null(&value); break; default: lua_pushnil(L); lua_pushliteral(L, "bad value type"); return 2; } if (n >= 4) { exptime = luaL_checknumber(L, 4); if (exptime < 0) { exptime = 0; } } if (n == 5) { user_flags = (uint32_t) luaL_checkinteger(L, 5); } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_tcp_lua_shdict_expire(ctx, 1); #endif rc = ngx_tcp_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_TCP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "not found"); lua_pushboolean(L, forcible); return 3; } /* rc == NGX_OK */ goto replace; } if (flags & NGX_TCP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "exists"); lua_pushboolean(L, forcible); return 3; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (value.data && value.len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: found old entry bug value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (value.data == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, 0); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_tcp_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (flags & NGX_TCP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_TCP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%V\"", &key); for (i = 0; i < 30; i++) { if (ngx_tcp_lua_shdict_expire(ctx, 0) == 0) { break; } forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); lua_pushboolean(L, forcible); return 3; } allocated: sd = (ngx_tcp_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; }
static ngx_int_t ngx_http_limit_req2_handler(ngx_http_request_t *r) { size_t n, total_len; uint32_t hash; ngx_int_t rc; ngx_msec_t delay_time; ngx_uint_t excess, delay_excess, delay_postion, nodelay, i; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_limit_req2_t *limit_req2; ngx_http_limit_req2_ctx_t *ctx; ngx_http_limit_req2_node_t *lr; ngx_http_limit_req2_conf_t *lrcf; delay_excess = 0; excess = 0; delay_postion = 0; nodelay = 0; ctx = NULL; rc = NGX_DECLINED; if (r->main->limit_req_set) { return NGX_DECLINED; } lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req2_module); if (lrcf->rules == NULL) { return NGX_DECLINED; } if (!lrcf->enable) { return NGX_DECLINED; } /* filter whitelist */ if (ngx_http_limit_req2_ip_filter(r, lrcf) == NGX_OK) { return NGX_DECLINED; } /* to match limit_req2 rule*/ limit_req2 = lrcf->rules->elts; for (i = 0; i < lrcf->rules->nelts; i++) { ctx = limit_req2[i].shm_zone->data; ngx_crc32_init(hash); total_len = 0; total_len = ngx_http_limit_req2_copy_variables(r, &hash, ctx, NULL); if (total_len == 0) { continue; } ngx_crc32_final(hash); ngx_shmtx_lock(&ctx->shpool->mutex); ngx_http_limit_req2_expire(r, ctx, 1); rc = ngx_http_limit_req2_lookup(r, &limit_req2[i], hash, &excess); ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req2 module: %i %ui.%03ui " "hash is %ui total_len is %i", rc, excess / 1000, excess % 1000, hash, total_len); /* first limit_req2 */ if (rc == NGX_DECLINED) { n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req2_node_t, data) + total_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_http_limit_req2_expire(r, ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } } lr = (ngx_http_limit_req2_node_t *) &node->color; node->key = hash; lr->len = (u_char) total_len; tp = ngx_timeofday(); lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); lr->excess = 0; ngx_http_limit_req2_copy_variables(r, &hash, ctx, lr); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_shmtx_unlock(&ctx->shpool->mutex); continue; } ngx_shmtx_unlock(&ctx->shpool->mutex); if (rc == NGX_BUSY || rc == NGX_ERROR) { break; } /* NGX_AGAIN or NGX_OK */ if (delay_excess < excess) { delay_excess = excess; nodelay = limit_req2[i].nodelay; delay_postion = i; } } r->main->limit_req_set = 1; if (rc == NGX_BUSY || rc == NGX_ERROR) { if (rc == NGX_BUSY) { ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, "limiting requests, excess: %ui.%03ui by zone \"%V\"", excess / 1000, excess % 1000, &limit_req2[i].shm_zone->shm.name); } if (rc == NGX_ERROR || limit_req2[i].forbid_action.len == 0) { return NGX_HTTP_SERVICE_UNAVAILABLE; } else if (limit_req2[i].forbid_action.data[0] == '@') { ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, "limiting requests, forbid_action is %V", &limit_req2[i].forbid_action); (void) ngx_http_named_location(r, &limit_req2[i].forbid_action); } else { ngx_log_error(lrcf->limit_log_level, r->connection->log, 0, "limiting requests, forbid_action is %V", &limit_req2[i].forbid_action); (void) ngx_http_internal_redirect(r, &limit_req2[i].forbid_action, &r->args); } ngx_http_finalize_request(r, NGX_DONE); return NGX_DONE; } /* rc = NGX_AGAIN */ if (delay_excess != 0) { if (nodelay) { return NGX_DECLINED; } delay_time = (ngx_msec_t) delay_excess * 1000 / ctx->rate; ngx_log_error(lrcf->delay_log_level, r->connection->log, 0, "delaying request," "excess: %ui.%03ui, by zone \"%V\", delay \"%ui\" s", delay_excess / 1000, delay_excess % 1000, &limit_req2[delay_postion].shm_zone->shm.name, delay_time); if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->read_event_handler = ngx_http_test_reading; r->write_event_handler = ngx_http_limit_req2_delay; ngx_add_timer(r->connection->write, delay_time); return NGX_AGAIN; } /* rc == NGX_OK or rc == NGX_DECLINED */ return NGX_DECLINED; }