mirror of
https://github.com/nginx/nginx.git
synced 2025-07-22 20:38:26 +08:00

Similar to 70e65bf8dfd7, the change is made to ensure that the ability to cancel resolver tasks is fully controlled by the caller. As mentioned in the referenced commit, it is safe to make this timer cancelable because resolve tasks can have their own timeouts that are not cancelable. The scenario where this may become a problem is a periodic background resolve task (not tied to a specific request or a client connection), which receives a response with short TTL, large enough to warrant fallback to a TCP query. With each event loop wakeup, we either have a previously set write timer instance or schedule a new one. The non-cancelable write timer can delay or block graceful shutdown of a worker even if the ngx_resolver_ctx_t->cancelable flag is set by the API user, and there are no other tasks or connections. We use the resolver API in this way to maintain the list of upstream server addresses specified with the 'resolve' parameter, and there could be third-party modules implementing similar logic.
4708 lines
104 KiB
C
4708 lines
104 KiB
C
|
|
/*
|
|
* Copyright (C) Igor Sysoev
|
|
* Copyright (C) Nginx, Inc.
|
|
*/
|
|
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_event.h>
|
|
|
|
|
|
#define NGX_RESOLVER_UDP_SIZE 4096
|
|
|
|
#define NGX_RESOLVER_TCP_RSIZE (2 + 65535)
|
|
#define NGX_RESOLVER_TCP_WSIZE 8192
|
|
|
|
|
|
typedef struct {
|
|
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_hdr_t;
|
|
|
|
|
|
typedef struct {
|
|
u_char type_hi;
|
|
u_char type_lo;
|
|
u_char class_hi;
|
|
u_char class_lo;
|
|
} ngx_resolver_qs_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;
|
|
|
|
|
|
#define ngx_resolver_node(n) ngx_rbtree_data(n, ngx_resolver_node_t, node)
|
|
|
|
|
|
static ngx_int_t ngx_udp_connect(ngx_resolver_connection_t *rec);
|
|
static ngx_int_t ngx_tcp_connect(ngx_resolver_connection_t *rec);
|
|
|
|
|
|
static void ngx_resolver_cleanup(void *data);
|
|
static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);
|
|
static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,
|
|
ngx_resolver_ctx_t *ctx, ngx_str_t *name);
|
|
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_send_udp_query(ngx_resolver_t *r,
|
|
ngx_resolver_connection_t *rec, u_char *query, u_short qlen);
|
|
static ngx_int_t ngx_resolver_send_tcp_query(ngx_resolver_t *r,
|
|
ngx_resolver_connection_t *rec, u_char *query, u_short qlen);
|
|
static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r,
|
|
ngx_resolver_node_t *rn, ngx_str_t *name);
|
|
static ngx_int_t ngx_resolver_create_srv_query(ngx_resolver_t *r,
|
|
ngx_resolver_node_t *rn, ngx_str_t *name);
|
|
static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_t *r,
|
|
ngx_resolver_node_t *rn, ngx_resolver_addr_t *addr);
|
|
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 ngx_uint_t ngx_resolver_resend_empty(ngx_resolver_t *r);
|
|
static void ngx_resolver_udp_read(ngx_event_t *rev);
|
|
static void ngx_resolver_tcp_write(ngx_event_t *wev);
|
|
static void ngx_resolver_tcp_read(ngx_event_t *rev);
|
|
static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf,
|
|
size_t n, ngx_uint_t tcp);
|
|
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 qtype,
|
|
ngx_uint_t nan, ngx_uint_t trunc, ngx_uint_t ans);
|
|
static void ngx_resolver_process_srv(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 trunc, ngx_uint_t ans);
|
|
static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
|
|
ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);
|
|
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_srv(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 ngx_int_t ngx_resolver_set_timeout(ngx_resolver_t *r,
|
|
ngx_resolver_ctx_t *ctx);
|
|
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_calloc(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);
|
|
static ngx_resolver_addr_t *ngx_resolver_export(ngx_resolver_t *r,
|
|
ngx_resolver_node_t *rn, ngx_uint_t rotate);
|
|
static void ngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx);
|
|
static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len);
|
|
static void ngx_resolver_resolve_srv_names(ngx_resolver_ctx_t *ctx,
|
|
ngx_resolver_node_t *rn);
|
|
static void ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *ctx);
|
|
static ngx_int_t ngx_resolver_cmp_srvs(const void *one, const void *two);
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
static void ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp,
|
|
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
|
|
static ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r,
|
|
struct in6_addr *addr, uint32_t hash);
|
|
#endif
|
|
|
|
|
|
ngx_resolver_t *
|
|
ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n)
|
|
{
|
|
ngx_str_t s;
|
|
ngx_url_t u;
|
|
ngx_uint_t i, j;
|
|
ngx_resolver_t *r;
|
|
ngx_pool_cleanup_t *cln;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
r = ngx_pcalloc(cf->pool, sizeof(ngx_resolver_t));
|
|
if (r == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
r->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
|
|
if (r->event == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
cln = ngx_pool_cleanup_add(cf->pool, 0);
|
|
if (cln == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
cln->handler = ngx_resolver_cleanup;
|
|
cln->data = r;
|
|
|
|
ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel,
|
|
ngx_resolver_rbtree_insert_value);
|
|
|
|
ngx_rbtree_init(&r->srv_rbtree, &r->srv_sentinel,
|
|
ngx_resolver_rbtree_insert_value);
|
|
|
|
ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel,
|
|
ngx_rbtree_insert_value);
|
|
|
|
ngx_queue_init(&r->name_resend_queue);
|
|
ngx_queue_init(&r->srv_resend_queue);
|
|
ngx_queue_init(&r->addr_resend_queue);
|
|
|
|
ngx_queue_init(&r->name_expire_queue);
|
|
ngx_queue_init(&r->srv_expire_queue);
|
|
ngx_queue_init(&r->addr_expire_queue);
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
r->ipv6 = 1;
|
|
|
|
ngx_rbtree_init(&r->addr6_rbtree, &r->addr6_sentinel,
|
|
ngx_resolver_rbtree_insert_addr6_value);
|
|
|
|
ngx_queue_init(&r->addr6_resend_queue);
|
|
|
|
ngx_queue_init(&r->addr6_expire_queue);
|
|
#endif
|
|
|
|
r->event->handler = ngx_resolver_resend_handler;
|
|
r->event->data = r;
|
|
r->event->log = &cf->cycle->new_log;
|
|
r->event->cancelable = 1;
|
|
r->ident = -1;
|
|
|
|
r->resend_timeout = 5;
|
|
r->tcp_timeout = 5;
|
|
r->expire = 30;
|
|
r->valid = 0;
|
|
|
|
r->log = &cf->cycle->new_log;
|
|
r->log_level = NGX_LOG_ERR;
|
|
|
|
if (n) {
|
|
if (ngx_array_init(&r->connections, cf->pool, n,
|
|
sizeof(ngx_resolver_connection_t))
|
|
!= NGX_OK)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (ngx_strncmp(names[i].data, "valid=", 6) == 0) {
|
|
s.len = names[i].len - 6;
|
|
s.data = names[i].data + 6;
|
|
|
|
r->valid = ngx_parse_time(&s, 1);
|
|
|
|
if (r->valid == (time_t) NGX_ERROR) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"invalid parameter: %V", &names[i]);
|
|
return NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (ngx_strncmp(names[i].data, "ipv6=", 5) == 0) {
|
|
|
|
if (ngx_strcmp(&names[i].data[5], "on") == 0) {
|
|
r->ipv6 = 1;
|
|
|
|
} else if (ngx_strcmp(&names[i].data[5], "off") == 0) {
|
|
r->ipv6 = 0;
|
|
|
|
} else {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"invalid parameter: %V", &names[i]);
|
|
return NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
ngx_memzero(&u, sizeof(ngx_url_t));
|
|
|
|
u.url = names[i];
|
|
u.default_port = 53;
|
|
|
|
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
|
|
if (u.err) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"%s in resolver \"%V\"",
|
|
u.err, &u.url);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rec = ngx_array_push_n(&r->connections, u.naddrs);
|
|
if (rec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ngx_memzero(rec, u.naddrs * sizeof(ngx_resolver_connection_t));
|
|
|
|
for (j = 0; j < u.naddrs; j++) {
|
|
rec[j].sockaddr = u.addrs[j].sockaddr;
|
|
rec[j].socklen = u.addrs[j].socklen;
|
|
rec[j].server = u.addrs[j].name;
|
|
rec[j].resolver = r;
|
|
}
|
|
}
|
|
|
|
if (n && r->connections.nelts == 0) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no name servers defined");
|
|
return NULL;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_cleanup(void *data)
|
|
{
|
|
ngx_resolver_t *r = data;
|
|
|
|
ngx_uint_t i;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "cleanup resolver");
|
|
|
|
ngx_resolver_cleanup_tree(r, &r->name_rbtree);
|
|
|
|
ngx_resolver_cleanup_tree(r, &r->srv_rbtree);
|
|
|
|
ngx_resolver_cleanup_tree(r, &r->addr_rbtree);
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
ngx_resolver_cleanup_tree(r, &r->addr6_rbtree);
|
|
#endif
|
|
|
|
if (r->event->timer_set) {
|
|
ngx_del_timer(r->event);
|
|
}
|
|
|
|
rec = r->connections.elts;
|
|
|
|
for (i = 0; i < r->connections.nelts; i++) {
|
|
if (rec[i].udp) {
|
|
ngx_close_connection(rec[i].udp);
|
|
}
|
|
|
|
if (rec[i].tcp) {
|
|
ngx_close_connection(rec[i].tcp);
|
|
}
|
|
|
|
if (rec[i].read_buf) {
|
|
ngx_resolver_free(r, rec[i].read_buf->start);
|
|
ngx_resolver_free(r, rec[i].read_buf);
|
|
}
|
|
|
|
if (rec[i].write_buf) {
|
|
ngx_resolver_free(r, rec[i].write_buf->start);
|
|
ngx_resolver_free(r, rec[i].write_buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree)
|
|
{
|
|
ngx_resolver_ctx_t *ctx, *next;
|
|
ngx_resolver_node_t *rn;
|
|
|
|
while (tree->root != tree->sentinel) {
|
|
|
|
rn = ngx_resolver_node(ngx_rbtree_min(tree->root, tree->sentinel));
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
for (ctx = rn->waiting; ctx; ctx = next) {
|
|
next = ctx->next;
|
|
|
|
if (ctx->event) {
|
|
if (ctx->event->timer_set) {
|
|
ngx_del_timer(ctx->event);
|
|
}
|
|
|
|
ngx_resolver_free(r, ctx->event);
|
|
}
|
|
|
|
ngx_resolver_free(r, ctx);
|
|
}
|
|
|
|
ngx_rbtree_delete(tree, &rn->node);
|
|
|
|
ngx_resolver_free_node(r, rn);
|
|
}
|
|
}
|
|
|
|
|
|
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.sockaddr = (struct sockaddr *) &temp->sin;
|
|
temp->addr.socklen = sizeof(struct sockaddr_in);
|
|
ngx_memzero(&temp->sin, sizeof(struct sockaddr_in));
|
|
temp->sin.sin_family = AF_INET;
|
|
temp->sin.sin_addr.s_addr = addr;
|
|
temp->quick = 1;
|
|
|
|
return temp;
|
|
}
|
|
}
|
|
|
|
if (r->connections.nelts == 0) {
|
|
return NGX_NO_RESOLVER;
|
|
}
|
|
|
|
ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t));
|
|
|
|
if (ctx) {
|
|
ctx->resolver = r;
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_resolve_name(ngx_resolver_ctx_t *ctx)
|
|
{
|
|
size_t slen;
|
|
ngx_int_t rc;
|
|
ngx_str_t name;
|
|
ngx_resolver_t *r;
|
|
|
|
r = ctx->resolver;
|
|
|
|
if (ctx->name.len > 0 && ctx->name.data[ctx->name.len - 1] == '.') {
|
|
ctx->name.len--;
|
|
}
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolve: \"%V\"", &ctx->name);
|
|
|
|
if (ctx->quick) {
|
|
ctx->handler(ctx);
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (ctx->service.len) {
|
|
slen = ctx->service.len;
|
|
|
|
if (ngx_strlchr(ctx->service.data,
|
|
ctx->service.data + ctx->service.len, '.')
|
|
== NULL)
|
|
{
|
|
slen += sizeof("_._tcp") - 1;
|
|
}
|
|
|
|
name.len = slen + 1 + ctx->name.len;
|
|
|
|
name.data = ngx_resolver_alloc(r, name.len);
|
|
if (name.data == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
if (slen == ctx->service.len) {
|
|
ngx_sprintf(name.data, "%V.%V", &ctx->service, &ctx->name);
|
|
|
|
} else {
|
|
ngx_sprintf(name.data, "_%V._tcp.%V", &ctx->service, &ctx->name);
|
|
}
|
|
|
|
/* lock name mutex */
|
|
|
|
rc = ngx_resolve_name_locked(r, ctx, &name);
|
|
|
|
ngx_resolver_free(r, name.data);
|
|
|
|
} else {
|
|
/* lock name mutex */
|
|
|
|
rc = ngx_resolve_name_locked(r, ctx, &ctx->name);
|
|
}
|
|
|
|
if (rc == NGX_OK) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
/* unlock name mutex */
|
|
|
|
if (rc == NGX_AGAIN) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
/* NGX_ERROR */
|
|
|
|
if (ctx->event) {
|
|
ngx_resolver_free(r, ctx->event);
|
|
}
|
|
|
|
failed:
|
|
|
|
ngx_resolver_free(r, ctx);
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
|
|
{
|
|
ngx_uint_t i;
|
|
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->nsrvs) {
|
|
for (i = 0; i < ctx->nsrvs; i++) {
|
|
if (ctx->srvs[i].ctx) {
|
|
ngx_resolve_name_done(ctx->srvs[i].ctx);
|
|
}
|
|
|
|
if (ctx->srvs[i].addrs) {
|
|
ngx_resolver_free(r, ctx->srvs[i].addrs->sockaddr);
|
|
ngx_resolver_free(r, ctx->srvs[i].addrs);
|
|
}
|
|
|
|
ngx_resolver_free(r, ctx->srvs[i].name.data);
|
|
}
|
|
|
|
ngx_resolver_free(r, ctx->srvs);
|
|
}
|
|
|
|
if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
|
|
|
|
rn = ctx->node;
|
|
|
|
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:
|
|
|
|
if (ctx->service.len) {
|
|
ngx_resolver_expire(r, &r->srv_rbtree, &r->srv_expire_queue);
|
|
|
|
} else {
|
|
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 */
|
|
|
|
if (r->event->timer_set && ngx_resolver_resend_empty(r)) {
|
|
ngx_del_timer(r->event);
|
|
}
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx,
|
|
ngx_str_t *name)
|
|
{
|
|
uint32_t hash;
|
|
ngx_int_t rc;
|
|
ngx_str_t cname;
|
|
ngx_uint_t i, naddrs;
|
|
ngx_queue_t *resend_queue, *expire_queue;
|
|
ngx_rbtree_t *tree;
|
|
ngx_resolver_ctx_t *next, *last;
|
|
ngx_resolver_addr_t *addrs;
|
|
ngx_resolver_node_t *rn;
|
|
|
|
ngx_strlow(name->data, name->data, name->len);
|
|
|
|
hash = ngx_crc32_short(name->data, name->len);
|
|
|
|
if (ctx->service.len) {
|
|
rn = ngx_resolver_lookup_srv(r, name, hash);
|
|
|
|
tree = &r->srv_rbtree;
|
|
resend_queue = &r->srv_resend_queue;
|
|
expire_queue = &r->srv_expire_queue;
|
|
|
|
} else {
|
|
rn = ngx_resolver_lookup_name(r, name, hash);
|
|
|
|
tree = &r->name_rbtree;
|
|
resend_queue = &r->name_resend_queue;
|
|
expire_queue = &r->name_expire_queue;
|
|
}
|
|
|
|
if (rn) {
|
|
|
|
/* ctx can be a list after NGX_RESOLVE_CNAME */
|
|
for (last = ctx; last->next; last = last->next);
|
|
|
|
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(expire_queue, &rn->queue);
|
|
|
|
naddrs = (rn->naddrs == (u_short) -1) ? 0 : rn->naddrs;
|
|
#if (NGX_HAVE_INET6)
|
|
naddrs += (rn->naddrs6 == (u_short) -1) ? 0 : rn->naddrs6;
|
|
#endif
|
|
|
|
if (naddrs) {
|
|
|
|
if (naddrs == 1 && rn->naddrs == 1) {
|
|
addrs = NULL;
|
|
|
|
} else {
|
|
addrs = ngx_resolver_export(r, rn, 1);
|
|
if (addrs == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
last->next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
/* unlock name mutex */
|
|
|
|
do {
|
|
ctx->state = NGX_OK;
|
|
ctx->valid = rn->valid;
|
|
ctx->naddrs = naddrs;
|
|
|
|
if (addrs == NULL) {
|
|
ctx->addrs = &ctx->addr;
|
|
ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin;
|
|
ctx->addr.socklen = sizeof(struct sockaddr_in);
|
|
ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in));
|
|
ctx->sin.sin_family = AF_INET;
|
|
ctx->sin.sin_addr.s_addr = rn->u.addr;
|
|
|
|
} else {
|
|
ctx->addrs = addrs;
|
|
}
|
|
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ctx = next;
|
|
} while (ctx);
|
|
|
|
if (addrs != NULL) {
|
|
ngx_resolver_free(r, addrs->sockaddr);
|
|
ngx_resolver_free(r, addrs);
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (rn->nsrvs) {
|
|
last->next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
/* unlock name mutex */
|
|
|
|
do {
|
|
next = ctx->next;
|
|
|
|
ngx_resolver_resolve_srv_names(ctx, rn);
|
|
|
|
ctx = next;
|
|
} while (ctx);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
/* NGX_RESOLVE_CNAME */
|
|
|
|
if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {
|
|
|
|
cname.len = rn->cnlen;
|
|
cname.data = rn->u.cname;
|
|
|
|
return ngx_resolve_name_locked(r, ctx, &cname);
|
|
}
|
|
|
|
last->next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
/* unlock name mutex */
|
|
|
|
do {
|
|
ctx->state = NGX_RESOLVE_NXDOMAIN;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ctx = next;
|
|
} while (ctx);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (rn->waiting) {
|
|
if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
last->next = rn->waiting;
|
|
rn->waiting = ctx;
|
|
ctx->state = NGX_AGAIN;
|
|
ctx->async = 1;
|
|
|
|
do {
|
|
ctx->node = rn;
|
|
ctx = ctx->next;
|
|
} while (ctx);
|
|
|
|
return NGX_AGAIN;
|
|
}
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
/* lock alloc mutex */
|
|
|
|
if (rn->query) {
|
|
ngx_resolver_free_locked(r, rn->query);
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
}
|
|
|
|
if (rn->cnlen) {
|
|
ngx_resolver_free_locked(r, rn->u.cname);
|
|
}
|
|
|
|
if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) {
|
|
ngx_resolver_free_locked(r, rn->u.addrs);
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) {
|
|
ngx_resolver_free_locked(r, rn->u6.addrs6);
|
|
}
|
|
#endif
|
|
|
|
if (rn->nsrvs) {
|
|
for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {
|
|
if (rn->u.srvs[i].name.data) {
|
|
ngx_resolver_free_locked(r, rn->u.srvs[i].name.data);
|
|
}
|
|
}
|
|
|
|
ngx_resolver_free_locked(r, rn->u.srvs);
|
|
}
|
|
|
|
/* 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, name->data, name->len);
|
|
if (rn->name == NULL) {
|
|
ngx_resolver_free(r, rn);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rn->node.key = hash;
|
|
rn->nlen = (u_short) name->len;
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
|
|
ngx_rbtree_insert(tree, &rn->node);
|
|
}
|
|
|
|
if (ctx->service.len) {
|
|
rc = ngx_resolver_create_srv_query(r, rn, name);
|
|
|
|
} else {
|
|
rc = ngx_resolver_create_name_query(r, rn, name);
|
|
}
|
|
|
|
if (rc == NGX_ERROR) {
|
|
goto failed;
|
|
}
|
|
|
|
if (rc == NGX_DECLINED) {
|
|
ngx_rbtree_delete(tree, &rn->node);
|
|
|
|
ngx_resolver_free(r, rn->query);
|
|
ngx_resolver_free(r, rn->name);
|
|
ngx_resolver_free(r, rn);
|
|
|
|
do {
|
|
ctx->state = NGX_RESOLVE_NXDOMAIN;
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ctx = next;
|
|
} while (ctx);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
rn->last_connection = r->last_connection++;
|
|
if (r->last_connection == r->connections.nelts) {
|
|
r->last_connection = 0;
|
|
}
|
|
|
|
rn->naddrs = (u_short) -1;
|
|
rn->tcp = 0;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->naddrs6 = r->ipv6 ? (u_short) -1 : 0;
|
|
rn->tcp6 = 0;
|
|
#endif
|
|
rn->nsrvs = 0;
|
|
|
|
if (ngx_resolver_send_query(r, rn) != NGX_OK) {
|
|
|
|
/* immediately retry once on failure */
|
|
|
|
rn->last_connection++;
|
|
if (rn->last_connection == r->connections.nelts) {
|
|
rn->last_connection = 0;
|
|
}
|
|
|
|
(void) ngx_resolver_send_query(r, rn);
|
|
}
|
|
|
|
if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
if (ngx_resolver_resend_empty(r)) {
|
|
ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
|
|
}
|
|
|
|
rn->expire = ngx_time() + r->resend_timeout;
|
|
|
|
ngx_queue_insert_head(resend_queue, &rn->queue);
|
|
|
|
rn->code = 0;
|
|
rn->cnlen = 0;
|
|
rn->valid = 0;
|
|
rn->ttl = NGX_MAX_UINT32_VALUE;
|
|
rn->waiting = ctx;
|
|
|
|
ctx->state = NGX_AGAIN;
|
|
ctx->async = 1;
|
|
|
|
do {
|
|
ctx->node = rn;
|
|
ctx = ctx->next;
|
|
} while (ctx);
|
|
|
|
return NGX_AGAIN;
|
|
|
|
failed:
|
|
|
|
ngx_rbtree_delete(tree, &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;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
|
|
{
|
|
u_char *name;
|
|
in_addr_t addr;
|
|
ngx_queue_t *resend_queue, *expire_queue;
|
|
ngx_rbtree_t *tree;
|
|
ngx_resolver_t *r;
|
|
struct sockaddr_in *sin;
|
|
ngx_resolver_node_t *rn;
|
|
#if (NGX_HAVE_INET6)
|
|
uint32_t hash;
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
|
|
#if (NGX_SUPPRESS_WARN)
|
|
addr = 0;
|
|
#if (NGX_HAVE_INET6)
|
|
hash = 0;
|
|
sin6 = NULL;
|
|
#endif
|
|
#endif
|
|
|
|
r = ctx->resolver;
|
|
|
|
switch (ctx->addr.sockaddr->sa_family) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case AF_INET6:
|
|
sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;
|
|
hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);
|
|
|
|
/* lock addr mutex */
|
|
|
|
rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);
|
|
|
|
tree = &r->addr6_rbtree;
|
|
resend_queue = &r->addr6_resend_queue;
|
|
expire_queue = &r->addr6_expire_queue;
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* AF_INET */
|
|
sin = (struct sockaddr_in *) ctx->addr.sockaddr;
|
|
addr = ntohl(sin->sin_addr.s_addr);
|
|
|
|
/* lock addr mutex */
|
|
|
|
rn = ngx_resolver_lookup_addr(r, addr);
|
|
|
|
tree = &r->addr_rbtree;
|
|
resend_queue = &r->addr_resend_queue;
|
|
expire_queue = &r->addr_expire_queue;
|
|
}
|
|
|
|
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(expire_queue, &rn->queue);
|
|
|
|
name = ngx_resolver_dup(r, rn->name, rn->nlen);
|
|
if (name == NULL) {
|
|
ngx_resolver_free(r, ctx);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ctx->name.len = rn->nlen;
|
|
ctx->name.data = name;
|
|
|
|
/* unlock addr mutex */
|
|
|
|
ctx->state = NGX_OK;
|
|
ctx->valid = rn->valid;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ngx_resolver_free(r, name);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (rn->waiting) {
|
|
if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ctx->next = rn->waiting;
|
|
rn->waiting = ctx;
|
|
ctx->state = NGX_AGAIN;
|
|
ctx->async = 1;
|
|
ctx->node = rn;
|
|
|
|
/* unlock addr mutex */
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
ngx_resolver_free(r, rn->query);
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
|
|
} else {
|
|
rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
|
|
if (rn == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
switch (ctx->addr.sockaddr->sa_family) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case AF_INET6:
|
|
rn->addr6 = sin6->sin6_addr;
|
|
rn->node.key = hash;
|
|
break;
|
|
#endif
|
|
|
|
default: /* AF_INET */
|
|
rn->node.key = addr;
|
|
}
|
|
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
|
|
ngx_rbtree_insert(tree, &rn->node);
|
|
}
|
|
|
|
if (ngx_resolver_create_addr_query(r, rn, &ctx->addr) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
rn->last_connection = r->last_connection++;
|
|
if (r->last_connection == r->connections.nelts) {
|
|
r->last_connection = 0;
|
|
}
|
|
|
|
rn->naddrs = (u_short) -1;
|
|
rn->tcp = 0;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->naddrs6 = (u_short) -1;
|
|
rn->tcp6 = 0;
|
|
#endif
|
|
rn->nsrvs = 0;
|
|
|
|
if (ngx_resolver_send_query(r, rn) != NGX_OK) {
|
|
|
|
/* immediately retry once on failure */
|
|
|
|
rn->last_connection++;
|
|
if (rn->last_connection == r->connections.nelts) {
|
|
rn->last_connection = 0;
|
|
}
|
|
|
|
(void) ngx_resolver_send_query(r, rn);
|
|
}
|
|
|
|
if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
if (ngx_resolver_resend_empty(r)) {
|
|
ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
|
|
}
|
|
|
|
rn->expire = ngx_time() + r->resend_timeout;
|
|
|
|
ngx_queue_insert_head(resend_queue, &rn->queue);
|
|
|
|
rn->code = 0;
|
|
rn->cnlen = 0;
|
|
rn->name = NULL;
|
|
rn->nlen = 0;
|
|
rn->valid = 0;
|
|
rn->ttl = NGX_MAX_UINT32_VALUE;
|
|
rn->waiting = ctx;
|
|
|
|
/* unlock addr mutex */
|
|
|
|
ctx->state = NGX_AGAIN;
|
|
ctx->async = 1;
|
|
ctx->node = rn;
|
|
|
|
return NGX_OK;
|
|
|
|
failed:
|
|
|
|
if (rn) {
|
|
ngx_rbtree_delete(tree, &rn->node);
|
|
|
|
if (rn->query) {
|
|
ngx_resolver_free(r, rn->query);
|
|
}
|
|
|
|
ngx_resolver_free(r, rn);
|
|
}
|
|
|
|
/* unlock addr mutex */
|
|
|
|
if (ctx->event) {
|
|
ngx_resolver_free(r, ctx->event);
|
|
}
|
|
|
|
ngx_resolver_free(r, ctx);
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
|
|
{
|
|
ngx_queue_t *expire_queue;
|
|
ngx_rbtree_t *tree;
|
|
ngx_resolver_t *r;
|
|
ngx_resolver_ctx_t *w, **p;
|
|
ngx_resolver_node_t *rn;
|
|
|
|
r = ctx->resolver;
|
|
|
|
switch (ctx->addr.sockaddr->sa_family) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case AF_INET6:
|
|
tree = &r->addr6_rbtree;
|
|
expire_queue = &r->addr6_expire_queue;
|
|
break;
|
|
#endif
|
|
|
|
default: /* AF_INET */
|
|
tree = &r->addr_rbtree;
|
|
expire_queue = &r->addr_expire_queue;
|
|
}
|
|
|
|
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 = ctx->node;
|
|
|
|
if (rn) {
|
|
p = &rn->waiting;
|
|
w = rn->waiting;
|
|
|
|
while (w) {
|
|
if (w == ctx) {
|
|
*p = w->next;
|
|
|
|
goto done;
|
|
}
|
|
|
|
p = &w->next;
|
|
w = w->next;
|
|
}
|
|
}
|
|
|
|
{
|
|
u_char text[NGX_SOCKADDR_STRLEN];
|
|
ngx_str_t addrtext;
|
|
|
|
addrtext.data = text;
|
|
addrtext.len = ngx_sock_ntop(ctx->addr.sockaddr, ctx->addr.socklen,
|
|
text, NGX_SOCKADDR_STRLEN, 0);
|
|
|
|
ngx_log_error(NGX_LOG_ALERT, r->log, 0,
|
|
"could not cancel %V resolving", &addrtext);
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
ngx_resolver_expire(r, tree, expire_queue);
|
|
|
|
/* 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 */
|
|
|
|
if (r->event->timer_set && ngx_resolver_resend_empty(r)) {
|
|
ngx_del_timer(r->event);
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolver expire \"%*s\"", (size_t) rn->nlen, rn->name);
|
|
|
|
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)
|
|
{
|
|
ngx_int_t rc;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
rec = r->connections.elts;
|
|
rec = &rec[rn->last_connection];
|
|
|
|
if (rec->log.handler == NULL) {
|
|
rec->log = *r->log;
|
|
rec->log.handler = ngx_resolver_log_error;
|
|
rec->log.data = rec;
|
|
rec->log.action = "resolving";
|
|
}
|
|
|
|
if (rn->naddrs == (u_short) -1) {
|
|
rc = rn->tcp ? ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen)
|
|
: ngx_resolver_send_udp_query(r, rec, rn->query, rn->qlen);
|
|
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
if (rn->query6 && rn->naddrs6 == (u_short) -1) {
|
|
rc = rn->tcp6
|
|
? ngx_resolver_send_tcp_query(r, rec, rn->query6, rn->qlen)
|
|
: ngx_resolver_send_udp_query(r, rec, rn->query6, rn->qlen);
|
|
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_send_udp_query(ngx_resolver_t *r, ngx_resolver_connection_t *rec,
|
|
u_char *query, u_short qlen)
|
|
{
|
|
ssize_t n;
|
|
|
|
if (rec->udp == NULL) {
|
|
if (ngx_udp_connect(rec) != NGX_OK) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rec->udp->data = rec;
|
|
rec->udp->read->handler = ngx_resolver_udp_read;
|
|
rec->udp->read->resolver = 1;
|
|
}
|
|
|
|
n = ngx_send(rec->udp, query, qlen);
|
|
|
|
if (n == NGX_ERROR) {
|
|
goto failed;
|
|
}
|
|
|
|
if ((size_t) n != (size_t) qlen) {
|
|
ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "send() incomplete");
|
|
goto failed;
|
|
}
|
|
|
|
return NGX_OK;
|
|
|
|
failed:
|
|
|
|
ngx_close_connection(rec->udp);
|
|
rec->udp = NULL;
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_send_tcp_query(ngx_resolver_t *r, ngx_resolver_connection_t *rec,
|
|
u_char *query, u_short qlen)
|
|
{
|
|
ngx_buf_t *b;
|
|
ngx_int_t rc;
|
|
|
|
rc = NGX_OK;
|
|
|
|
if (rec->tcp == NULL) {
|
|
b = rec->read_buf;
|
|
|
|
if (b == NULL) {
|
|
b = ngx_resolver_calloc(r, sizeof(ngx_buf_t));
|
|
if (b == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
b->start = ngx_resolver_alloc(r, NGX_RESOLVER_TCP_RSIZE);
|
|
if (b->start == NULL) {
|
|
ngx_resolver_free(r, b);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
b->end = b->start + NGX_RESOLVER_TCP_RSIZE;
|
|
|
|
rec->read_buf = b;
|
|
}
|
|
|
|
b->pos = b->start;
|
|
b->last = b->start;
|
|
|
|
b = rec->write_buf;
|
|
|
|
if (b == NULL) {
|
|
b = ngx_resolver_calloc(r, sizeof(ngx_buf_t));
|
|
if (b == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
b->start = ngx_resolver_alloc(r, NGX_RESOLVER_TCP_WSIZE);
|
|
if (b->start == NULL) {
|
|
ngx_resolver_free(r, b);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
b->end = b->start + NGX_RESOLVER_TCP_WSIZE;
|
|
|
|
rec->write_buf = b;
|
|
}
|
|
|
|
b->pos = b->start;
|
|
b->last = b->start;
|
|
|
|
rc = ngx_tcp_connect(rec);
|
|
if (rc == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rec->tcp->data = rec;
|
|
rec->tcp->write->handler = ngx_resolver_tcp_write;
|
|
rec->tcp->write->cancelable = 1;
|
|
rec->tcp->read->handler = ngx_resolver_tcp_read;
|
|
rec->tcp->read->resolver = 1;
|
|
|
|
ngx_add_timer(rec->tcp->write, (ngx_msec_t) (r->tcp_timeout * 1000));
|
|
}
|
|
|
|
b = rec->write_buf;
|
|
|
|
if (b->end - b->last < 2 + qlen) {
|
|
ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "buffer overflow");
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
*b->last++ = (u_char) (qlen >> 8);
|
|
*b->last++ = (u_char) qlen;
|
|
b->last = ngx_cpymem(b->last, query, qlen);
|
|
|
|
if (rc == NGX_OK) {
|
|
ngx_resolver_tcp_write(rec->tcp->write);
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_resend_handler(ngx_event_t *ev)
|
|
{
|
|
time_t timer, atimer, stimer, ntimer;
|
|
#if (NGX_HAVE_INET6)
|
|
time_t a6timer;
|
|
#endif
|
|
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);
|
|
|
|
stimer = ngx_resolver_resend(r, &r->srv_rbtree, &r->srv_resend_queue);
|
|
|
|
/* unlock name mutex */
|
|
|
|
/* lock addr mutex */
|
|
|
|
atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue);
|
|
|
|
/* unlock addr mutex */
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
/* lock addr6 mutex */
|
|
|
|
a6timer = ngx_resolver_resend(r, &r->addr6_rbtree, &r->addr6_resend_queue);
|
|
|
|
/* unlock addr6 mutex */
|
|
|
|
#endif
|
|
|
|
timer = ntimer;
|
|
|
|
if (timer == 0) {
|
|
timer = atimer;
|
|
|
|
} else if (atimer) {
|
|
timer = ngx_min(timer, atimer);
|
|
}
|
|
|
|
if (timer == 0) {
|
|
timer = stimer;
|
|
|
|
} else if (stimer) {
|
|
timer = ngx_min(timer, stimer);
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
if (timer == 0) {
|
|
timer = a6timer;
|
|
|
|
} else if (a6timer) {
|
|
timer = ngx_min(timer, a6timer);
|
|
}
|
|
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolver resend \"%*s\" %p",
|
|
(size_t) rn->nlen, rn->name, rn->waiting);
|
|
|
|
ngx_queue_remove(q);
|
|
|
|
if (rn->waiting) {
|
|
|
|
if (++rn->last_connection == r->connections.nelts) {
|
|
rn->last_connection = 0;
|
|
}
|
|
|
|
(void) ngx_resolver_send_query(r, rn);
|
|
|
|
rn->expire = now + r->resend_timeout;
|
|
|
|
ngx_queue_insert_head(queue, q);
|
|
|
|
continue;
|
|
}
|
|
|
|
ngx_rbtree_delete(tree, &rn->node);
|
|
|
|
ngx_resolver_free_node(r, rn);
|
|
}
|
|
}
|
|
|
|
|
|
static ngx_uint_t
|
|
ngx_resolver_resend_empty(ngx_resolver_t *r)
|
|
{
|
|
return ngx_queue_empty(&r->name_resend_queue)
|
|
&& ngx_queue_empty(&r->srv_resend_queue)
|
|
#if (NGX_HAVE_INET6)
|
|
&& ngx_queue_empty(&r->addr6_resend_queue)
|
|
#endif
|
|
&& ngx_queue_empty(&r->addr_resend_queue);
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_udp_read(ngx_event_t *rev)
|
|
{
|
|
ssize_t n;
|
|
ngx_connection_t *c;
|
|
ngx_resolver_connection_t *rec;
|
|
u_char buf[NGX_RESOLVER_UDP_SIZE];
|
|
|
|
c = rev->data;
|
|
rec = c->data;
|
|
|
|
do {
|
|
n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE);
|
|
|
|
if (n == NGX_AGAIN) {
|
|
break;
|
|
}
|
|
|
|
if (n == NGX_ERROR) {
|
|
goto failed;
|
|
}
|
|
|
|
ngx_resolver_process_response(rec->resolver, buf, n, 0);
|
|
|
|
} while (rev->ready);
|
|
|
|
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
return;
|
|
|
|
failed:
|
|
|
|
ngx_close_connection(rec->udp);
|
|
rec->udp = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_tcp_write(ngx_event_t *wev)
|
|
{
|
|
off_t sent;
|
|
ssize_t n;
|
|
ngx_buf_t *b;
|
|
ngx_resolver_t *r;
|
|
ngx_connection_t *c;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
c = wev->data;
|
|
rec = c->data;
|
|
b = rec->write_buf;
|
|
r = rec->resolver;
|
|
|
|
if (wev->timedout) {
|
|
goto failed;
|
|
}
|
|
|
|
sent = c->sent;
|
|
|
|
while (wev->ready && b->pos < b->last) {
|
|
n = ngx_send(c, b->pos, b->last - b->pos);
|
|
|
|
if (n == NGX_AGAIN) {
|
|
break;
|
|
}
|
|
|
|
if (n == NGX_ERROR) {
|
|
goto failed;
|
|
}
|
|
|
|
b->pos += n;
|
|
}
|
|
|
|
if (b->pos != b->start) {
|
|
b->last = ngx_movemem(b->start, b->pos, b->last - b->pos);
|
|
b->pos = b->start;
|
|
}
|
|
|
|
if (c->sent != sent) {
|
|
ngx_add_timer(wev, (ngx_msec_t) (r->tcp_timeout * 1000));
|
|
}
|
|
|
|
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
return;
|
|
|
|
failed:
|
|
|
|
ngx_close_connection(c);
|
|
rec->tcp = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_tcp_read(ngx_event_t *rev)
|
|
{
|
|
u_char *p;
|
|
size_t size;
|
|
ssize_t n;
|
|
u_short qlen;
|
|
ngx_buf_t *b;
|
|
ngx_resolver_t *r;
|
|
ngx_connection_t *c;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
c = rev->data;
|
|
rec = c->data;
|
|
b = rec->read_buf;
|
|
r = rec->resolver;
|
|
|
|
while (rev->ready) {
|
|
n = ngx_recv(c, b->last, b->end - b->last);
|
|
|
|
if (n == NGX_AGAIN) {
|
|
break;
|
|
}
|
|
|
|
if (n == NGX_ERROR || n == 0) {
|
|
goto failed;
|
|
}
|
|
|
|
b->last += n;
|
|
|
|
for ( ;; ) {
|
|
p = b->pos;
|
|
size = b->last - p;
|
|
|
|
if (size < 2) {
|
|
break;
|
|
}
|
|
|
|
qlen = (u_short) *p++ << 8;
|
|
qlen += *p++;
|
|
|
|
if (size < (size_t) (2 + qlen)) {
|
|
break;
|
|
}
|
|
|
|
ngx_resolver_process_response(r, p, qlen, 1);
|
|
|
|
b->pos += 2 + qlen;
|
|
}
|
|
|
|
if (b->pos != b->start) {
|
|
b->last = ngx_movemem(b->start, b->pos, b->last - b->pos);
|
|
b->pos = b->start;
|
|
}
|
|
}
|
|
|
|
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
return;
|
|
|
|
failed:
|
|
|
|
ngx_close_connection(c);
|
|
rec->tcp = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n,
|
|
ngx_uint_t tcp)
|
|
{
|
|
char *err;
|
|
ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, trunc,
|
|
qtype, qclass;
|
|
#if (NGX_HAVE_INET6)
|
|
ngx_uint_t qident6;
|
|
#endif
|
|
ngx_queue_t *q;
|
|
ngx_resolver_qs_t *qs;
|
|
ngx_resolver_hdr_t *response;
|
|
ngx_resolver_node_t *rn;
|
|
|
|
if (n < sizeof(ngx_resolver_hdr_t)) {
|
|
goto short_response;
|
|
}
|
|
|
|
response = (ngx_resolver_hdr_t *) buf;
|
|
|
|
ident = (response->ident_hi << 8) + response->ident_lo;
|
|
flags = (response->flags_hi << 8) + response->flags_lo;
|
|
nqs = (response->nqs_hi << 8) + response->nqs_lo;
|
|
nan = (response->nan_hi << 8) + response->nan_lo;
|
|
trunc = flags & 0x0200;
|
|
|
|
ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolver DNS response %ui fl:%04Xi %ui/%ui/%ud/%ud",
|
|
ident, flags, nqs, nan,
|
|
(response->nns_hi << 8) + response->nns_lo,
|
|
(response->nar_hi << 8) + response->nar_lo);
|
|
|
|
/* response to a standard query */
|
|
if ((flags & 0xf870) != 0x8000 || (trunc && tcp)) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"invalid %s DNS response %ui fl:%04Xi",
|
|
tcp ? "TCP" : "UDP", ident, flags);
|
|
return;
|
|
}
|
|
|
|
code = flags & 0xf;
|
|
|
|
if (code == NGX_RESOLVE_FORMERR) {
|
|
|
|
times = 0;
|
|
|
|
for (q = ngx_queue_head(&r->name_resend_queue);
|
|
q != ngx_queue_sentinel(&r->name_resend_queue) && times++ < 100;
|
|
q = ngx_queue_next(q))
|
|
{
|
|
rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
|
|
qident = (rn->query[0] << 8) + rn->query[1];
|
|
|
|
if (qident == ident) {
|
|
goto dns_error_name;
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (rn->query6) {
|
|
qident6 = (rn->query6[0] << 8) + rn->query6[1];
|
|
|
|
if (qident6 == ident) {
|
|
goto dns_error_name;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
goto dns_error;
|
|
}
|
|
|
|
if (code > NGX_RESOLVE_REFUSED) {
|
|
goto dns_error;
|
|
}
|
|
|
|
if (nqs != 1) {
|
|
err = "invalid number of questions in DNS response";
|
|
goto done;
|
|
}
|
|
|
|
i = sizeof(ngx_resolver_hdr_t);
|
|
|
|
while (i < (ngx_uint_t) n) {
|
|
|
|
if (buf[i] & 0xc0) {
|
|
err = "unexpected compression pointer in DNS response";
|
|
goto done;
|
|
}
|
|
|
|
if (buf[i] == '\0') {
|
|
goto found;
|
|
}
|
|
|
|
i += 1 + buf[i];
|
|
}
|
|
|
|
goto short_response;
|
|
|
|
found:
|
|
|
|
if (i++ == sizeof(ngx_resolver_hdr_t)) {
|
|
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:%ui cl:%ui", qtype, qclass);
|
|
|
|
if (qclass != 1) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unknown query class %ui in DNS response", qclass);
|
|
return;
|
|
}
|
|
|
|
switch (qtype) {
|
|
|
|
case NGX_RESOLVE_A:
|
|
#if (NGX_HAVE_INET6)
|
|
case NGX_RESOLVE_AAAA:
|
|
#endif
|
|
|
|
ngx_resolver_process_a(r, buf, n, ident, code, qtype, nan, trunc,
|
|
i + sizeof(ngx_resolver_qs_t));
|
|
|
|
break;
|
|
|
|
case NGX_RESOLVE_SRV:
|
|
|
|
ngx_resolver_process_srv(r, buf, n, ident, code, nan, trunc,
|
|
i + sizeof(ngx_resolver_qs_t));
|
|
|
|
break;
|
|
|
|
case NGX_RESOLVE_PTR:
|
|
|
|
ngx_resolver_process_ptr(r, buf, n, ident, code, nan);
|
|
|
|
break;
|
|
|
|
default:
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unknown query type %ui in DNS response", qtype);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
short_response:
|
|
|
|
err = "short DNS response";
|
|
|
|
done:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0, err);
|
|
|
|
return;
|
|
|
|
dns_error_name:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"DNS error (%ui: %s), query id:%ui, name:\"%*s\"",
|
|
code, ngx_resolver_strerror(code), ident,
|
|
(size_t) rn->nlen, rn->name);
|
|
return;
|
|
|
|
dns_error:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"DNS error (%ui: %s), query id:%ui",
|
|
code, ngx_resolver_strerror(code), ident);
|
|
return;
|
|
}
|
|
|
|
|
|
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 qtype,
|
|
ngx_uint_t nan, ngx_uint_t trunc, ngx_uint_t ans)
|
|
{
|
|
char *err;
|
|
u_char *cname;
|
|
size_t len;
|
|
int32_t ttl;
|
|
uint32_t hash;
|
|
in_addr_t *addr;
|
|
ngx_str_t name;
|
|
ngx_uint_t type, class, qident, naddrs, a, i, j, start;
|
|
#if (NGX_HAVE_INET6)
|
|
struct in6_addr *addr6;
|
|
#endif
|
|
ngx_resolver_an_t *an;
|
|
ngx_resolver_ctx_t *ctx, *next;
|
|
ngx_resolver_node_t *rn;
|
|
ngx_resolver_addr_t *addrs;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
if (ngx_resolver_copy(r, &name, buf,
|
|
buf + sizeof(ngx_resolver_hdr_t), buf + n)
|
|
!= 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) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected DNS response for %V", &name);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
switch (qtype) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected DNS response for %V", &name);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
if (trunc && rn->tcp6) {
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
qident = (rn->query6[0] << 8) + rn->query6[1];
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* NGX_RESOLVE_A */
|
|
|
|
if (rn->query == NULL || rn->naddrs != (u_short) -1) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected DNS response for %V", &name);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
if (trunc && rn->tcp) {
|
|
ngx_resolver_free(r, name.data);
|
|
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 in DNS response for %V, expect %ui",
|
|
ident, &name, qident);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
ngx_resolver_free(r, name.data);
|
|
|
|
if (trunc) {
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
if (rn->waiting == NULL) {
|
|
ngx_rbtree_delete(&r->name_rbtree, &rn->node);
|
|
ngx_resolver_free_node(r, rn);
|
|
goto next;
|
|
}
|
|
|
|
rec = r->connections.elts;
|
|
rec = &rec[rn->last_connection];
|
|
|
|
switch (qtype) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
rn->tcp6 = 1;
|
|
|
|
(void) ngx_resolver_send_tcp_query(r, rec, rn->query6, rn->qlen);
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* NGX_RESOLVE_A */
|
|
|
|
rn->tcp = 1;
|
|
|
|
(void) ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen);
|
|
}
|
|
|
|
rn->expire = ngx_time() + r->resend_timeout;
|
|
|
|
ngx_queue_insert_head(&r->name_resend_queue, &rn->queue);
|
|
|
|
goto next;
|
|
}
|
|
|
|
if (code == 0 && rn->code) {
|
|
code = rn->code;
|
|
}
|
|
|
|
if (code == 0 && nan == 0) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
switch (qtype) {
|
|
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
rn->naddrs6 = 0;
|
|
|
|
if (rn->naddrs == (u_short) -1) {
|
|
goto next;
|
|
}
|
|
|
|
if (rn->naddrs) {
|
|
goto export;
|
|
}
|
|
|
|
break;
|
|
|
|
default: /* NGX_RESOLVE_A */
|
|
|
|
rn->naddrs = 0;
|
|
|
|
if (rn->naddrs6 == (u_short) -1) {
|
|
goto next;
|
|
}
|
|
|
|
if (rn->naddrs6) {
|
|
goto export;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
code = NGX_RESOLVE_NXDOMAIN;
|
|
}
|
|
|
|
if (code) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
switch (qtype) {
|
|
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
rn->naddrs6 = 0;
|
|
|
|
if (rn->naddrs == (u_short) -1) {
|
|
rn->code = (u_char) code;
|
|
goto next;
|
|
}
|
|
|
|
break;
|
|
|
|
default: /* NGX_RESOLVE_A */
|
|
|
|
rn->naddrs = 0;
|
|
|
|
if (rn->naddrs6 == (u_short) -1) {
|
|
rn->code = (u_char) code;
|
|
goto next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
ngx_rbtree_delete(&r->name_rbtree, &rn->node);
|
|
|
|
/* unlock name mutex */
|
|
|
|
while (next) {
|
|
ctx = next;
|
|
ctx->state = code;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
}
|
|
|
|
ngx_resolver_free_node(r, rn);
|
|
|
|
return;
|
|
}
|
|
|
|
i = ans;
|
|
naddrs = 0;
|
|
cname = NULL;
|
|
|
|
for (a = 0; a < nan; a++) {
|
|
|
|
start = i;
|
|
|
|
while (i < n) {
|
|
|
|
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) >= n) {
|
|
goto short_response;
|
|
}
|
|
|
|
an = (ngx_resolver_an_t *) &buf[i];
|
|
|
|
type = (an->type_hi << 8) + an->type_lo;
|
|
class = (an->class_hi << 8) + an->class_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 (class != 1) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected RR class %ui in DNS response", class);
|
|
goto failed;
|
|
}
|
|
|
|
if (ttl < 0) {
|
|
ttl = 0;
|
|
}
|
|
|
|
rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl);
|
|
|
|
i += sizeof(ngx_resolver_an_t);
|
|
|
|
switch (type) {
|
|
|
|
case NGX_RESOLVE_A:
|
|
|
|
if (qtype != NGX_RESOLVE_A) {
|
|
err = "unexpected A record in DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
if (len != 4) {
|
|
err = "invalid A record in DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
if (i + 4 > n) {
|
|
goto short_response;
|
|
}
|
|
|
|
naddrs++;
|
|
|
|
break;
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
if (qtype != NGX_RESOLVE_AAAA) {
|
|
err = "unexpected AAAA record in DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
if (len != 16) {
|
|
err = "invalid AAAA record in DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
if (i + 16 > n) {
|
|
goto short_response;
|
|
}
|
|
|
|
naddrs++;
|
|
|
|
break;
|
|
#endif
|
|
|
|
case NGX_RESOLVE_CNAME:
|
|
|
|
cname = &buf[i];
|
|
|
|
break;
|
|
|
|
case NGX_RESOLVE_DNAME:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected RR type %ui in DNS response", type);
|
|
}
|
|
|
|
i += len;
|
|
}
|
|
|
|
ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolver naddrs:%ui cname:%p ttl:%uD",
|
|
naddrs, cname, rn->ttl);
|
|
|
|
if (naddrs) {
|
|
|
|
switch (qtype) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
if (naddrs == 1) {
|
|
addr6 = &rn->u6.addr6;
|
|
rn->naddrs6 = 1;
|
|
|
|
} else {
|
|
addr6 = ngx_resolver_alloc(r, naddrs * sizeof(struct in6_addr));
|
|
if (addr6 == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
rn->u6.addrs6 = addr6;
|
|
rn->naddrs6 = (u_short) naddrs;
|
|
}
|
|
|
|
#if (NGX_SUPPRESS_WARN)
|
|
addr = NULL;
|
|
#endif
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* NGX_RESOLVE_A */
|
|
|
|
if (naddrs == 1) {
|
|
addr = &rn->u.addr;
|
|
rn->naddrs = 1;
|
|
|
|
} else {
|
|
addr = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));
|
|
if (addr == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
rn->u.addrs = addr;
|
|
rn->naddrs = (u_short) naddrs;
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6 && NGX_SUPPRESS_WARN)
|
|
addr6 = NULL;
|
|
#endif
|
|
}
|
|
|
|
j = 0;
|
|
i = ans;
|
|
|
|
for (a = 0; a < nan; a++) {
|
|
|
|
for ( ;; ) {
|
|
|
|
if (buf[i] & 0xc0) {
|
|
i += 2;
|
|
break;
|
|
}
|
|
|
|
if (buf[i] == 0) {
|
|
i++;
|
|
break;
|
|
}
|
|
|
|
i += 1 + buf[i];
|
|
}
|
|
|
|
an = (ngx_resolver_an_t *) &buf[i];
|
|
|
|
type = (an->type_hi << 8) + an->type_lo;
|
|
len = (an->len_hi << 8) + an->len_lo;
|
|
|
|
i += sizeof(ngx_resolver_an_t);
|
|
|
|
if (type == NGX_RESOLVE_A) {
|
|
|
|
addr[j] = htonl((buf[i] << 24) + (buf[i + 1] << 16)
|
|
+ (buf[i + 2] << 8) + (buf[i + 3]));
|
|
|
|
if (++j == naddrs) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (rn->naddrs6 == (u_short) -1) {
|
|
goto next;
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
else if (type == NGX_RESOLVE_AAAA) {
|
|
|
|
ngx_memcpy(addr6[j].s6_addr, &buf[i], 16);
|
|
|
|
if (++j == naddrs) {
|
|
|
|
if (rn->naddrs == (u_short) -1) {
|
|
goto next;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
i += len;
|
|
}
|
|
}
|
|
|
|
switch (qtype) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case NGX_RESOLVE_AAAA:
|
|
|
|
if (rn->naddrs6 == (u_short) -1) {
|
|
rn->naddrs6 = 0;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* NGX_RESOLVE_A */
|
|
|
|
if (rn->naddrs == (u_short) -1) {
|
|
rn->naddrs = 0;
|
|
}
|
|
}
|
|
|
|
if (rn->naddrs != (u_short) -1
|
|
#if (NGX_HAVE_INET6)
|
|
&& rn->naddrs6 != (u_short) -1
|
|
#endif
|
|
&& rn->naddrs
|
|
#if (NGX_HAVE_INET6)
|
|
+ rn->naddrs6
|
|
#endif
|
|
> 0)
|
|
{
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
export:
|
|
#endif
|
|
|
|
naddrs = rn->naddrs;
|
|
#if (NGX_HAVE_INET6)
|
|
naddrs += rn->naddrs6;
|
|
#endif
|
|
|
|
if (naddrs == 1 && rn->naddrs == 1) {
|
|
addrs = NULL;
|
|
|
|
} else {
|
|
addrs = ngx_resolver_export(r, rn, 0);
|
|
if (addrs == NULL) {
|
|
goto failed;
|
|
}
|
|
}
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->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->valid = rn->valid;
|
|
ctx->naddrs = naddrs;
|
|
|
|
if (addrs == NULL) {
|
|
ctx->addrs = &ctx->addr;
|
|
ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin;
|
|
ctx->addr.socklen = sizeof(struct sockaddr_in);
|
|
ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in));
|
|
ctx->sin.sin_family = AF_INET;
|
|
ctx->sin.sin_addr.s_addr = rn->u.addr;
|
|
|
|
} else {
|
|
ctx->addrs = addrs;
|
|
}
|
|
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
}
|
|
|
|
if (addrs != NULL) {
|
|
ngx_resolver_free(r, addrs->sockaddr);
|
|
ngx_resolver_free(r, addrs);
|
|
}
|
|
|
|
ngx_resolver_free(r, rn->query);
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
if (cname) {
|
|
|
|
/* CNAME only */
|
|
|
|
if (rn->naddrs == (u_short) -1
|
|
#if (NGX_HAVE_INET6)
|
|
|| rn->naddrs6 == (u_short) -1
|
|
#endif
|
|
)
|
|
{
|
|
goto next;
|
|
}
|
|
|
|
if (ngx_resolver_copy(r, &name, buf, cname, buf + n) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
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 : (time_t) rn->ttl);
|
|
rn->expire = ngx_time() + r->expire;
|
|
|
|
ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
|
|
|
|
ngx_resolver_free(r, rn->query);
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
|
|
ctx = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
if (ctx) {
|
|
|
|
if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) {
|
|
|
|
/* unlock name mutex */
|
|
|
|
do {
|
|
ctx->state = NGX_RESOLVE_NXDOMAIN;
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ctx = next;
|
|
} while (ctx);
|
|
|
|
return;
|
|
}
|
|
|
|
for (next = ctx; next; next = next->next) {
|
|
next->node = NULL;
|
|
}
|
|
|
|
(void) ngx_resolve_name_locked(r, ctx, &name);
|
|
}
|
|
|
|
/* unlock name mutex */
|
|
|
|
return;
|
|
}
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"no A or CNAME types in DNS response");
|
|
return;
|
|
|
|
short_response:
|
|
|
|
err = "short DNS response";
|
|
|
|
invalid:
|
|
|
|
/* unlock name mutex */
|
|
|
|
ngx_log_error(r->log_level, r->log, 0, err);
|
|
|
|
return;
|
|
|
|
failed:
|
|
|
|
next:
|
|
|
|
/* unlock name mutex */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_process_srv(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 trunc, ngx_uint_t ans)
|
|
{
|
|
char *err;
|
|
u_char *cname;
|
|
size_t len;
|
|
int32_t ttl;
|
|
uint32_t hash;
|
|
ngx_str_t name;
|
|
ngx_uint_t type, qident, class, start, nsrvs, a, i, j;
|
|
ngx_resolver_an_t *an;
|
|
ngx_resolver_ctx_t *ctx, *next;
|
|
ngx_resolver_srv_t *srvs;
|
|
ngx_resolver_node_t *rn;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
if (ngx_resolver_copy(r, &name, buf,
|
|
buf + sizeof(ngx_resolver_hdr_t), buf + n)
|
|
!= 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);
|
|
|
|
rn = ngx_resolver_lookup_srv(r, &name, hash);
|
|
|
|
if (rn == NULL || rn->query == NULL) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected DNS response for %V", &name);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
if (trunc && rn->tcp) {
|
|
ngx_resolver_free(r, name.data);
|
|
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 in DNS response for %V, expect %ui",
|
|
ident, &name, qident);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
ngx_resolver_free(r, name.data);
|
|
|
|
if (trunc) {
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
if (rn->waiting == NULL) {
|
|
ngx_rbtree_delete(&r->srv_rbtree, &rn->node);
|
|
ngx_resolver_free_node(r, rn);
|
|
return;
|
|
}
|
|
|
|
rec = r->connections.elts;
|
|
rec = &rec[rn->last_connection];
|
|
|
|
rn->tcp = 1;
|
|
|
|
(void) ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen);
|
|
|
|
rn->expire = ngx_time() + r->resend_timeout;
|
|
|
|
ngx_queue_insert_head(&r->srv_resend_queue, &rn->queue);
|
|
|
|
return;
|
|
}
|
|
|
|
if (code == 0 && rn->code) {
|
|
code = rn->code;
|
|
}
|
|
|
|
if (code == 0 && nan == 0) {
|
|
code = NGX_RESOLVE_NXDOMAIN;
|
|
}
|
|
|
|
if (code) {
|
|
next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
ngx_rbtree_delete(&r->srv_rbtree, &rn->node);
|
|
|
|
while (next) {
|
|
ctx = next;
|
|
ctx->state = code;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
}
|
|
|
|
ngx_resolver_free_node(r, rn);
|
|
|
|
return;
|
|
}
|
|
|
|
i = ans;
|
|
nsrvs = 0;
|
|
cname = NULL;
|
|
|
|
for (a = 0; a < nan; a++) {
|
|
|
|
start = i;
|
|
|
|
while (i < n) {
|
|
|
|
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 DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
found:
|
|
|
|
if (i + sizeof(ngx_resolver_an_t) >= n) {
|
|
goto short_response;
|
|
}
|
|
|
|
an = (ngx_resolver_an_t *) &buf[i];
|
|
|
|
type = (an->type_hi << 8) + an->type_lo;
|
|
class = (an->class_hi << 8) + an->class_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 (class != 1) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected RR class %ui in DNS response", class);
|
|
goto failed;
|
|
}
|
|
|
|
if (ttl < 0) {
|
|
ttl = 0;
|
|
}
|
|
|
|
rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl);
|
|
|
|
i += sizeof(ngx_resolver_an_t);
|
|
|
|
switch (type) {
|
|
|
|
case NGX_RESOLVE_SRV:
|
|
|
|
if (i + 6 > n) {
|
|
goto short_response;
|
|
}
|
|
|
|
if (ngx_resolver_copy(r, NULL, buf, &buf[i + 6], buf + n)
|
|
!= NGX_OK)
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
nsrvs++;
|
|
|
|
break;
|
|
|
|
case NGX_RESOLVE_CNAME:
|
|
|
|
cname = &buf[i];
|
|
|
|
break;
|
|
|
|
case NGX_RESOLVE_DNAME:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected RR type %ui in DNS response", type);
|
|
}
|
|
|
|
i += len;
|
|
}
|
|
|
|
ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolver nsrvs:%ui cname:%p ttl:%uD",
|
|
nsrvs, cname, rn->ttl);
|
|
|
|
if (nsrvs) {
|
|
|
|
srvs = ngx_resolver_calloc(r, nsrvs * sizeof(ngx_resolver_srv_t));
|
|
if (srvs == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
rn->u.srvs = srvs;
|
|
rn->nsrvs = (u_short) nsrvs;
|
|
|
|
j = 0;
|
|
i = ans;
|
|
|
|
for (a = 0; a < nan; a++) {
|
|
|
|
for ( ;; ) {
|
|
|
|
if (buf[i] & 0xc0) {
|
|
i += 2;
|
|
break;
|
|
}
|
|
|
|
if (buf[i] == 0) {
|
|
i++;
|
|
break;
|
|
}
|
|
|
|
i += 1 + buf[i];
|
|
}
|
|
|
|
an = (ngx_resolver_an_t *) &buf[i];
|
|
|
|
type = (an->type_hi << 8) + an->type_lo;
|
|
len = (an->len_hi << 8) + an->len_lo;
|
|
|
|
i += sizeof(ngx_resolver_an_t);
|
|
|
|
if (type == NGX_RESOLVE_SRV) {
|
|
|
|
srvs[j].priority = (buf[i] << 8) + buf[i + 1];
|
|
srvs[j].weight = (buf[i + 2] << 8) + buf[i + 3];
|
|
|
|
if (srvs[j].weight == 0) {
|
|
srvs[j].weight = 1;
|
|
}
|
|
|
|
srvs[j].port = (buf[i + 4] << 8) + buf[i + 5];
|
|
|
|
if (ngx_resolver_copy(r, &srvs[j].name, buf, &buf[i + 6],
|
|
buf + n)
|
|
!= NGX_OK)
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
i += len;
|
|
}
|
|
|
|
ngx_sort(srvs, nsrvs, sizeof(ngx_resolver_srv_t),
|
|
ngx_resolver_cmp_srvs);
|
|
|
|
ngx_resolver_free(r, rn->query);
|
|
rn->query = NULL;
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);
|
|
rn->expire = ngx_time() + r->expire;
|
|
|
|
ngx_queue_insert_head(&r->srv_expire_queue, &rn->queue);
|
|
|
|
next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
while (next) {
|
|
ctx = next;
|
|
next = ctx->next;
|
|
|
|
ngx_resolver_resolve_srv_names(ctx, rn);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
rn->nsrvs = 0;
|
|
|
|
if (cname) {
|
|
|
|
/* CNAME only */
|
|
|
|
if (ngx_resolver_copy(r, &name, buf, cname, buf + n) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
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 : (time_t) rn->ttl);
|
|
rn->expire = ngx_time() + r->expire;
|
|
|
|
ngx_queue_insert_head(&r->srv_expire_queue, &rn->queue);
|
|
|
|
ngx_resolver_free(r, rn->query);
|
|
rn->query = NULL;
|
|
#if (NGX_HAVE_INET6)
|
|
rn->query6 = NULL;
|
|
#endif
|
|
|
|
ctx = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
if (ctx) {
|
|
|
|
if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) {
|
|
|
|
/* unlock name mutex */
|
|
|
|
do {
|
|
ctx->state = NGX_RESOLVE_NXDOMAIN;
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ctx = next;
|
|
} while (ctx);
|
|
|
|
return;
|
|
}
|
|
|
|
for (next = ctx; next; next = next->next) {
|
|
next->node = NULL;
|
|
}
|
|
|
|
(void) ngx_resolve_name_locked(r, ctx, &name);
|
|
}
|
|
|
|
/* unlock name mutex */
|
|
|
|
return;
|
|
}
|
|
|
|
ngx_log_error(r->log_level, r->log, 0, "no SRV type in DNS response");
|
|
|
|
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_resolve_srv_names(ngx_resolver_ctx_t *ctx, ngx_resolver_node_t *rn)
|
|
{
|
|
ngx_uint_t i;
|
|
ngx_resolver_t *r;
|
|
ngx_resolver_ctx_t *cctx;
|
|
ngx_resolver_srv_name_t *srvs;
|
|
|
|
r = ctx->resolver;
|
|
|
|
ctx->node = NULL;
|
|
ctx->state = NGX_OK;
|
|
ctx->valid = rn->valid;
|
|
ctx->count = rn->nsrvs;
|
|
|
|
srvs = ngx_resolver_calloc(r, rn->nsrvs * sizeof(ngx_resolver_srv_name_t));
|
|
if (srvs == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
ctx->srvs = srvs;
|
|
ctx->nsrvs = rn->nsrvs;
|
|
|
|
if (ctx->event && ctx->event->timer_set) {
|
|
ngx_del_timer(ctx->event);
|
|
}
|
|
|
|
for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {
|
|
srvs[i].name.data = ngx_resolver_alloc(r, rn->u.srvs[i].name.len);
|
|
if (srvs[i].name.data == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
srvs[i].name.len = rn->u.srvs[i].name.len;
|
|
ngx_memcpy(srvs[i].name.data, rn->u.srvs[i].name.data,
|
|
srvs[i].name.len);
|
|
|
|
cctx = ngx_resolve_start(r, NULL);
|
|
if (cctx == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
cctx->name = srvs[i].name;
|
|
cctx->handler = ngx_resolver_srv_names_handler;
|
|
cctx->data = ctx;
|
|
cctx->srvs = &srvs[i];
|
|
cctx->timeout = ctx->timeout;
|
|
|
|
srvs[i].priority = rn->u.srvs[i].priority;
|
|
srvs[i].weight = rn->u.srvs[i].weight;
|
|
srvs[i].port = rn->u.srvs[i].port;
|
|
srvs[i].ctx = cctx;
|
|
|
|
if (ngx_resolve_name(cctx) == NGX_ERROR) {
|
|
srvs[i].ctx = NULL;
|
|
goto failed;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
failed:
|
|
|
|
ctx->state = NGX_ERROR;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
|
|
ctx->handler(ctx);
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx)
|
|
{
|
|
ngx_uint_t i;
|
|
ngx_addr_t *addrs;
|
|
ngx_resolver_t *r;
|
|
ngx_sockaddr_t *sockaddr;
|
|
ngx_resolver_ctx_t *ctx;
|
|
ngx_resolver_srv_name_t *srv;
|
|
|
|
r = cctx->resolver;
|
|
ctx = cctx->data;
|
|
srv = cctx->srvs;
|
|
|
|
ctx->count--;
|
|
ctx->async |= cctx->async;
|
|
|
|
srv->ctx = NULL;
|
|
srv->state = cctx->state;
|
|
|
|
if (cctx->naddrs) {
|
|
|
|
ctx->valid = ngx_min(ctx->valid, cctx->valid);
|
|
|
|
addrs = ngx_resolver_calloc(r, cctx->naddrs * sizeof(ngx_addr_t));
|
|
if (addrs == NULL) {
|
|
srv->state = NGX_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
sockaddr = ngx_resolver_alloc(r, cctx->naddrs * sizeof(ngx_sockaddr_t));
|
|
if (sockaddr == NULL) {
|
|
ngx_resolver_free(r, addrs);
|
|
srv->state = NGX_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < cctx->naddrs; i++) {
|
|
addrs[i].sockaddr = &sockaddr[i].sockaddr;
|
|
addrs[i].socklen = cctx->addrs[i].socklen;
|
|
|
|
ngx_memcpy(&sockaddr[i], cctx->addrs[i].sockaddr,
|
|
addrs[i].socklen);
|
|
|
|
ngx_inet_set_port(addrs[i].sockaddr, srv->port);
|
|
}
|
|
|
|
srv->addrs = addrs;
|
|
srv->naddrs = cctx->naddrs;
|
|
}
|
|
|
|
done:
|
|
|
|
ngx_resolve_name_done(cctx);
|
|
|
|
if (ctx->count == 0) {
|
|
ngx_resolver_report_srv(r, ctx);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
|
|
ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)
|
|
{
|
|
char *err;
|
|
size_t len;
|
|
in_addr_t addr;
|
|
int32_t ttl;
|
|
ngx_int_t octet;
|
|
ngx_str_t name;
|
|
ngx_uint_t mask, type, class, qident, a, i, start;
|
|
ngx_queue_t *expire_queue;
|
|
ngx_rbtree_t *tree;
|
|
ngx_resolver_an_t *an;
|
|
ngx_resolver_ctx_t *ctx, *next;
|
|
ngx_resolver_node_t *rn;
|
|
#if (NGX_HAVE_INET6)
|
|
uint32_t hash;
|
|
ngx_int_t digit;
|
|
struct in6_addr addr6;
|
|
#endif
|
|
|
|
if (ngx_resolver_copy(r, &name, buf,
|
|
buf + sizeof(ngx_resolver_hdr_t), buf + n)
|
|
!= NGX_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name);
|
|
|
|
/* AF_INET */
|
|
|
|
addr = 0;
|
|
i = sizeof(ngx_resolver_hdr_t);
|
|
|
|
for (mask = 0; mask < 32; mask += 8) {
|
|
len = buf[i++];
|
|
|
|
octet = ngx_atoi(&buf[i], len);
|
|
if (octet == NGX_ERROR || octet > 255) {
|
|
goto invalid_in_addr_arpa;
|
|
}
|
|
|
|
addr += octet << mask;
|
|
i += len;
|
|
}
|
|
|
|
if (ngx_strcasecmp(&buf[i], (u_char *) "\7in-addr\4arpa") == 0) {
|
|
i += sizeof("\7in-addr\4arpa");
|
|
|
|
/* lock addr mutex */
|
|
|
|
rn = ngx_resolver_lookup_addr(r, addr);
|
|
|
|
tree = &r->addr_rbtree;
|
|
expire_queue = &r->addr_expire_queue;
|
|
|
|
goto valid;
|
|
}
|
|
|
|
invalid_in_addr_arpa:
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
i = sizeof(ngx_resolver_hdr_t);
|
|
|
|
for (octet = 15; octet >= 0; octet--) {
|
|
if (buf[i++] != '\1') {
|
|
goto invalid_ip6_arpa;
|
|
}
|
|
|
|
digit = ngx_hextoi(&buf[i++], 1);
|
|
if (digit == NGX_ERROR) {
|
|
goto invalid_ip6_arpa;
|
|
}
|
|
|
|
addr6.s6_addr[octet] = (u_char) digit;
|
|
|
|
if (buf[i++] != '\1') {
|
|
goto invalid_ip6_arpa;
|
|
}
|
|
|
|
digit = ngx_hextoi(&buf[i++], 1);
|
|
if (digit == NGX_ERROR) {
|
|
goto invalid_ip6_arpa;
|
|
}
|
|
|
|
addr6.s6_addr[octet] += (u_char) (digit * 16);
|
|
}
|
|
|
|
if (ngx_strcasecmp(&buf[i], (u_char *) "\3ip6\4arpa") == 0) {
|
|
i += sizeof("\3ip6\4arpa");
|
|
|
|
/* lock addr mutex */
|
|
|
|
hash = ngx_crc32_short(addr6.s6_addr, 16);
|
|
rn = ngx_resolver_lookup_addr6(r, &addr6, hash);
|
|
|
|
tree = &r->addr6_rbtree;
|
|
expire_queue = &r->addr6_expire_queue;
|
|
|
|
goto valid;
|
|
}
|
|
|
|
invalid_ip6_arpa:
|
|
#endif
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"invalid in-addr.arpa or ip6.arpa name in DNS response");
|
|
ngx_resolver_free(r, name.data);
|
|
return;
|
|
|
|
valid:
|
|
|
|
if (rn == NULL || rn->query == NULL) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected DNS response for %V", &name);
|
|
ngx_resolver_free(r, name.data);
|
|
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 in DNS response for %V, expect %ui",
|
|
ident, &name, qident);
|
|
ngx_resolver_free(r, name.data);
|
|
goto failed;
|
|
}
|
|
|
|
ngx_resolver_free(r, name.data);
|
|
|
|
if (code == 0 && nan == 0) {
|
|
code = NGX_RESOLVE_NXDOMAIN;
|
|
}
|
|
|
|
if (code) {
|
|
next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
ngx_rbtree_delete(tree, &rn->node);
|
|
|
|
/* unlock addr mutex */
|
|
|
|
while (next) {
|
|
ctx = next;
|
|
ctx->state = code;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
}
|
|
|
|
ngx_resolver_free_node(r, rn);
|
|
|
|
return;
|
|
}
|
|
|
|
i += sizeof(ngx_resolver_qs_t);
|
|
|
|
for (a = 0; a < nan; a++) {
|
|
|
|
start = i;
|
|
|
|
while (i < n) {
|
|
|
|
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) >= n) {
|
|
goto short_response;
|
|
}
|
|
|
|
an = (ngx_resolver_an_t *) &buf[i];
|
|
|
|
type = (an->type_hi << 8) + an->type_lo;
|
|
class = (an->class_hi << 8) + an->class_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 (class != 1) {
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected RR class %ui in DNS response", class);
|
|
goto failed;
|
|
}
|
|
|
|
if (ttl < 0) {
|
|
ttl = 0;
|
|
}
|
|
|
|
ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolver qt:%ui cl:%ui len:%uz",
|
|
type, class, len);
|
|
|
|
i += sizeof(ngx_resolver_an_t);
|
|
|
|
switch (type) {
|
|
|
|
case NGX_RESOLVE_PTR:
|
|
|
|
goto ptr;
|
|
|
|
case NGX_RESOLVE_CNAME:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"unexpected RR type %ui in DNS response", type);
|
|
}
|
|
|
|
i += len;
|
|
}
|
|
|
|
/* unlock addr mutex */
|
|
|
|
ngx_log_error(r->log_level, r->log, 0,
|
|
"no PTR type in DNS response");
|
|
return;
|
|
|
|
ptr:
|
|
|
|
if (ngx_resolver_copy(r, &name, buf, buf + i, buf + n) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name);
|
|
|
|
if (name.len != (size_t) rn->nlen
|
|
|| ngx_strncmp(name.data, rn->name, name.len) != 0)
|
|
{
|
|
if (rn->nlen) {
|
|
ngx_resolver_free(r, rn->name);
|
|
}
|
|
|
|
rn->nlen = (u_short) name.len;
|
|
rn->name = name.data;
|
|
|
|
name.data = ngx_resolver_dup(r, rn->name, name.len);
|
|
if (name.data == NULL) {
|
|
goto failed;
|
|
}
|
|
}
|
|
|
|
ngx_queue_remove(&rn->queue);
|
|
|
|
rn->valid = ngx_time() + (r->valid ? r->valid : ttl);
|
|
rn->expire = ngx_time() + r->expire;
|
|
|
|
ngx_queue_insert_head(expire_queue, &rn->queue);
|
|
|
|
next = rn->waiting;
|
|
rn->waiting = NULL;
|
|
|
|
/* unlock addr mutex */
|
|
|
|
while (next) {
|
|
ctx = next;
|
|
ctx->state = NGX_OK;
|
|
ctx->valid = rn->valid;
|
|
ctx->name = name;
|
|
next = ctx->next;
|
|
|
|
ctx->handler(ctx);
|
|
}
|
|
|
|
ngx_resolver_free(r, name.data);
|
|
|
|
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;
|
|
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 */
|
|
|
|
rn = ngx_resolver_node(node);
|
|
|
|
rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);
|
|
|
|
if (rc == 0) {
|
|
return rn;
|
|
}
|
|
|
|
node = (rc < 0) ? node->left : node->right;
|
|
}
|
|
|
|
/* not found */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static ngx_resolver_node_t *
|
|
ngx_resolver_lookup_srv(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)
|
|
{
|
|
ngx_int_t rc;
|
|
ngx_rbtree_node_t *node, *sentinel;
|
|
ngx_resolver_node_t *rn;
|
|
|
|
node = r->srv_rbtree.root;
|
|
sentinel = r->srv_rbtree.sentinel;
|
|
|
|
while (node != sentinel) {
|
|
|
|
if (hash < node->key) {
|
|
node = node->left;
|
|
continue;
|
|
}
|
|
|
|
if (hash > node->key) {
|
|
node = node->right;
|
|
continue;
|
|
}
|
|
|
|
/* hash == node->key */
|
|
|
|
rn = ngx_resolver_node(node);
|
|
|
|
rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);
|
|
|
|
if (rc == 0) {
|
|
return rn;
|
|
}
|
|
|
|
node = (rc < 0) ? node->left : node->right;
|
|
}
|
|
|
|
/* 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(node);
|
|
}
|
|
|
|
/* not found */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
static ngx_resolver_node_t *
|
|
ngx_resolver_lookup_addr6(ngx_resolver_t *r, struct in6_addr *addr,
|
|
uint32_t hash)
|
|
{
|
|
ngx_int_t rc;
|
|
ngx_rbtree_node_t *node, *sentinel;
|
|
ngx_resolver_node_t *rn;
|
|
|
|
node = r->addr6_rbtree.root;
|
|
sentinel = r->addr6_rbtree.sentinel;
|
|
|
|
while (node != sentinel) {
|
|
|
|
if (hash < node->key) {
|
|
node = node->left;
|
|
continue;
|
|
}
|
|
|
|
if (hash > node->key) {
|
|
node = node->right;
|
|
continue;
|
|
}
|
|
|
|
/* hash == node->key */
|
|
|
|
rn = ngx_resolver_node(node);
|
|
|
|
rc = ngx_memcmp(addr, &rn->addr6, 16);
|
|
|
|
if (rc == 0) {
|
|
return rn;
|
|
}
|
|
|
|
node = (rc < 0) ? node->left : node->right;
|
|
}
|
|
|
|
/* not found */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static void
|
|
ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
|
|
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
|
|
{
|
|
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(node);
|
|
rn_temp = ngx_resolver_node(temp);
|
|
|
|
p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen)
|
|
< 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);
|
|
}
|
|
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
|
|
static void
|
|
ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp,
|
|
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
|
|
{
|
|
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(node);
|
|
rn_temp = ngx_resolver_node(temp);
|
|
|
|
p = (ngx_memcmp(&rn->addr6, &rn_temp->addr6, 16)
|
|
< 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);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_create_name_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,
|
|
ngx_str_t *name)
|
|
{
|
|
u_char *p, *s;
|
|
size_t len, nlen;
|
|
ngx_uint_t ident;
|
|
ngx_resolver_qs_t *qs;
|
|
ngx_resolver_hdr_t *query;
|
|
|
|
nlen = name->len ? (1 + name->len + 1) : 1;
|
|
|
|
len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t);
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
p = ngx_resolver_alloc(r, r->ipv6 ? len * 2 : len);
|
|
#else
|
|
p = ngx_resolver_alloc(r, len);
|
|
#endif
|
|
if (p == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rn->qlen = (u_short) len;
|
|
rn->query = p;
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (r->ipv6) {
|
|
rn->query6 = p + len;
|
|
}
|
|
#endif
|
|
|
|
query = (ngx_resolver_hdr_t *) p;
|
|
|
|
ident = ngx_random();
|
|
|
|
ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolve: \"%V\" A %i", 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_hdr_t) + nlen;
|
|
|
|
qs = (ngx_resolver_qs_t *) p;
|
|
|
|
/* query type */
|
|
qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_A;
|
|
|
|
/* IN query class */
|
|
qs->class_hi = 0; qs->class_lo = 1;
|
|
|
|
/* convert "www.example.com" to "\3www\7example\3com\0" */
|
|
|
|
len = 0;
|
|
p--;
|
|
*p-- = '\0';
|
|
|
|
if (name->len == 0) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
for (s = name->data + name->len - 1; s >= name->data; s--) {
|
|
if (*s != '.') {
|
|
*p = *s;
|
|
len++;
|
|
|
|
} else {
|
|
if (len == 0 || len > 255) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
*p = (u_char) len;
|
|
len = 0;
|
|
}
|
|
|
|
p--;
|
|
}
|
|
|
|
if (len == 0 || len > 255) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
*p = (u_char) len;
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (!r->ipv6) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
p = rn->query6;
|
|
|
|
ngx_memcpy(p, rn->query, rn->qlen);
|
|
|
|
query = (ngx_resolver_hdr_t *) p;
|
|
|
|
ident = ngx_random();
|
|
|
|
ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolve: \"%V\" AAAA %i", name, ident & 0xffff);
|
|
|
|
query->ident_hi = (u_char) ((ident >> 8) & 0xff);
|
|
query->ident_lo = (u_char) (ident & 0xff);
|
|
|
|
p += sizeof(ngx_resolver_hdr_t) + nlen;
|
|
|
|
qs = (ngx_resolver_qs_t *) p;
|
|
|
|
qs->type_lo = NGX_RESOLVE_AAAA;
|
|
#endif
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_create_srv_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,
|
|
ngx_str_t *name)
|
|
{
|
|
u_char *p, *s;
|
|
size_t len, nlen;
|
|
ngx_uint_t ident;
|
|
ngx_resolver_qs_t *qs;
|
|
ngx_resolver_hdr_t *query;
|
|
|
|
nlen = name->len ? (1 + name->len + 1) : 1;
|
|
|
|
len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t);
|
|
|
|
p = ngx_resolver_alloc(r, len);
|
|
if (p == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rn->qlen = (u_short) len;
|
|
rn->query = p;
|
|
|
|
query = (ngx_resolver_hdr_t *) p;
|
|
|
|
ident = ngx_random();
|
|
|
|
ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
|
|
"resolve: \"%V\" SRV %i", 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_hdr_t) + nlen;
|
|
|
|
qs = (ngx_resolver_qs_t *) p;
|
|
|
|
/* query type */
|
|
qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_SRV;
|
|
|
|
/* IN query class */
|
|
qs->class_hi = 0; qs->class_lo = 1;
|
|
|
|
/* converts "www.example.com" to "\3www\7example\3com\0" */
|
|
|
|
len = 0;
|
|
p--;
|
|
*p-- = '\0';
|
|
|
|
if (name->len == 0) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
for (s = name->data + name->len - 1; s >= name->data; s--) {
|
|
if (*s != '.') {
|
|
*p = *s;
|
|
len++;
|
|
|
|
} else {
|
|
if (len == 0 || len > 255) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
*p = (u_char) len;
|
|
len = 0;
|
|
}
|
|
|
|
p--;
|
|
}
|
|
|
|
if (len == 0 || len > 255) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
*p = (u_char) len;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_create_addr_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,
|
|
ngx_resolver_addr_t *addr)
|
|
{
|
|
u_char *p, *d;
|
|
size_t len;
|
|
in_addr_t inaddr;
|
|
ngx_int_t n;
|
|
ngx_uint_t ident;
|
|
ngx_resolver_hdr_t *query;
|
|
struct sockaddr_in *sin;
|
|
#if (NGX_HAVE_INET6)
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
|
|
switch (addr->sockaddr->sa_family) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case AF_INET6:
|
|
len = sizeof(ngx_resolver_hdr_t)
|
|
+ 64 + sizeof(".ip6.arpa.") - 1
|
|
+ sizeof(ngx_resolver_qs_t);
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* AF_INET */
|
|
len = sizeof(ngx_resolver_hdr_t)
|
|
+ sizeof(".255.255.255.255.in-addr.arpa.") - 1
|
|
+ sizeof(ngx_resolver_qs_t);
|
|
}
|
|
|
|
p = ngx_resolver_alloc(r, len);
|
|
if (p == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rn->query = p;
|
|
query = (ngx_resolver_hdr_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_hdr_t);
|
|
|
|
switch (addr->sockaddr->sa_family) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case AF_INET6:
|
|
sin6 = (struct sockaddr_in6 *) addr->sockaddr;
|
|
|
|
for (n = 15; n >= 0; n--) {
|
|
p = ngx_sprintf(p, "\1%xd\1%xd",
|
|
sin6->sin6_addr.s6_addr[n] & 0xf,
|
|
(sin6->sin6_addr.s6_addr[n] >> 4) & 0xf);
|
|
}
|
|
|
|
p = ngx_cpymem(p, "\3ip6\4arpa\0", 10);
|
|
|
|
break;
|
|
#endif
|
|
|
|
default: /* AF_INET */
|
|
|
|
sin = (struct sockaddr_in *) addr->sockaddr;
|
|
inaddr = ntohl(sin->sin_addr.s_addr);
|
|
|
|
for (n = 0; n < 32; n += 8) {
|
|
d = ngx_sprintf(&p[1], "%ud", (inaddr >> n) & 0xff);
|
|
*p = (u_char) (d - &p[1]);
|
|
p = d;
|
|
}
|
|
|
|
p = ngx_cpymem(p, "\7in-addr\4arpa\0", 14);
|
|
}
|
|
|
|
/* query type "PTR", IN query class */
|
|
p = ngx_cpymem(p, "\0\14\0\1", 4);
|
|
|
|
rn->qlen = (u_short) (p - 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;
|
|
size_t len;
|
|
ngx_uint_t i, n;
|
|
|
|
p = src;
|
|
len = 0;
|
|
|
|
/*
|
|
* 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) {
|
|
if ((n & 0xc0) != 0xc0) {
|
|
err = "invalid label type in DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
if (p >= last) {
|
|
err = "name is out of DNS response";
|
|
goto invalid;
|
|
}
|
|
|
|
n = ((n & 0x3f) << 8) + *p;
|
|
p = &buf[n];
|
|
|
|
} else {
|
|
len += 1 + n;
|
|
p = &p[n];
|
|
}
|
|
|
|
if (p >= last) {
|
|
err = "name is out of DNS response";
|
|
goto invalid;
|
|
}
|
|
}
|
|
|
|
err = "compression pointers loop in DNS response";
|
|
|
|
invalid:
|
|
|
|
ngx_log_error(r->log_level, r->log, 0, err);
|
|
|
|
return NGX_ERROR;
|
|
|
|
done:
|
|
|
|
if (name == NULL) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (len == 0) {
|
|
ngx_str_null(name);
|
|
return NGX_OK;
|
|
}
|
|
|
|
dst = ngx_resolver_alloc(r, len);
|
|
if (dst == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
name->data = dst;
|
|
|
|
for ( ;; ) {
|
|
n = *src++;
|
|
|
|
if (n == 0) {
|
|
name->len = dst - name->data - 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (n & 0xc0) {
|
|
n = ((n & 0x3f) << 8) + *src;
|
|
src = &buf[n];
|
|
|
|
} else {
|
|
ngx_strlow(dst, src, n);
|
|
dst += n;
|
|
src += n;
|
|
*dst++ = '.';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_set_timeout(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
|
{
|
|
if (ctx->event || ctx->timeout == 0) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
|
|
if (ctx->event == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ctx->event->handler = ngx_resolver_timeout_handler;
|
|
ctx->event->data = ctx;
|
|
ctx->event->log = r->log;
|
|
ctx->event->cancelable = ctx->cancelable;
|
|
ctx->ident = -1;
|
|
|
|
ngx_add_timer(ctx->event, ctx->timeout);
|
|
|
|
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)
|
|
{
|
|
ngx_uint_t i;
|
|
|
|
/* 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 && rn->naddrs != (u_short) -1) {
|
|
ngx_resolver_free_locked(r, rn->u.addrs);
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) {
|
|
ngx_resolver_free_locked(r, rn->u6.addrs6);
|
|
}
|
|
#endif
|
|
|
|
if (rn->nsrvs) {
|
|
for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {
|
|
if (rn->u.srvs[i].name.data) {
|
|
ngx_resolver_free_locked(r, rn->u.srvs[i].name.data);
|
|
}
|
|
}
|
|
|
|
ngx_resolver_free_locked(r, rn->u.srvs);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static 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;
|
|
}
|
|
|
|
|
|
static ngx_resolver_addr_t *
|
|
ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn,
|
|
ngx_uint_t rotate)
|
|
{
|
|
ngx_uint_t d, i, j, n;
|
|
in_addr_t *addr;
|
|
ngx_sockaddr_t *sockaddr;
|
|
struct sockaddr_in *sin;
|
|
ngx_resolver_addr_t *dst;
|
|
#if (NGX_HAVE_INET6)
|
|
struct in6_addr *addr6;
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
|
|
n = rn->naddrs;
|
|
#if (NGX_HAVE_INET6)
|
|
n += rn->naddrs6;
|
|
#endif
|
|
|
|
dst = ngx_resolver_calloc(r, n * sizeof(ngx_resolver_addr_t));
|
|
if (dst == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
sockaddr = ngx_resolver_calloc(r, n * sizeof(ngx_sockaddr_t));
|
|
if (sockaddr == NULL) {
|
|
ngx_resolver_free(r, dst);
|
|
return NULL;
|
|
}
|
|
|
|
i = 0;
|
|
d = rotate ? ngx_random() % n : 0;
|
|
|
|
if (rn->naddrs) {
|
|
j = rotate ? ngx_random() % rn->naddrs : 0;
|
|
|
|
addr = (rn->naddrs == 1) ? &rn->u.addr : rn->u.addrs;
|
|
|
|
do {
|
|
sin = &sockaddr[d].sockaddr_in;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_addr.s_addr = addr[j++];
|
|
dst[d].sockaddr = (struct sockaddr *) sin;
|
|
dst[d++].socklen = sizeof(struct sockaddr_in);
|
|
|
|
if (d == n) {
|
|
d = 0;
|
|
}
|
|
|
|
if (j == (ngx_uint_t) rn->naddrs) {
|
|
j = 0;
|
|
}
|
|
} while (++i < (ngx_uint_t) rn->naddrs);
|
|
}
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
if (rn->naddrs6) {
|
|
j = rotate ? ngx_random() % rn->naddrs6 : 0;
|
|
|
|
addr6 = (rn->naddrs6 == 1) ? &rn->u6.addr6 : rn->u6.addrs6;
|
|
|
|
do {
|
|
sin6 = &sockaddr[d].sockaddr_in6;
|
|
sin6->sin6_family = AF_INET6;
|
|
ngx_memcpy(sin6->sin6_addr.s6_addr, addr6[j++].s6_addr, 16);
|
|
dst[d].sockaddr = (struct sockaddr *) sin6;
|
|
dst[d++].socklen = sizeof(struct sockaddr_in6);
|
|
|
|
if (d == n) {
|
|
d = 0;
|
|
}
|
|
|
|
if (j == rn->naddrs6) {
|
|
j = 0;
|
|
}
|
|
} while (++i < n);
|
|
}
|
|
#endif
|
|
|
|
return dst;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
|
{
|
|
ngx_uint_t naddrs, nsrvs, nw, i, j, k, l, m, n, w;
|
|
ngx_resolver_addr_t *addrs;
|
|
ngx_resolver_srv_name_t *srvs;
|
|
|
|
srvs = ctx->srvs;
|
|
nsrvs = ctx->nsrvs;
|
|
|
|
naddrs = 0;
|
|
|
|
for (i = 0; i < nsrvs; i++) {
|
|
if (srvs[i].state == NGX_ERROR) {
|
|
ctx->state = NGX_ERROR;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
|
|
ctx->handler(ctx);
|
|
return;
|
|
}
|
|
|
|
naddrs += srvs[i].naddrs;
|
|
}
|
|
|
|
if (naddrs == 0) {
|
|
ctx->state = srvs[0].state;
|
|
|
|
for (i = 0; i < nsrvs; i++) {
|
|
if (srvs[i].state == NGX_RESOLVE_NXDOMAIN) {
|
|
ctx->state = NGX_RESOLVE_NXDOMAIN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
|
|
ctx->handler(ctx);
|
|
return;
|
|
}
|
|
|
|
addrs = ngx_resolver_calloc(r, naddrs * sizeof(ngx_resolver_addr_t));
|
|
if (addrs == NULL) {
|
|
ctx->state = NGX_ERROR;
|
|
ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
|
|
|
|
ctx->handler(ctx);
|
|
return;
|
|
}
|
|
|
|
i = 0;
|
|
n = 0;
|
|
|
|
do {
|
|
nw = 0;
|
|
|
|
for (j = i; j < nsrvs; j++) {
|
|
if (srvs[j].priority != srvs[i].priority) {
|
|
break;
|
|
}
|
|
|
|
nw += srvs[j].naddrs * srvs[j].weight;
|
|
}
|
|
|
|
if (nw == 0) {
|
|
goto next_srv;
|
|
}
|
|
|
|
w = ngx_random() % nw;
|
|
|
|
for (k = i; k < j; k++) {
|
|
if (w < srvs[k].naddrs * srvs[k].weight) {
|
|
break;
|
|
}
|
|
|
|
w -= srvs[k].naddrs * srvs[k].weight;
|
|
}
|
|
|
|
for (l = i; l < j; l++) {
|
|
|
|
for (m = 0; m < srvs[k].naddrs; m++) {
|
|
addrs[n].socklen = srvs[k].addrs[m].socklen;
|
|
addrs[n].sockaddr = srvs[k].addrs[m].sockaddr;
|
|
addrs[n].name = srvs[k].name;
|
|
addrs[n].priority = srvs[k].priority;
|
|
addrs[n].weight = srvs[k].weight;
|
|
n++;
|
|
}
|
|
|
|
if (++k == j) {
|
|
k = i;
|
|
}
|
|
}
|
|
|
|
next_srv:
|
|
|
|
i = j;
|
|
|
|
} while (i < ctx->nsrvs);
|
|
|
|
ctx->state = NGX_OK;
|
|
ctx->addrs = addrs;
|
|
ctx->naddrs = naddrs;
|
|
|
|
ctx->handler(ctx);
|
|
|
|
ngx_resolver_free(r, addrs);
|
|
}
|
|
|
|
|
|
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";
|
|
}
|
|
|
|
|
|
static u_char *
|
|
ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len)
|
|
{
|
|
u_char *p;
|
|
ngx_resolver_connection_t *rec;
|
|
|
|
p = buf;
|
|
|
|
if (log->action) {
|
|
p = ngx_snprintf(buf, len, " while %s", log->action);
|
|
len -= p - buf;
|
|
}
|
|
|
|
rec = log->data;
|
|
|
|
if (rec) {
|
|
p = ngx_snprintf(p, len, ", resolver: %V", &rec->server);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_udp_connect(ngx_resolver_connection_t *rec)
|
|
{
|
|
int rc;
|
|
ngx_int_t event;
|
|
ngx_event_t *rev, *wev;
|
|
ngx_socket_t s;
|
|
ngx_connection_t *c;
|
|
|
|
s = ngx_socket(rec->sockaddr->sa_family, SOCK_DGRAM, 0);
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "UDP socket %d", s);
|
|
|
|
if (s == (ngx_socket_t) -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_socket_n " failed");
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
c = ngx_get_connection(s, &rec->log);
|
|
|
|
if (c == NULL) {
|
|
if (ngx_close_socket(s) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_close_socket_n " failed");
|
|
}
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
if (ngx_nonblocking(s) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_nonblocking_n " failed");
|
|
|
|
goto failed;
|
|
}
|
|
|
|
rev = c->read;
|
|
wev = c->write;
|
|
|
|
rev->log = &rec->log;
|
|
wev->log = &rec->log;
|
|
|
|
rec->udp = c;
|
|
|
|
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
|
|
|
|
c->start_time = ngx_current_msec;
|
|
|
|
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0,
|
|
"connect to %V, fd:%d #%uA", &rec->server, s, c->number);
|
|
|
|
rc = connect(s, rec->sockaddr, rec->socklen);
|
|
|
|
/* TODO: iocp */
|
|
|
|
if (rc == -1) {
|
|
ngx_log_error(NGX_LOG_CRIT, &rec->log, ngx_socket_errno,
|
|
"connect() failed");
|
|
|
|
goto failed;
|
|
}
|
|
|
|
/* UDP sockets are always ready to write */
|
|
wev->ready = 1;
|
|
|
|
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) {
|
|
goto failed;
|
|
}
|
|
|
|
return NGX_OK;
|
|
|
|
failed:
|
|
|
|
ngx_close_connection(c);
|
|
rec->udp = NULL;
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_tcp_connect(ngx_resolver_connection_t *rec)
|
|
{
|
|
int rc;
|
|
ngx_int_t event;
|
|
ngx_err_t err;
|
|
ngx_uint_t level;
|
|
ngx_socket_t s;
|
|
ngx_event_t *rev, *wev;
|
|
ngx_connection_t *c;
|
|
|
|
s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0);
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s);
|
|
|
|
if (s == (ngx_socket_t) -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_socket_n " failed");
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
c = ngx_get_connection(s, &rec->log);
|
|
|
|
if (c == NULL) {
|
|
if (ngx_close_socket(s) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_close_socket_n " failed");
|
|
}
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
if (ngx_nonblocking(s) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_nonblocking_n " failed");
|
|
|
|
goto failed;
|
|
}
|
|
|
|
rev = c->read;
|
|
wev = c->write;
|
|
|
|
rev->log = &rec->log;
|
|
wev->log = &rec->log;
|
|
|
|
rec->tcp = c;
|
|
|
|
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
|
|
|
|
c->start_time = ngx_current_msec;
|
|
|
|
if (ngx_add_conn) {
|
|
if (ngx_add_conn(c) == NGX_ERROR) {
|
|
goto failed;
|
|
}
|
|
}
|
|
|
|
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0,
|
|
"connect to %V, fd:%d #%uA", &rec->server, s, c->number);
|
|
|
|
rc = connect(s, rec->sockaddr, rec->socklen);
|
|
|
|
if (rc == -1) {
|
|
err = ngx_socket_errno;
|
|
|
|
|
|
if (err != NGX_EINPROGRESS
|
|
#if (NGX_WIN32)
|
|
/* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
|
|
&& err != NGX_EAGAIN
|
|
#endif
|
|
)
|
|
{
|
|
if (err == NGX_ECONNREFUSED
|
|
#if (NGX_LINUX)
|
|
/*
|
|
* Linux returns EAGAIN instead of ECONNREFUSED
|
|
* for unix sockets if listen queue is full
|
|
*/
|
|
|| err == NGX_EAGAIN
|
|
#endif
|
|
|| err == NGX_ECONNRESET
|
|
|| err == NGX_ENETDOWN
|
|
|| err == NGX_ENETUNREACH
|
|
|| err == NGX_EHOSTDOWN
|
|
|| err == NGX_EHOSTUNREACH)
|
|
{
|
|
level = NGX_LOG_ERR;
|
|
|
|
} else {
|
|
level = NGX_LOG_CRIT;
|
|
}
|
|
|
|
ngx_log_error(level, &rec->log, err, "connect() to %V failed",
|
|
&rec->server);
|
|
|
|
ngx_close_connection(c);
|
|
rec->tcp = NULL;
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ngx_add_conn) {
|
|
if (rc == -1) {
|
|
|
|
/* NGX_EINPROGRESS */
|
|
|
|
return NGX_AGAIN;
|
|
}
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "connected");
|
|
|
|
wev->ready = 1;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, ngx_socket_errno,
|
|
"connect(): %d", rc);
|
|
|
|
if (ngx_blocking(s) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
|
|
ngx_blocking_n " failed");
|
|
goto failed;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
goto failed;
|
|
}
|
|
|
|
if (rc == -1) {
|
|
|
|
/* NGX_EINPROGRESS */
|
|
|
|
if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
|
|
goto failed;
|
|
}
|
|
|
|
return NGX_AGAIN;
|
|
}
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "connected");
|
|
|
|
wev->ready = 1;
|
|
|
|
return NGX_OK;
|
|
|
|
failed:
|
|
|
|
ngx_close_connection(c);
|
|
rec->tcp = NULL;
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_resolver_cmp_srvs(const void *one, const void *two)
|
|
{
|
|
ngx_int_t p1, p2;
|
|
ngx_resolver_srv_t *first, *second;
|
|
|
|
first = (ngx_resolver_srv_t *) one;
|
|
second = (ngx_resolver_srv_t *) two;
|
|
|
|
p1 = first->priority;
|
|
p2 = second->priority;
|
|
|
|
return p1 - p2;
|
|
}
|