# HG changeset patch # User Dmitry Volyntsev # Date 1458744276 -10800 # Node ID 384154fc634f76f07ad67e7642733096bc500fb0 # Parent a6711b85da833027424103105131a154cc66bbe1 Resolver: added support for SRV records. diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -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; + + 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_addr_t *addr) + ngx_resolver_addr_t *addr) { u_char *p, *d; size_t len; @@ -3250,6 +4050,8 @@ ngx_resolver_timeout_handler(ngx_event_t 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) { @@ -3274,6 +4076,16 @@ ngx_resolver_free_node(ngx_resolver_t *r } #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); + } + ngx_resolver_free_locked(r, rn); /* unlock alloc mutex */ @@ -3345,15 +4157,15 @@ ngx_resolver_dup(ngx_resolver_t *r, void } -static ngx_addr_t * +static ngx_resolver_addr_t * ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn, ngx_uint_t rotate) { - ngx_addr_t *dst; ngx_uint_t d, i, j, n; u_char (*sockaddr)[NGX_SOCKADDRLEN]; in_addr_t *addr; struct sockaddr_in *sin; + ngx_resolver_addr_t *dst; #if (NGX_HAVE_INET6) struct in6_addr *addr6; struct sockaddr_in6 *sin6; @@ -3364,7 +4176,7 @@ ngx_resolver_export(ngx_resolver_t *r, n n += rn->naddrs6; #endif - dst = ngx_resolver_calloc(r, n * sizeof(ngx_addr_t)); + dst = ngx_resolver_calloc(r, n * sizeof(ngx_resolver_addr_t)); if (dst == NULL) { return NULL; } @@ -3428,6 +4240,100 @@ ngx_resolver_export(ngx_resolver_t *r, n } +static void +ngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) +{ + u_short nsrvs; + ngx_uint_t naddrs, nw, i, j, k, l, m, n, w; + ngx_resolver_addr_t *addrs; + ngx_resolver_srv_name_t *srvs; + + naddrs = 0; + + for (i = 0; i < ctx->nsrvs; i++) { + naddrs += ctx->srvs[i].naddrs; + } + + if (naddrs == 0) { + ctx->state = NGX_RESOLVE_NXDOMAIN; + 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; + } + + srvs = ctx->srvs; + nsrvs = ctx->nsrvs; + + 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) { @@ -3739,3 +4645,19 @@ failed: 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; +} diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h --- a/src/core/ngx_resolver.h +++ b/src/core/ngx_resolver.h @@ -21,6 +21,7 @@ #if (NGX_HAVE_INET6) #define NGX_RESOLVE_AAAA 28 #endif +#define NGX_RESOLVE_SRV 33 #define NGX_RESOLVE_DNAME 39 #define NGX_RESOLVE_FORMERR 1 @@ -58,6 +59,36 @@ typedef void (*ngx_resolver_handler_pt)( typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + u_short priority; + u_short weight; +} ngx_resolver_addr_t; + + +typedef struct { + ngx_str_t name; + u_short priority; + u_short weight; + u_short port; +} ngx_resolver_srv_t; + + +typedef struct { + ngx_str_t name; + u_short priority; + u_short weight; + u_short port; + + ngx_resolver_ctx_t *ctx; + + ngx_uint_t naddrs; + ngx_addr_t *addrs; +} ngx_resolver_srv_name_t; + + +typedef struct { ngx_rbtree_node_t node; ngx_queue_t queue; @@ -81,10 +112,12 @@ typedef struct { in_addr_t addr; in_addr_t *addrs; u_char *cname; + ngx_resolver_srv_t *srvs; } u; u_char code; u_short naddrs; + u_short nsrvs; u_short cnlen; #if (NGX_HAVE_INET6) @@ -127,13 +160,18 @@ struct ngx_resolver_s { ngx_rbtree_t name_rbtree; ngx_rbtree_node_t name_sentinel; + ngx_rbtree_t srv_rbtree; + ngx_rbtree_node_t srv_sentinel; + ngx_rbtree_t addr_rbtree; ngx_rbtree_node_t addr_sentinel; ngx_queue_t name_resend_queue; + ngx_queue_t srv_resend_queue; ngx_queue_t addr_resend_queue; ngx_queue_t name_expire_queue; + ngx_queue_t srv_expire_queue; ngx_queue_t addr_expire_queue; #if (NGX_HAVE_INET6) @@ -163,13 +201,18 @@ struct ngx_resolver_ctx_s { ngx_int_t state; ngx_str_t name; + ngx_str_t service; time_t valid; ngx_uint_t naddrs; - ngx_addr_t *addrs; - ngx_addr_t addr; + ngx_resolver_addr_t *addrs; + ngx_resolver_addr_t addr; struct sockaddr_in sin; + ngx_uint_t count; + ngx_uint_t nsrvs; + ngx_resolver_srv_name_t *srvs; + ngx_resolver_handler_pt handler; void *data; ngx_msec_t timeout; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -281,7 +281,7 @@ typedef struct { ngx_uint_t no_port; /* unsigned no_port:1 */ ngx_uint_t naddrs; - ngx_addr_t *addrs; + ngx_resolver_addr_t *addrs; struct sockaddr *sockaddr; socklen_t socklen;