[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