[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