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_int_t rc; 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 */ if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { ctx->name.len = rn->cnlen; ctx->name.data = rn->u.cname; return ngx_resolve_name_locked(r, ctx); } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_RESOLVE_NXDOMAIN; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; } ngx_queue_remove(&rn->queue); /* lock alloc mutex */ ngx_resolver_free_locked(r, rn->query); rn->query = NULL; 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; rn->query = NULL; ngx_rbtree_insert(&r->name_rbtree, &rn->node); } rc = ngx_resolver_create_name_query(rn, ctx); if (rc == NGX_ERROR) { goto failed; } if (rc == NGX_DECLINED) { ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free(r, rn->query); ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); ctx->state = NGX_RESOLVE_NXDOMAIN; ctx->handler(ctx); return NGX_OK; } 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); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); return NGX_ERROR; }
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; int32_t ttl; 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 %ui response for %V, expect %ui", ident, &name, qident); goto failed; } ngx_resolver_free(r, name.data); if (code == 0 && nan == 0) { code = 3; /* NXDOMAIN */ } 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; ttl = 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; ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + (an->ttl[2] << 8) + (an->ttl[3]); if (ttl < 0) { ttl = 0; } 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; } else if (qtype == NGX_RESOLVE_DNAME) { i += sizeof(ngx_resolver_an_t) + len; } else { ngx_log_error(r->log_level, r->log, 0, "unexpected qtype %ui", qtype); } } ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver naddrs:%ui cname:%p ttl:%d", naddrs, cname, ttl); 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 ? r->valid : ttl); 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 > 1) { 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); ngx_queue_remove(&rn->queue); rn->cnlen = (u_short) name.len; rn->u.cname = name.data; rn->valid = ngx_time() + (r->valid ? r->valid : ttl); 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: %ui", 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 */ ngx_resolver_free(r, name.data); return; }
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 */ /* lock alloc mutex */ if (ctx->event) { ngx_resolver_free_locked(r, ctx->event); } ngx_resolver_free_locked(r, ctx); /* unlock alloc mutex */ }
static void ngx_http_upstream_dynamic_server_resolve_handler(ngx_resolver_ctx_t *ctx) { ngx_http_upstream_dynamic_server_main_conf_t *udsmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_upstream_dynamic_servers_module); ngx_http_upstream_dynamic_server_conf_t *dynamic_server; ngx_conf_t cf; uint32_t hash; ngx_resolver_node_t *rn; ngx_pool_t *new_pool; ngx_addr_t *addrs; dynamic_server = ctx->data; ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Finished resolving '%V'", &ctx->name); if (ctx->state) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: '%V' could not be resolved (%i: %s)", &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); ngx_url_t u; ngx_memzero(&u, sizeof(ngx_url_t)); // If the domain fails to resolve on start up, assign a static IP that // should never route (we'll also mark it as down in the upstream later // on). This is to account for various things inside nginx that seem to // expect a server to always have at least 1 IP. u.url = ngx_http_upstream_dynamic_server_null_route; u.default_port = 80; u.no_resolve = 1; if (ngx_parse_url(ngx_cycle->pool, &u) != NGX_OK) { if (u.err) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "%s in upstream \"%V\"", u.err, &u.url); } goto end; } ctx->addr.sockaddr = u.addrs[0].sockaddr; ctx->addr.socklen = u.addrs[0].socklen; ctx->addr.name = u.addrs[0].name; ctx->addrs = &ctx->addr; ctx->naddrs = u.naddrs; } if (ctx->naddrs != dynamic_server->server->naddrs) { goto reinit_upstream; } ngx_uint_t i, j, founded; ngx_addr_t *existing_addr; for (i = 0; i < ctx->naddrs; i++) { founded = 0; for (j = 0; j < ctx->naddrs; j++) { existing_addr = &dynamic_server->server->addrs[j]; if (ngx_cmp_sockaddr(existing_addr->sockaddr, existing_addr->socklen, ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, 0) == NGX_OK) { founded = 1; break; } } if (!founded) { goto reinit_upstream; } } ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: No DNS changes for '%V' - keeping existing upstream configuration", &ctx->name); goto end; reinit_upstream: new_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, ctx->resolver->log); if (new_pool == NULL) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: Could not create new pool"); goto end; } ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: DNS changes for '%V' detected - reinitializing upstream configuration", &ctx->name); ngx_memzero(&cf, sizeof(ngx_conf_t)); cf.name = "dynamic_server_init_upstream"; cf.cycle = (ngx_cycle_t *) ngx_cycle; cf.pool = new_pool; cf.module_type = NGX_HTTP_MODULE; cf.cmd_type = NGX_HTTP_MAIN_CONF; cf.log = ngx_cycle->log; cf.ctx = udsmcf->conf_ctx; addrs = ngx_pcalloc(new_pool, ctx->naddrs * sizeof(ngx_addr_t)); ngx_memcpy(addrs, ctx->addrs, ctx->naddrs * sizeof(ngx_addr_t)); struct sockaddr *sockaddr; ngx_addr_t *addr; socklen_t socklen; for (i = 0; i < ctx->naddrs; i++) { addr = &addrs[i]; socklen = ctx->addrs[i].socklen; sockaddr = ngx_palloc(new_pool, socklen); ngx_memcpy(sockaddr, ctx->addrs[i].sockaddr, socklen); switch(sockaddr->sa_family) { case AF_INET6: ((struct sockaddr_in6 *)sockaddr)->sin6_port = htons((u_short) dynamic_server->port); break; default: ((struct sockaddr_in *)sockaddr)->sin_port = htons((u_short) dynamic_server->port); } addr->sockaddr = sockaddr; addr->socklen = socklen; u_char *p; size_t len; p = ngx_pnalloc(new_pool, NGX_SOCKADDR_STRLEN); if (p == NULL) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: Error initializing sockaddr"); ngx_destroy_pool(new_pool); goto end; } len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); addr->name.len = len; addr->name.data = p; ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: '%V' was resolved to '%V'", &ctx->name, &addr->name); } // If the domain failed to resolve, mark this server as down. dynamic_server->server->down = ctx->state ? 1 : 0; dynamic_server->server->addrs = addrs; dynamic_server->server->naddrs = ctx->naddrs; ngx_http_upstream_init_pt init; init = dynamic_server->upstream_conf->peer.init_upstream ? dynamic_server->upstream_conf->peer.init_upstream : ngx_http_upstream_init_round_robin; if (init(&cf, dynamic_server->upstream_conf) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, ctx->resolver->log, 0, "upstream-dynamic-servers: Error re-initializing upstream after DNS changes"); } if (dynamic_server->previous_pool != NULL) { ngx_destroy_pool(dynamic_server->previous_pool); dynamic_server->previous_pool = NULL; } dynamic_server->previous_pool = dynamic_server->pool; dynamic_server->pool = new_pool; end: if (ctx->resolver->log->log_level & NGX_LOG_DEBUG_CORE) { hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(ctx->resolver, &ctx->name, hash); uint32_t refresh_in; if (rn != NULL && rn->ttl) { refresh_in = (rn->valid - ngx_time()) * 1000; if (!refresh_in || refresh_in < 1000) { refresh_in = 1000; } } else { refresh_in = 1000; } ngx_log_debug(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, "upstream-dynamic-servers: Refreshing DNS of '%V' in %ims", &ctx->name, refresh_in); } ngx_resolve_name_done(ctx); if (ngx_exiting) { ngx_log_debug(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "upstream-dynamic-servers: worker is about to exit, do not set the timer again"); return; } ngx_add_timer(&dynamic_server->timer, 1000); }