[nginx] Resolver: added support for SRV records.
Dmitry Volyntsev
xeioex at nginx.com
Wed Mar 23 14:50:23 UTC 2016
details: http://hg.nginx.org/nginx/rev/384154fc634f
branches:
changeset: 6458:384154fc634f
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Wed Mar 23 17:44:36 2016 +0300
description:
Resolver: added support for SRV records.
diffstat:
src/core/ngx_resolver.c | 972 +++++++++++++++++++++++++++++++++++++++++-
src/core/ngx_resolver.h | 47 +-
src/http/ngx_http_upstream.h | 2 +-
3 files changed, 993 insertions(+), 28 deletions(-)
diffs (truncated from 1371 to 1000 lines):
diff -r a6711b85da83 -r 384154fc634f src/core/ngx_resolver.c
--- a/src/core/ngx_resolver.c Wed Mar 23 17:44:36 2016 +0300
+++ b/src/core/ngx_resolver.c Wed Mar 23 17:44:36 2016 +0300
@@ -74,8 +74,10 @@ static ngx_int_t ngx_resolver_send_tcp_q
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_addr_t *addr);
+ 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);
@@ -88,10 +90,15 @@ static void ngx_resolver_process_respons
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,
@@ -105,9 +112,14 @@ static void *ngx_resolver_calloc(ngx_res
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_addr_t *ngx_resolver_export(ngx_resolver_t *r,
+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,
@@ -149,13 +161,18 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_
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)
@@ -274,6 +291,8 @@ ngx_resolver_cleanup(void *data)
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)
@@ -383,7 +402,9 @@ ngx_resolve_start(ngx_resolver_t *r, ngx
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;
@@ -400,9 +421,41 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx
return NGX_OK;
}
- /* lock name mutex */
-
- rc = ngx_resolve_name_locked(r, ctx, &ctx->name);
+ 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) {
+ return NGX_ERROR;
+ }
+
+ 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;
@@ -429,6 +482,7 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx
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;
@@ -448,6 +502,23 @@ ngx_resolve_name_done(ngx_resolver_ctx_t
/* 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;
@@ -466,15 +537,20 @@ ngx_resolve_name_done(ngx_resolver_ctx_t
p = &w->next;
w = w->next;
}
+
+ ngx_log_error(NGX_LOG_ALERT, r->log, 0,
+ "could not cancel %V resolving", &ctx->name);
}
-
- ngx_log_error(NGX_LOG_ALERT, r->log, 0,
- "could not cancel %V resolving", &ctx->name);
}
done:
- ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue);
+ 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 */
@@ -501,16 +577,31 @@ ngx_resolve_name_locked(ngx_resolver_t *
uint32_t hash;
ngx_int_t rc;
ngx_str_t cname;
- ngx_uint_t naddrs;
- ngx_addr_t *addrs;
+ 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);
- rn = ngx_resolver_lookup_name(r, name, hash);
+ 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) {
@@ -525,7 +616,7 @@ ngx_resolve_name_locked(ngx_resolver_t *
rn->expire = ngx_time() + r->expire;
- ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+ ngx_queue_insert_head(expire_queue, &rn->queue);
naddrs = (rn->naddrs == (u_short) -1) ? 0 : rn->naddrs;
#if (NGX_HAVE_INET6)
@@ -581,6 +672,23 @@ ngx_resolve_name_locked(ngx_resolver_t *
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) {
@@ -663,6 +771,16 @@ ngx_resolve_name_locked(ngx_resolver_t *
}
#endif
+ if (rn->nsrvs) {
+ for (i = 0; i < 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 {
@@ -685,17 +803,22 @@ ngx_resolve_name_locked(ngx_resolver_t *
rn->query6 = NULL;
#endif
- ngx_rbtree_insert(&r->name_rbtree, &rn->node);
+ ngx_rbtree_insert(tree, &rn->node);
}
- rc = ngx_resolver_create_name_query(r, rn, name);
+ 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(&r->name_rbtree, &rn->node);
+ ngx_rbtree_delete(tree, &rn->node);
ngx_resolver_free(r, rn->query);
ngx_resolver_free(r, rn->name);
@@ -724,6 +847,7 @@ ngx_resolve_name_locked(ngx_resolver_t *
rn->naddrs6 = r->ipv6 ? (u_short) -1 : 0;
rn->tcp6 = 0;
#endif
+ rn->nsrvs = 0;
if (ngx_resolver_send_query(r, rn) != NGX_OK) {
goto failed;
@@ -743,13 +867,13 @@ ngx_resolve_name_locked(ngx_resolver_t *
ngx_add_timer(ctx->event, ctx->timeout);
}
- if (ngx_queue_empty(&r->name_resend_queue)) {
+ if (ngx_queue_empty(resend_queue)) {
ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
}
rn->expire = ngx_time() + r->resend_timeout;
- ngx_queue_insert_head(&r->name_resend_queue, &rn->queue);
+ ngx_queue_insert_head(resend_queue, &rn->queue);
rn->code = 0;
rn->cnlen = 0;
@@ -768,7 +892,7 @@ ngx_resolve_name_locked(ngx_resolver_t *
failed:
- ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+ ngx_rbtree_delete(tree, &rn->node);
if (rn->query) {
ngx_resolver_free(r, rn->query);
@@ -946,6 +1070,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx
rn->naddrs6 = (u_short) -1;
rn->tcp6 = 0;
#endif
+ rn->nsrvs = 0;
if (ngx_resolver_send_query(r, rn) != NGX_OK) {
goto failed;
@@ -1301,7 +1426,7 @@ ngx_resolver_send_tcp_query(ngx_resolver
static void
ngx_resolver_resend_handler(ngx_event_t *ev)
{
- time_t timer, atimer, ntimer;
+ time_t timer, atimer, stimer, ntimer;
#if (NGX_HAVE_INET6)
time_t a6timer;
#endif
@@ -1316,6 +1441,8 @@ ngx_resolver_resend_handler(ngx_event_t
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 */
@@ -1343,6 +1470,13 @@ ngx_resolver_resend_handler(ngx_event_t
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) {
@@ -1703,6 +1837,13 @@ found:
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);
@@ -1756,7 +1897,6 @@ ngx_resolver_process_a(ngx_resolver_t *r
uint32_t hash;
in_addr_t *addr;
ngx_str_t name;
- ngx_addr_t *addrs;
ngx_uint_t type, class, qident, naddrs, a, i, j, start;
#if (NGX_HAVE_INET6)
struct in6_addr *addr6;
@@ -1764,6 +1904,7 @@ ngx_resolver_process_a(ngx_resolver_t *r
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,
@@ -2400,6 +2541,536 @@ next:
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 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 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", 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", 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 = 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;
+
+ for (i = 0; i < 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 = 0;
+
+ 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;
+ u_char (*sockaddr)[NGX_SOCKADDRLEN];
+ ngx_addr_t *addrs;
+ ngx_resolver_t *r;
+ struct sockaddr_in *sin;
+ ngx_resolver_ctx_t *ctx;
+ ngx_resolver_srv_name_t *srv;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ r = cctx->resolver;
+ ctx = cctx->data;
+ srv = cctx->srvs;
+
+ ctx->count--;
+
+ srv->ctx = NULL;
+
+ 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) {
+ ngx_resolve_name_done(cctx);
+
+ ctx->state = NGX_ERROR;
+ ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
+
+ ctx->handler(ctx);
+ return;
+ }
+
+ sockaddr = ngx_resolver_alloc(r, cctx->naddrs * NGX_SOCKADDRLEN);
+ if (sockaddr == NULL) {
+ ngx_resolver_free(r, addrs);
+ ngx_resolve_name_done(cctx);
+
+ ctx->state = NGX_ERROR;
+ ctx->valid = ngx_time() + (r->valid ? r->valid : 10);
+
+ ctx->handler(ctx);
+ return;
+ }
+
+ for (i = 0; i < cctx->naddrs; i++) {
+ addrs[i].sockaddr = (struct sockaddr *) sockaddr[i];
+ addrs[i].socklen = cctx->addrs[i].socklen;
+
+ ngx_memcpy(sockaddr[i], cctx->addrs[i].sockaddr,
+ addrs[i].socklen);
+
+ switch (addrs[i].sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) addrs[i].sockaddr;
+ sin6->sin6_port = htons(srv->port);
+ break;
+#endif
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) addrs[i].sockaddr;
+ sin->sin_port = htons(srv->port);
+ }
+ }
+
+ srv->addrs = addrs;
+ srv->naddrs = cctx->naddrs;
+ }
+
+ 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)
{
@@ -2758,6 +3429,47 @@ ngx_resolver_lookup_name(ngx_resolver_t
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;
@@ -3045,8 +3757,96 @@ ngx_resolver_create_name_query(ngx_resol
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;
More information about the nginx-devel
mailing list