[nginx] Resolver: TCP support.

Roman Arutyunyan arut at nginx.com
Thu Jan 28 12:43:32 UTC 2016


details:   http://hg.nginx.org/nginx/rev/5a16d40c63de
branches:  
changeset: 6367:5a16d40c63de
user:      Roman Arutyunyan <arut at nginx.com>
date:      Thu Jan 28 15:28:20 2016 +0300
description:
Resolver: TCP support.

Resend DNS query over TCP once UDP response came truncated.

diffstat:

 src/core/ngx_resolver.c |  593 +++++++++++++++++++++++++++++++++++++++++++----
 src/core/ngx_resolver.h |   17 +-
 2 files changed, 554 insertions(+), 56 deletions(-)

diffs (truncated from 834 to 300 lines):

diff -r 2e5c027f2a98 -r 5a16d40c63de src/core/ngx_resolver.c
--- a/src/core/ngx_resolver.c	Thu Jan 28 15:28:20 2016 +0300
+++ b/src/core/ngx_resolver.c	Thu Jan 28 15:28:20 2016 +0300
@@ -12,6 +12,9 @@
 
 #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;
@@ -54,6 +57,7 @@ typedef struct {
 
 
 ngx_int_t ngx_udp_connect(ngx_resolver_connection_t *rec);
+ngx_int_t ngx_tcp_connect(ngx_resolver_connection_t *rec);
 
 
 static void ngx_resolver_cleanup(void *data);
@@ -64,6 +68,10 @@ static void ngx_resolver_expire(ngx_reso
     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_addr_query(ngx_resolver_t *r,
@@ -72,12 +80,14 @@ static void ngx_resolver_resend_handler(
 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_read_response(ngx_event_t *rev);
+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);
+    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 ans);
+    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,
@@ -165,6 +175,7 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_
     r->ident = -1;
 
     r->resend_timeout = 5;
+    r->tcp_timeout = 5;
     r->expire = 30;
     r->valid = 0;
 
@@ -241,6 +252,7 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_
             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;
         }
     }
 
@@ -279,6 +291,10 @@ ngx_resolver_cleanup(void *data)
             if (rec[i].udp) {
                 ngx_close_connection(rec[i].udp);
             }
+
+            if (rec[i].tcp) {
+                ngx_close_connection(rec[i].tcp);
+            }
         }
 
         ngx_free(r);
@@ -691,8 +707,10 @@ ngx_resolve_name_locked(ngx_resolver_t *
     }
 
     rn->naddrs = (u_short) -1;
+    rn->tcp = 0;
 #if (NGX_HAVE_INET6)
     rn->naddrs6 = r->ipv6 ? (u_short) -1 : 0;
+    rn->tcp6 = 0;
 #endif
 
     if (ngx_resolver_send_query(r, rn) != NGX_OK) {
@@ -908,8 +926,10 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx
     }
 
     rn->naddrs = (u_short) -1;
+    rn->tcp = 0;
 #if (NGX_HAVE_INET6)
     rn->naddrs6 = (u_short) -1;
+    rn->tcp6 = 0;
 #endif
 
     if (ngx_resolver_send_query(r, rn) != NGX_OK) {
@@ -1104,55 +1124,156 @@ ngx_resolver_expire(ngx_resolver_t *r, n
 static ngx_int_t
 ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)
 {
-    ssize_t                     n;
+    ngx_int_t                   rc;
     ngx_resolver_connection_t  *rec;
 
     rec = r->connections.elts;
     rec = &rec[rn->last_connection];
 
-    if (rec->udp == NULL) {
-
+    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 = r;
-        rec->udp->read->handler = ngx_resolver_read_response;
+        rec->udp->data = rec;
+        rec->udp->read->handler = ngx_resolver_udp_read;
         rec->udp->read->resolver = 1;
     }
 
-    if (rn->naddrs == (u_short) -1) {
-        n = ngx_send(rec->udp, rn->query, rn->qlen);
-
-        if (n == -1) {
+    n = ngx_send(rec->udp, query, qlen);
+
+    if (n == -1) {
+        return NGX_ERROR;
+    }
+
+    if ((size_t) n != (size_t) qlen) {
+        ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "send() incomplete");
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+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) {
+                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) {
+                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;
         }
 
-        if ((size_t) n != (size_t) rn->qlen) {
-            ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "send() incomplete");
-            return NGX_ERROR;
-        }
+        rec->tcp->data = rec;
+        rec->tcp->write->handler = ngx_resolver_tcp_write;
+        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));
     }
 
-#if (NGX_HAVE_INET6)
-    if (rn->query6 && rn->naddrs6 == (u_short) -1) {
-        n = ngx_send(rec->udp, rn->query6, rn->qlen);
-
-        if (n == -1) {
-            return NGX_ERROR;
-        }
-
-        if ((size_t) n != (size_t) rn->qlen) {
-            ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "send() incomplete");
-            return NGX_ERROR;
-        }
+    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;
     }
-#endif
+
+    *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;
 }
@@ -1282,13 +1403,15 @@ ngx_resolver_resend_empty(ngx_resolver_t
 
 
 static void
-ngx_resolver_read_response(ngx_event_t *rev)
+ngx_resolver_udp_read(ngx_event_t *rev)
 {
-    ssize_t            n;
-    ngx_connection_t  *c;
-    u_char             buf[NGX_RESOLVER_UDP_SIZE];
+    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;



More information about the nginx-devel mailing list