diff --git a/auto/sources b/auto/sources index d56abaed5..074f96478 100644 --- a/auto/sources +++ b/auto/sources @@ -32,6 +32,7 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_connection.h \ src/core/ngx_cycle.h \ src/core/ngx_conf_file.h \ + src/core/ngx_resolver.h \ src/core/ngx_open_file_cache.h \ src/core/ngx_garbage_collector.h" @@ -59,6 +60,7 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_spinlock.c \ src/core/ngx_cpuinfo.c \ src/core/ngx_conf_file.c \ + src/core/ngx_resolver.c \ src/core/ngx_open_file_cache.c \ src/core/ngx_garbage_collector.c" diff --git a/misc/GNUmakefile b/misc/GNUmakefile index 1d10c0e87..066be771a 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -50,8 +50,6 @@ snapshot: rm $(TEMP)/$(NGINX)/src/event/modules/ngx_iocp_module.* rm -r $(TEMP)/$(NGINX)/src/os/win32 - rm $(TEMP)/$(NGINX)/src/core/ngx_resolver.* - rm -r $(TEMP)/$(NGINX)/src/mysql rm $(TEMP)/$(NGINX)/src/http/modules/ngx_http_status_module.c diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index cf47cd8f5..1914aa728 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -9,175 +9,2002 @@ #include +#define NGX_RESOLVER_UDP_SIZE 4096 + + typedef struct { - ngx_connection_t *connection; + u_char ident_hi; + u_char ident_lo; + u_char flags_hi; + u_char flags_lo; + u_char nqs_hi; + u_char nqs_lo; + u_char nan_hi; + u_char nan_lo; + u_char nns_hi; + u_char nns_lo; + u_char nar_hi; + u_char nar_lo; +} ngx_resolver_query_t; - struct sockaddr *sockaddr; - socklen_t socklen; - ngx_str_r server; - ngx_str_r name; +typedef struct { + u_char type_hi; + u_char type_lo; + u_char class_hi; + u_char class_lo; +} ngx_resolver_qs_t; - ngx_event_handler_pt handler; - ngx_log_t *pool; - ngx_log_t *log; -} ngx_resolver_t; +typedef struct { + u_char type_hi; + u_char type_lo; + u_char class_hi; + u_char class_lo; + u_char ttl[4]; + u_char len_hi; + u_char len_lo; +} ngx_resolver_an_t; + + +ngx_int_t ngx_udp_connect(ngx_udp_connection_t *uc); + + +static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, + ngx_resolver_ctx_t *ctx); +static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn); +static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn, + ngx_resolver_ctx_t *ctx); +static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, + ngx_resolver_ctx_t *ctx); +static void ngx_resolver_resend_handler(ngx_event_t *ev); +static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +static void ngx_resolver_read_response(ngx_event_t *rev); +static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, + size_t n); +static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t i); +static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code); +static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r, + ngx_str_t *name, uint32_t hash); +static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r, + in_addr_t addr); +static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, + u_char *buf, u_char *src, u_char *last); +static void ngx_resolver_timeout_handler(ngx_event_t *ev); +static void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn); +static void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size); +static void ngx_resolver_free(ngx_resolver_t *r, void *p); +static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p); +static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size); + + +/* STUB: ngx_peer_addr_t * */ + +ngx_resolver_t * +ngx_resolver_create(ngx_peer_addr_t *addr, ngx_log_t *log) +{ + ngx_resolver_t *r; + ngx_udp_connection_t *uc; + + r = ngx_calloc(sizeof(ngx_resolver_t), log); + if (r == NULL) { + return NULL; + } + + uc = ngx_calloc(sizeof(ngx_udp_connection_t), log); + if (uc == NULL) { + return NULL; + } + + r->event = ngx_calloc(sizeof(ngx_event_t), log); + if (r->event == NULL) { + return NULL; + } + + ngx_rbtree_sentinel_init(&r->name_sentinel); + + r->name_rbtree.root = &r->name_sentinel; + r->name_rbtree.sentinel = &r->name_sentinel; + r->name_rbtree.insert = ngx_resolver_rbtree_insert_value; + + ngx_rbtree_sentinel_init(&r->addr_sentinel); + + r->addr_rbtree.root = &r->addr_sentinel; + r->addr_rbtree.sentinel = &r->addr_sentinel; + r->addr_rbtree.insert = ngx_rbtree_insert_value; + + r->name_resend_queue.prev = &r->name_resend_queue; + r->name_resend_queue.next = &r->name_resend_queue; + + r->addr_resend_queue.prev = &r->addr_resend_queue; + r->addr_resend_queue.next = &r->addr_resend_queue; + + r->name_expire_queue.prev = &r->name_expire_queue; + r->name_expire_queue.next = &r->name_expire_queue; + + r->addr_expire_queue.prev = &r->addr_expire_queue; + r->addr_expire_queue.next = &r->addr_expire_queue; + + r->event->handler = ngx_resolver_resend_handler; + r->event->data = r; + r->event->log = log; + r->ident = -1; + + r->udp_connection = uc; + + r->resend_timeout = 5; + r->expire = 30; + r->valid = 300; + + r->log = log; + r->log_level = NGX_LOG_ALERT; + + uc->sockaddr = addr->sockaddr; + uc->socklen = addr->socklen; + uc->server = addr->name; + uc->log = log; + + return r; +} + + +ngx_resolver_ctx_t * +ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp) +{ + in_addr_t addr; + ngx_resolver_ctx_t *ctx; + + if (temp) { + addr = ngx_inet_addr(temp->name.data, temp->name.len); + + if (addr != INADDR_NONE) { + temp->resolver = r; + temp->state = NGX_OK; + temp->naddrs = 1; + temp->addrs = &temp->addr; + temp->addr = addr; + temp->quick = 1; + + return temp; + } + } + + ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t)); + + if (ctx) { + ctx->resolver = r; + } + + return ctx; +} ngx_int_t -ngx_gethostbyname(ngx_resolver_t *r) +ngx_resolve_name(ngx_resolver_ctx_t *ctx) { - ngx_socket_t s; + ngx_int_t rc; + ngx_resolver_t *r; - if (r->connection) { + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\"", &ctx->name); + + if (ctx->quick) { + ctx->handler(ctx); return NGX_OK; } + /* lock name mutex */ + + rc = ngx_resolve_name_locked(r, ctx); + + if (rc == NGX_OK) { + return NGX_OK; + } + + /* unlock name mutex */ + + if (rc == NGX_AGAIN) { + return NGX_OK; + } + + /* lock alloc mutex */ + + if (ctx->event) { + ngx_resolver_free_locked(r, ctx->event); + } + + ngx_resolver_free_locked(r, ctx); + + /* unlock alloc mutex */ + + return NGX_ERROR; +} + + +void +ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) +{ + uint32_t hash; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve name done: %i", ctx->state); + + if (ctx->quick) { + return; + } + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + /* lock name mutex */ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + hash = ngx_crc32_short(ctx->name.data, ctx->name.len); + + rn = ngx_resolver_lookup_name(r, &ctx->name, hash); + + if (rn) { + p = &rn->waiting; + w = rn->waiting; + + while (w) { + if (w == ctx) { + *p = w->next; + + goto done; + } + + p = &w->next; + w = w->next; + } + } + + ngx_log_error(NGX_LOG_ALERT, r->log, 0, + "could not cancel %V resolving", &ctx->name); + } + +done: + + ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue); + + /* unlock name mutex */ + + ngx_resolver_free(r, ctx); +} + + +/* NGX_RESOLVE_A only */ + +static ngx_int_t +ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) +{ + uint32_t hash; + in_addr_t addr, *addrs; + ngx_uint_t naddrs; + ngx_resolver_ctx_t *next; + ngx_resolver_node_t *rn; + + hash = ngx_crc32_short(ctx->name.data, ctx->name.len); + + rn = ngx_resolver_lookup_name(r, &ctx->name, hash); + + if (rn) { + + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); + + ngx_queue_remove(&rn->queue); + + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + naddrs = rn->naddrs; + + if (naddrs) { + + /* NGX_RESOLVE_A answer */ + + if (naddrs != 1) { + addr = 0; + addrs = ngx_resolver_dup(r, rn->u.addrs, + naddrs * sizeof(in_addr_t)); + if (addrs == NULL) { + return NGX_ERROR; + } + + } else { + addr = rn->u.addr; + addrs = NULL; + } + + ctx->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + do { + ctx->state = NGX_OK; + ctx->naddrs = naddrs; + ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; + ctx->addr = addr; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + if (addrs) { + ngx_resolver_free(r, addrs); + } + + return NGX_OK; + } + + /* NGX_RESOLVE_CNAME */ + + ctx->name.len = rn->cnlen; + ctx->name.data = rn->u.cname; + + return ngx_resolve_name_locked(r, ctx); + } + + if (rn->waiting) { + + ctx->next = rn->waiting; + rn->waiting = ctx; + + return NGX_AGAIN; + } + + ngx_queue_remove(&rn->queue); + + /* lock alloc mutex */ + + ngx_resolver_free_locked(r, rn->query); + + if (rn->cnlen) { + ngx_resolver_free_locked(r, rn->u.cname); + } + + if (rn->naddrs > 1) { + ngx_resolver_free_locked(r, rn->u.addrs); + } + + /* unlock alloc mutex */ + + } else { + + rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); + if (rn == NULL) { + return NGX_ERROR; + } + + rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); + if (rn->name == NULL) { + ngx_resolver_free(r, rn); + return NGX_ERROR; + } + + rn->node.key = hash; + rn->nlen = (u_short) ctx->name.len; + + ngx_rbtree_insert(&r->name_rbtree, &rn->node); + } + + if (ngx_resolver_create_name_query(rn, ctx) != NGX_OK) { + goto failed; + } + + if (ngx_resolver_send_query(r, rn) != NGX_OK) { + goto failed; + } + + if (ctx->event == NULL) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + goto failed; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + + if (ngx_queue_empty(&r->name_resend_queue)) { + ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(&r->name_resend_queue, &rn->queue); + + rn->cnlen = 0; + rn->naddrs = 0; + rn->valid = 0; + rn->waiting = ctx; + + ctx->state = NGX_AGAIN; + + return NGX_AGAIN; + +failed: + + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + + ngx_resolver_free(r, rn->name); + + ngx_resolver_free(r, rn); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_resolve_addr(ngx_resolver_ctx_t *ctx) +{ + ngx_resolver_t *r; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ctx->addr = ntohl(ctx->addr); + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr(r, ctx->addr); + + if (rn) { + + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); + + ngx_queue_remove(&rn->queue); + + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); + + ctx->name.len = rn->nlen; + ctx->name.data = ngx_resolver_dup(r, rn->name, rn->nlen); + if (ctx->name.data == NULL) { + goto failed; + } + + /* unlock addr mutex */ + + ctx->state = NGX_OK; + + ctx->handler(ctx); + + ngx_resolver_free(r, ctx->name.data); + + return NGX_OK; + } + + if (rn->waiting) { + + ctx->next = rn->waiting; + rn->waiting = ctx; + + return NGX_AGAIN; + } + + ngx_queue_remove(&rn->queue); + + ngx_resolver_free(r, rn->query); + + } else { + rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); + if (rn == NULL) { + goto failed; + } + + rn->node.key = ctx->addr; + + ngx_rbtree_insert(&r->addr_rbtree, &rn->node); + } + + if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { + goto failed; + } + + if (ngx_resolver_send_query(r, rn) != NGX_OK) { + goto failed; + } + + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + goto failed; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + + if (ngx_queue_empty(&r->addr_resend_queue)) { + ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(&r->addr_resend_queue, &rn->queue); + + rn->cnlen = 0; + rn->naddrs = 0; + rn->name = NULL; + rn->nlen = 0; + rn->valid = 0; + rn->waiting = ctx; + + /* unlock addr mutex */ + + ctx->state = NGX_AGAIN; + + return NGX_OK; + +failed: + + if (rn) { + ngx_rbtree_delete(&r->addr_rbtree, &rn->node); + + ngx_resolver_free(r, rn); + } + + /* unlock addr mutex */ + + /* lock alloc mutex */ + + if (ctx->event) { + ngx_resolver_free_locked(r, ctx->event); + } + + ngx_resolver_free_locked(r, ctx); + + /* unlock alloc mutex */ + + return NGX_ERROR; +} + + +void +ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) +{ + in_addr_t addr; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve addr done: %i", ctx->state); + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + /* lock addr mutex */ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + rn = ngx_resolver_lookup_addr(r, ctx->addr); + + if (rn) { + p = &rn->waiting; + w = rn->waiting; + + while (w) { + if (w == ctx) { + *p = w->next; + + goto done; + } + + p = &w->next; + w = w->next; + } + } + + addr = ntohl(ctx->addr); + + ngx_log_error(NGX_LOG_ALERT, r->log, 0, + "could not cancel %ud.%ud.%ud.%ud resolving", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + } + +done: + + ngx_resolver_expire(r, &r->addr_rbtree, &r->addr_expire_queue); + + /* unlock addr mutex */ + + ngx_resolver_free(r, ctx); +} + + +static void +ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue) +{ + time_t now; + ngx_uint_t i; + ngx_queue_t *q; + ngx_resolver_node_t *rn; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver expire"); + + now = ngx_time(); + + for (i = 0; i < 2; i++) { + if (ngx_queue_empty(queue)) { + return; + } + + q = ngx_queue_last(queue); + + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + + if (now <= rn->expire) { + return; + } + +#if (NGX_DEBUG) + { + ngx_str_t s; + + s.len = rn->nlen; + s.data = rn->name; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver expire \"%V\"", &s); + } +#endif + + ngx_queue_remove(q); + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +static ngx_int_t +ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn) +{ + ssize_t n; + ngx_udp_connection_t *uc; + + uc = r->udp_connection; + + if (uc->connection == NULL) { + if (ngx_udp_connect(uc) != NGX_OK) { + return NGX_ERROR; + } + + uc->connection->data = r; + uc->connection->read->handler = ngx_resolver_read_response; + } + + n = ngx_send(uc->connection, rn->query, rn->qlen); + + if (n == -1) { + return NGX_ERROR; + } + + if ((size_t) n != (size_t) rn->qlen) { + ngx_log_error(NGX_LOG_CRIT, uc->log, 0, "send() incomplete"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_resolver_resend_handler(ngx_event_t *ev) +{ + time_t timer, atimer, ntimer; + ngx_resolver_t *r; + + r = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver resend handler"); + + /* lock name mutex */ + + ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue); + + /* unlock name mutex */ + + /* lock addr mutex */ + + atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue); + + /* unlock addr mutex */ + + if (ntimer == 0) { + timer = atimer; + + } else if (atimer == 0) { + timer = ntimer; + + } else { + timer = (atimer < ntimer) ? atimer : ntimer; + } + + if (timer) { + ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000)); + } +} + + +static time_t +ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue) +{ + time_t now; + ngx_queue_t *q; + ngx_resolver_node_t *rn; + + now = ngx_time(); + + for ( ;; ) { + if (ngx_queue_empty(queue)) { + return 0; + } + + q = ngx_queue_last(queue); + + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + + if (now < rn->expire) { + return rn->expire - now; + } + +#if (NGX_DEBUG) + { + ngx_str_t s; + + s.len = rn->nlen; + s.data = rn->name; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver resend \"%V\" %p", &s, rn->waiting); + } +#endif + + ngx_queue_remove(q); + + if (rn->waiting) { + + if (ngx_resolver_send_query(r, rn) == NGX_OK) { + + rn->expire = now + r->resend_timeout; + + ngx_queue_insert_head(queue, &rn->queue); + + continue; + } + } + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +static void +ngx_resolver_read_response(ngx_event_t *rev) +{ + ssize_t n; + ngx_connection_t *c; + u_char buf[NGX_RESOLVER_UDP_SIZE]; + + c = rev->data; + + do { + n = ngx_recv(c, buf, NGX_RESOLVER_UDP_SIZE); + + if (n == -1) { + return; + } + + ngx_resolver_process_response(c->data, buf, n); + + } while (rev->ready); +} + + +static void +ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n) +{ + char *err; + size_t len; + ngx_uint_t i, ident, flags, code, nqs, nan, qtype, qclass; + ngx_resolver_qs_t *qs; + ngx_resolver_query_t *query; + + if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) { + goto short_response; + } + + query = (ngx_resolver_query_t *) buf; + + ident = (query->ident_hi << 8) + query->ident_lo; + flags = (query->flags_hi << 8) + query->flags_lo; + nqs = (query->nqs_hi << 8) + query->nqs_lo; + nan = (query->nan_hi << 8) + query->nan_lo; + + ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver DNS response %d fl:%04Xud %d/%d/%d/%d", + ident, flags, nqs, nan, + (query->nns_hi << 8) + query->nns_lo, + (query->nar_hi << 8) + query->nar_lo); + + if (!(flags & 0x8000)) { + ngx_log_error(r->log_level, r->log, 0, + "invalid DNS response %d fl:%04Xud", ident, flags); + return; + } + + code = flags & 0x7f; + + if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) { + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%d: %s), query id:%d", + code, ngx_resolver_strerror(code), ident); + return; + } + + if (nqs != 1) { + err = "invalid number of questions in DNS response"; + goto done; + } + + if (code == 0 && nan == 0) { + err = "no answers in DNS response"; + goto done; + } + + i = sizeof(ngx_resolver_query_t); + + while (i < (ngx_uint_t) n) { + if (buf[i] == '\0') { + goto found; + } + + len = buf[i]; + i += 1 + len; + } + + goto short_response; + +found: + + if (i++ == 0) { + err = "zero-length domain name in DNS response"; + goto done; + } + + if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t)) + > (ngx_uint_t) n) + { + goto short_response; + } + + qs = (ngx_resolver_qs_t *) &buf[i]; + + qtype = (qs->type_hi << 8) + qs->type_lo; + qclass = (qs->class_hi << 8) + qs->class_lo; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver DNS response qt:%d cl:%d", qtype, qclass); + + if (qclass != 1) { + ngx_log_error(r->log_level, r->log, 0, + "unknown query class %d in DNS response", qclass); + return; + } + + switch (qtype) { + + case NGX_RESOLVE_A: + + ngx_resolver_process_a(r, buf, n, ident, code, nan, + i + sizeof(ngx_resolver_qs_t)); + + break; + + case NGX_RESOLVE_PTR: + + ngx_resolver_process_ptr(r, buf, n, ident, code); + + break; + + default: + ngx_log_error(r->log_level, r->log, 0, + "unknown query type %d in DNS response", qtype); + return; + } + + return; + +short_response: + + err = "short dns response"; + +done: + + ngx_log_error(r->log_level, r->log, 0, err); + + return; +} + + +static void +ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans) +{ + char *err; + u_char *cname; + size_t len; + uint32_t hash; + in_addr_t addr, *addrs; + ngx_str_t name; + ngx_uint_t qtype, qident, naddrs, a, i, n, start; + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + + if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); + + hash = ngx_crc32_short(name.data, name.len); + + /* lock name mutex */ + + rn = ngx_resolver_lookup_name(r, &name, hash); + + if (rn == NULL || rn->query == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %d response for %V, expect %d", + ident, &name, qident); + goto failed; + } + + if (code) { + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + + ngx_resolver_free_node(r, rn); + + /* unlock name mutex */ + + while (next) { + ctx = next; + ctx->state = code; + next = ctx->next; + + ctx->handler(ctx); + } + + return; + } + + i = ans; + naddrs = 0; + addr = 0; + addrs = NULL; + cname = NULL; + qtype = 0; + + for (a = 0; a < nan; a++) { + + start = i; + + while (i < last) { + + if (buf[i] & 0xc0) { + i += 2; + goto found; + } + + if (buf[i] == 0) { + i++; + goto test_length; + } + + i += 1 + buf[i]; + } + + goto short_response; + + test_length: + + if (i - start < 2) { + err = "invalid name in dns response"; + goto invalid; + } + + found: + + if (i + sizeof(ngx_resolver_an_t) >= last) { + goto short_response; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + qtype = (an->type_hi << 8) + an->type_lo; + len = (an->len_hi << 8) + an->len_lo; + + if (qtype == NGX_RESOLVE_A) { + + i += sizeof(ngx_resolver_an_t); + + if (i + len > last) { + goto short_response; + } + + addr = htonl((buf[i] << 24) + (buf[i + 1] << 16) + + (buf[i + 2] << 8) + (buf[i + 3])); + + naddrs++; + + i += len; + + } else if (qtype == NGX_RESOLVE_CNAME) { + cname = &buf[i] + sizeof(ngx_resolver_an_t); + i += sizeof(ngx_resolver_an_t) + len; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver naddrs:%ui cname:%p", naddrs, cname); + + if (naddrs) { + + if (naddrs == 1) { + rn->u.addr = addr; + + } else { + + addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t)); + if (addrs == NULL) { + return; + } + + n = 0; + i = ans; + + for (a = 0; a < nan; a++) { + + for ( ;; ) { + + if (buf[i] & 0xc0) { + i += 2; + goto ok; + } + + if (buf[i] == 0) { + i++; + goto ok; + } + + i += 1 + buf[i]; + } + + ok: + + an = (ngx_resolver_an_t *) &buf[i]; + + qtype = (an->type_hi << 8) + an->type_lo; + len = (an->len_hi << 8) + an->len_lo; + + i += sizeof(ngx_resolver_an_t); + + if (qtype == NGX_RESOLVE_A) { + + addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16) + + (buf[i + 2] << 8) + (buf[i + 3])); + + if (n == naddrs) { + break; + } + } + + i += len; + } + + rn->u.addrs = addrs; + + addrs = ngx_resolver_dup(r, rn->u.addrs, + naddrs * sizeof(in_addr_t)); + if (addrs == NULL) { + return; + } + } + + rn->naddrs = (u_short) naddrs; + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + r->valid; + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + while (next) { + ctx = next; + ctx->state = NGX_OK; + ctx->naddrs = naddrs; + ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; + ctx->addr = addr; + next = ctx->next; + + ctx->handler(ctx); + } + + if (naddrs) { + ngx_resolver_free(r, addrs); + } + + return; + + } else if (cname) { + + /* CNAME only */ + + if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver cname:\"%V\"", &name); + + rn->cnlen = (u_short) name.len; + rn->u.cname = name.data; + rn->valid = ngx_time() + r->valid; + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + ctx = rn->waiting; + rn->waiting = NULL; + + if (ctx) { + ctx->name = name; + + (void) ngx_resolve_name_locked(r, ctx); + } + + return; + } + + ngx_log_error(r->log_level, r->log, 0, + "no A or CNAME types in DNS responses, unknown query type: %d", + qtype); + return; + +short_response: + + err = "short dns response"; + +invalid: + + /* unlock name mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + + /* unlock name mutex */ + + return; +} + + +static void +ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code) +{ + char *err; + size_t len; + in_addr_t addr; + ngx_int_t digit; + ngx_str_t name; + ngx_uint_t i, mask, qtype, qclass, qident; + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + + if (ngx_resolver_copy(r, NULL, buf, &buf[12], &buf[n]) != NGX_OK) { + goto invalid_in_addr_arpa; + } + + addr = 0; + i = 12; + + for (mask = 0; mask < 32; mask += 8) { + len = buf[i++]; + + digit = ngx_atoi(&buf[i], len); + if (digit == NGX_ERROR || digit > 255) { + goto invalid_in_addr_arpa; + } + + addr += digit << mask; + i += len; + } + + if (ngx_strcmp(&buf[i], "\7in-addr\4arpa") != 0) { + goto invalid_in_addr_arpa; + } + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr(r, addr); + + if (rn == NULL || rn->query == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %ud.%ud.%ud.%ud", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %d response for %ud.%ud.%ud.%ud, expect %d", + ident, (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff, qident); + goto failed; + } + + if (code) { + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(&r->addr_rbtree, &rn->node); + + ngx_resolver_free_node(r, rn); + + /* unlock addr mutex */ + + while (next) { + ctx = next; + ctx->state = code; + next = ctx->next; + + ctx->handler(ctx); + } + + return; + } + + i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t); + + if (i + 2 + sizeof(ngx_resolver_an_t) > (ngx_uint_t) n) { + goto short_response; + } + + /* compression pointer to "XX.XX.XX.XX.in-addr.arpa */ + + if (buf[i] != 0xc0 || buf[i + 1] != 0x0c) { + err = "invalid in-addr.arpa name in DNS response"; + goto invalid; + } + + an = (ngx_resolver_an_t *) &buf[i + 2]; + + qtype = (an->type_hi << 8) + an->type_lo; + qclass = (an->class_hi << 8) + an->class_lo; + len = (an->len_hi << 8) + an->len_lo; + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver qt:%d cl:%d len:%uz", qtype, qclass, len); + + i += 2 + sizeof(ngx_resolver_an_t); + + if (i + len > (ngx_uint_t) n) { + goto short_response; + } + + len -= 2; + + if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name); + + if (len != (size_t) rn->nlen || ngx_strncmp(name.data, rn->name, len) != 0) + { + ngx_resolver_free(r, rn->name); + rn->name = name.data; + + name.data = ngx_resolver_dup(r, rn->name, len); + if (name.data == NULL) { + goto failed; + } + } + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + r->valid; + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + /* unlock addr mutex */ + + while (next) { + ctx = next; + ctx->state = NGX_OK; + ctx->name = name; + next = ctx->next; + + ctx->handler(ctx); + } + + ngx_resolver_free(r, name.data); + + return; + +invalid_in_addr_arpa: + + ngx_log_error(r->log_level, r->log, 0, + "invalid in-addr.arpa name in DNS response"); + return; + +short_response: + + err = "short DNS response"; + +invalid: + + /* unlock addr mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + + /* unlock addr mutex */ + + return; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash) +{ + ngx_int_t rc; + size_t len; + ngx_rbtree_node_t *node, *sentinel; + ngx_resolver_node_t *rn; + + node = r->name_rbtree.root; + sentinel = r->name_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 { + rn = (ngx_resolver_node_t *) node; + + len = (name->len > (size_t) rn->nlen) ? rn->nlen : name->len; + + rc = ngx_strncmp(name->data, rn->name, len); + + if (rc == 0) { + return rn; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + /* not found */ + + return NULL; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr) +{ + ngx_rbtree_node_t *node, *sentinel; + + node = r->addr_rbtree.root; + sentinel = r->addr_rbtree.sentinel; + + while (node != sentinel) { + + if (addr < node->key) { + node = node->left; + continue; + } + + if (addr > node->key) { + node = node->right; + continue; + } + + /* addr == node->key */ + + return (ngx_resolver_node_t *) node; + } + + /* not found */ + + return NULL; +} + + +static void +ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + size_t len; + ngx_rbtree_node_t **p; + ngx_resolver_node_t *rn, *rn_temp; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + rn = (ngx_resolver_node_t *) node; + rn_temp = (ngx_resolver_node_t *) temp; + + len = (rn->nlen > rn_temp->nlen) ? rn_temp->nlen : rn->nlen; + + p = (ngx_strncmp(rn->name, rn_temp->name, len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_int_t +ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) +{ + u_char *p, *s; + size_t len; + ngx_uint_t ident; + ngx_resolver_qs_t *qs; + ngx_resolver_query_t *query; + + len = sizeof(ngx_resolver_query_t) + + 1 + ctx->name.len + 1 + sizeof(ngx_resolver_qs_t); + + p = ngx_resolver_calloc(ctx->resolver, len); + if (p == NULL) { + return NGX_ERROR; + } + + rn->qlen = (u_short) len; + rn->query = p; + + query = (ngx_resolver_query_t *) p; + + ident = ngx_random(); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, + "resolve: \"%V\" %i", &ctx->name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_query_t) + 1 + ctx->name.len + 1; + + qs = (ngx_resolver_qs_t *) p; + + /* query type */ + qs->type_hi = 0; qs->type_lo = (u_char) ctx->type; + + /* IP query class */ + qs->class_hi = 0; qs->class_lo = 1; + + /* convert "www.example.com" to "\3www\7example\3com\0" */ + + len = 0; + p--; + *p-- = '\0'; + + for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) { + if (*s != '.') { + *p = *s; + len++; + + } else { + *p = (u_char) len; + len = 0; + } + + p--; + } + + *p = (u_char) len; + + return NGX_OK; +} + + +/* AF_INET only */ + +static ngx_int_t +ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) +{ + u_char *p, *d; + size_t len; + ngx_int_t n; + ngx_uint_t ident; + ngx_resolver_query_t *query; + + len = sizeof(ngx_resolver_query_t) + + sizeof(".255.255.255.255.in-addr.arpa.") - 1 + + sizeof(ngx_resolver_qs_t); + + p = ngx_resolver_calloc(ctx->resolver, len); + if (p == NULL) { + return NGX_ERROR; + } + + rn->query = p; + query = (ngx_resolver_query_t *) p; + + ident = ngx_random(); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_query_t); + + for (n = 0; n < 32; n += 8){ + d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff); + *p = (u_char) (d - &p[1]); + p = d; + } + + /* query type "PTR", IP query class */ + ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18); + + rn->qlen = (u_short) + (p + sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t) + - rn->query); + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src, + u_char *last) +{ + char *err; + u_char *p, *dst; + ssize_t len; + ngx_uint_t i, n; + + p = src; + len = -1; + + /* + * compression pointers allow to create endless loop, so we set limit; + * 128 pointers should be enough to store 255-byte name + */ + + for (i = 0; i < 128; i++) { + n = *p++; + + if (n == 0) { + goto done; + } + + if (n & 0xc0) { + n = (n & 0x3f << 8) + *p; + p = &buf[n]; + + } else { + len += 1 + n; + p = &p[n]; + } + + if (p >= last) { + err = "name is out of response"; + goto invalid; + } + } + + err = "compression pointers loop"; + +invalid: + + ngx_log_error(r->log_level, r->log, 0, err); + + return NGX_ERROR; + +done: + + if (name == NULL) { + return NGX_OK; + } + + dst = ngx_resolver_alloc(r, len); + if (dst == NULL) { + return NGX_ERROR; + } + + name->data = dst; + + n = *src++; + + for ( ;; ) { + if (n != 0xc0) { + ngx_memcpy(dst, src, n); + dst += n; + src += n; + + n = *src++; + + if (n != 0) { + *dst++ = '.'; + } + + } else { + n = (n & 0x3f << 8) + *src; + src = &buf[n]; + + n = *src++; + } + + if (n == 0) { + name->len = dst - name->data; + return NGX_OK; + } + } +} + + +static void +ngx_resolver_timeout_handler(ngx_event_t *ev) +{ + ngx_resolver_ctx_t *ctx; + + ctx = ev->data; + + ctx->state = NGX_RESOLVE_TIMEDOUT; + + ctx->handler(ctx); +} + + +static void +ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn) +{ + /* lock alloc mutex */ + + if (rn->query) { + ngx_resolver_free_locked(r, rn->query); + } + + if (rn->name) { + ngx_resolver_free_locked(r, rn->name); + } + + if (rn->cnlen) { + ngx_resolver_free_locked(r, rn->u.cname); + } + + if (rn->naddrs > 1) { + ngx_resolver_free_locked(r, rn->u.addrs); + } + + ngx_resolver_free_locked(r, rn); + + /* unlock alloc mutex */ +} + + +static void * +ngx_resolver_alloc(ngx_resolver_t *r, size_t size) +{ + u_char *p; + + /* lock alloc mutex */ + + p = ngx_alloc(size, r->log); + + /* unlock alloc mutex */ + + return p; +} + + +void * +ngx_resolver_calloc(ngx_resolver_t *r, size_t size) +{ + u_char *p; + + p = ngx_resolver_alloc(r, size); + + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +static void +ngx_resolver_free(ngx_resolver_t *r, void *p) +{ + /* lock alloc mutex */ + + ngx_free(p); + + /* unlock alloc mutex */ +} + + +static void +ngx_resolver_free_locked(ngx_resolver_t *r, void *p) +{ + ngx_free(p); +} + + +static void * +ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size) +{ + void *dst; + + dst = ngx_resolver_alloc(r, size); + + if (dst == NULL) { + return dst; + } + + ngx_memcpy(dst, src, size); + + return dst; +} + + +char * +ngx_resolver_strerror(ngx_int_t err) +{ + static char *errors[] = { + "Format error", /* FORMERR */ + "Server failure", /* SERVFAIL */ + "Host not found", /* NXDOMAIN */ + "Unimplemented", /* NOTIMP */ + "Operation refused" /* REFUSED */ + }; + + if (err > 0 && err < 6) { + return errors[err - 1]; + } + + if (err == NGX_RESOLVE_TIMEDOUT) { + return "Operation timed out"; + } + + return "Unknown error"; +} + + +ngx_int_t +ngx_udp_connect(ngx_udp_connection_t *uc) +{ + int rc; + ngx_int_t event; + ngx_event_t *rev, *wev; + ngx_socket_t s; + ngx_connection_t *c; + s = ngx_socket(AF_INET, SOCK_DGRAM, 0); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, r->log, 0, "socket %d", s); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, uc->log, 0, "UDP socket %d", s); if (s == -1) { - ngx_log_error(NGX_LOG_ALERT, r->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } - c = ngx_get_connection(s, r->log); + c = ngx_get_connection(s, uc->log); if (c == NULL) { if (ngx_close_socket(s) == -1) { - ngx_log_error(NGX_LOG_ALERT, r->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno, ngx_close_socket_n "failed"); } return NGX_ERROR; } + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + ngx_free_connection(c); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, uc->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + return NGX_ERROR; + } + rev = c->read; wev = c->write; - rev->log = pc->log; - wev->log = pc->log; + rev->log = uc->log; + wev->log = uc->log; - r->connection = c; - - /* - * TODO: MT: - ngx_atomic_fetch_add() - * or protection by critical section or mutex - * - * TODO: MP: - allocated in a shared memory - * - ngx_atomic_fetch_add() - * or protection by critical section or mutex - */ + uc->connection = c; c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); #if (NGX_THREADS) - rev->lock = pc->lock; - wev->lock = pc->lock; + + /* TODO: lock event when call completion handler */ + + rev->lock = &c->lock; + wev->lock = &c->lock; rev->own_lock = &c->lock; wev->own_lock = &c->lock; + #endif - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0, - "connect to %V, fd:%d #%d", &r->server, s, c->number); + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, uc->log, 0, + "connect to %V, fd:%d #%d", &uc->server, s, c->number); - rc = connect(s, r->sockaddr, r->socklen); + rc = connect(s, uc->sockaddr, uc->socklen); + + /* TODO: aio, iocp */ if (rc == -1) { - ngx_log_error(level, r->log, ngx_socket_errno, - "connect() to %V failed", &r->server); + ngx_log_error(NGX_LOG_CRIT, uc->log, ngx_socket_errno, + "connect() to %V failed", &uc->server); return NGX_ERROR; } + /* UDP sockets are always ready to write */ + wev->ready = 1; + if (ngx_add_event) { + event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? + /* kqueue, epoll */ NGX_CLEAR_EVENT: + /* select, poll, /dev/poll */ NGX_LEVEL_EVENT; + /* eventport event type has no meaning: oneshot only */ + if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { + return NGX_ERROR; + } + } else { + /* rtsig */ - - if (ngx_add_conn) { if (ngx_add_conn(c) == NGX_ERROR) { return NGX_ERROR; } } - - if (ngx_add_conn) { - if (rc == -1) { - - /* NGX_EINPROGRESS */ - - return NGX_AGAIN; - } - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected"); - - wev->ready = 1; - - return NGX_OK; - } - - if (ngx_event_flags & NGX_USE_AIO_EVENT) { - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno, - "connect(): %d", rc); - - /* aio, iocp */ - - if (ngx_blocking(s) == -1) { - ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, - ngx_blocking_n " failed"); - return NGX_ERROR; - } - - /* - * FreeBSD's aio allows to post an operation on non-connected socket. - * NT does not support it. - * - * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT - */ - - rev->ready = 1; - wev->ready = 1; - - return NGX_OK; - } - - if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { - - /* kqueue */ - - event = NGX_CLEAR_EVENT; - - } else { - - /* select, poll, /dev/poll */ - - event = NGX_LEVEL_EVENT; - } - - if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { - return NGX_ERROR; - } - - if (rc == -1) { - - /* NGX_EINPROGRESS */ - - if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) { - return NGX_ERROR; - } - - return NGX_AGAIN; - } - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected"); - - wev->ready = 1; - return NGX_OK; } diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h new file mode 100644 index 000000000..e69f42087 --- /dev/null +++ b/src/core/ngx_resolver.h @@ -0,0 +1,142 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +#ifndef _NGX_RESOLVER_H_INCLUDED_ +#define _NGX_RESOLVER_H_INCLUDED_ + + +#define NGX_RESOLVE_A 1 +#define NGX_RESOLVE_CNAME 5 +#define NGX_RESOLVE_PTR 12 +#define NGX_RESOLVE_MX 15 +#define NGX_RESOLVE_TXT 16 + +#define NGX_RESOLVE_FORMERR 1 +#define NGX_RESOLVE_SERVFAIL 2 +#define NGX_RESOLVE_NXDOMAIN 3 +#define NGX_RESOLVE_NOTIMP 4 +#define NGX_RESOLVE_REFUSED 5 +#define NGX_RESOLVE_TIMEDOUT NGX_ETIMEDOUT + + +typedef struct { + ngx_connection_t *connection; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t server; + ngx_log_t *log; +} ngx_udp_connection_t; + + +typedef struct ngx_resolver_ctx_s ngx_resolver_ctx_t; + +typedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx); + + +typedef struct { + ngx_rbtree_node_t node; + ngx_queue_t queue; + + /* PTR: resolved name, A: name to resolve */ + u_char *name; + + u_short nlen; + u_short qlen; + + u_char *query; + + union { + in_addr_t addr; + in_addr_t *addrs; + u_char *cname; + } u; + + u_short naddrs; + u_short cnlen; + + time_t expire; + time_t valid; + + ngx_resolver_ctx_t *waiting; +} ngx_resolver_node_t; + + +typedef struct { + /* has to be pointer because of "incomplete type" */ + ngx_event_t *event; + + /* TODO: DNS peers balancer */ + /* STUB */ + ngx_udp_connection_t *udp_connection; + + ngx_log_t *log; + + /* ident must be after 3 pointers */ + ngx_int_t ident; + + ngx_rbtree_t name_rbtree; + ngx_rbtree_node_t name_sentinel; + + ngx_rbtree_t addr_rbtree; + ngx_rbtree_node_t addr_sentinel; + + ngx_queue_t name_resend_queue; + ngx_queue_t addr_resend_queue; + + ngx_queue_t name_expire_queue; + ngx_queue_t addr_expire_queue; + + time_t resend_timeout; + time_t expire; + time_t valid; + + ngx_uint_t log_level; +} ngx_resolver_t; + + +struct ngx_resolver_ctx_s { + ngx_resolver_ctx_t *next; + ngx_resolver_t *resolver; + ngx_udp_connection_t *udp_connection; + + /* ident must be after 3 pointers */ + ngx_int_t ident; + + ngx_int_t state; + ngx_int_t type; + ngx_str_t name; + + ngx_uint_t naddrs; + in_addr_t *addrs; + in_addr_t addr; + + /* TODO: DNS peers balancer ctx */ + + ngx_resolver_handler_pt handler; + void *data; + ngx_msec_t timeout; + + ngx_uint_t quick; /* unsigned quick:1; */ + ngx_event_t *event; +}; + + +ngx_resolver_t *ngx_resolver_create(ngx_peer_addr_t *addr, ngx_log_t *log); +ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r, + ngx_resolver_ctx_t *temp); +ngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx); +void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx); +ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx); +void ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx); +void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size); +char *ngx_resolver_strerror(ngx_int_t err); + + +#endif /* _NGX_RESOLVER_H_INCLUDED_ */