Welcome! Log In Create A New Profile

Advanced

Re: [PATCH] Resolver: parse hosts file entries

Thibault Charbonnier
August 22, 2017 02:46AM
In case this patch interests anyone, here is an updated version of it.

It is fully backwards compatible; as long as the new 'hostsfile' option
is not set, resolvers will behave as usual.

I believe it delivers on the promised features, but if it is lacking
anything that prevents it from being merged, I would be glad to improve
it (for now, the tests have been written with Test::Nginx only).

Best,
Thibault

# HG changeset patch
# User Thibault Charbonnier <thibaultcha@me.com>
# Date 1488252201 28800
# Mon Feb 27 19:23:21 2017 -0800
# Node ID 558041ef1d70689ccc4c12f2487c3f75e6bbbccc
# Parent a2f5e25d6a283546f76435b9fc3e7e814b092bae
Resolver: parse hosts file entries

The resolver directive can now take an optional 'hostsfile=<path>' option,
such as:

resolver 8.8.4.4 hostsfile=/etc/hosts;

Hosts parsed from the hosts file are considered valid forever. The behavior
tries to be conservative, and only parses the hosts file when the option is
provided, to enforce backwards compatibility.

Additionally, this patch makes the resolver able to handle a host file
in the
absence of a nameserver, like so:

resolver hostsfile=/etc/hosts;

The 'hostsfile' option also honors the 'ipv6' flag of the 'resolver'
directive, as in:

resolver 8.8.4.4 hostsfile=/etc/hosts ipv6=off;

diff -r a2f5e25d6a28 -r 558041ef1d70 src/core/ngx_resolver.c
--- a/src/core/ngx_resolver.c Thu Aug 10 22:21:23 2017 +0300
+++ b/src/core/ngx_resolver.c Mon Feb 27 19:23:21 2017 -0800
@@ -9,11 +9,12 @@
#include <ngx_core.h>
#include <ngx_event.h>

-
-#define NGX_RESOLVER_UDP_SIZE 4096
-
-#define NGX_RESOLVER_TCP_RSIZE (2 + 65535)
-#define NGX_RESOLVER_TCP_WSIZE 8192
+#define NGX_RESOLVER_HOSTSFILE_BUFFER_SIZE 4096
+
+#define NGX_RESOLVER_UDP_SIZE 4096
+
+#define NGX_RESOLVER_TCP_RSIZE (2 + 65535)
+#define NGX_RESOLVER_TCP_WSIZE 8192


typedef struct {
@@ -122,6 +123,8 @@
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);
+static ngx_int_t ngx_resolver_parse_hosts_file(ngx_conf_t *cf,
+ ngx_resolver_t *r);

#if (NGX_HAVE_INET6)
static void ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t
*temp,
@@ -130,7 +133,6 @@
struct in6_addr *addr, uint32_t hash);
#endif

-
ngx_resolver_t *
ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n)
{
@@ -246,6 +248,20 @@
}
#endif

+ if (ngx_strncmp(names[i].data, "hostsfile=", 10) == 0) {
+ r->hosts_file.len = names[i].len - 10;
+
+ if (r->hosts_file.len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter: %V", &names[i]);
+ return NULL;
+ }
+
+ r->hosts_file.data = names[i].data + 10;
+
+ continue;
+ }
+
ngx_memzero(&u, sizeof(ngx_url_t));

u.url = names[i];
@@ -276,6 +292,13 @@
}
}

+ if (r->hosts_file.len > 0
+ && ngx_resolver_parse_hosts_file(cf, r)
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
return r;
}

@@ -396,7 +419,7 @@
}
}

- if (r->connections.nelts == 0) {
+ if (r->connections.nelts == 0 && !r->hosts_file.len) {
return NGX_NO_RESOLVER;
}

@@ -807,6 +830,13 @@
#endif

ngx_rbtree_insert(tree, &rn->node);
+
+ if (r->connections.nelts == 0) {
+ ctx->quick = 1;
+ ctx->state = NGX_RESOLVE_NXDOMAIN;
+ ctx->handler(ctx);
+ return NGX_OK;
+ }
}

if (ctx->service.len) {
@@ -4652,3 +4682,281 @@

return p1 - p2;
}
+
+
+static ngx_int_t
+ngx_resolver_parse_hosts_file(ngx_conf_t *cf, ngx_resolver_t *r)
+{
+ off_t file_size;
+ u_char ch;
+ u_char *start;
+ size_t len;
+ ssize_t n, size;
+ ngx_int_t rc;
+ ngx_buf_t b;
+ ngx_fd_t fd;
+ ngx_str_t filename, s;
+ ngx_file_t file;
+ in_addr_t addr;
+ ngx_resolver_node_t *rn;
+ enum {
+ scan_line = 0,
+ scan_skipline,
+ scan_addr,
+ scan_hosts,
+ scan_name
+ } state;
+
+ b.start = NULL;
+ s.data = NULL;
+ s.len = 0;
+ rn = NULL;
+ rc = NGX_OK;
+
+ filename = r->hosts_file;
+
+ fd = ngx_open_file(filename.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+ if (fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", filename.data);
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+
+ if (ngx_fd_info(fd, &file.info) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", filename.data);
+ goto fail;
+ }
+
+ file.fd = fd;
+ file.log = cf->log;
+ file.name.len = filename.len;
+ file.name.data = filename.data;
+ file.offset = 0;
+
+ b.start = ngx_alloc(NGX_RESOLVER_HOSTSFILE_BUFFER_SIZE, cf->log);
+ if (b.start == NULL) {
+ goto fail;
+ }
+
+ b.pos = b.start;
+ b.last = b.start;
+ b.end = b.last + NGX_RESOLVER_HOSTSFILE_BUFFER_SIZE;
+ b.temporary = 1;
+
+ start = b.pos;
+ state = scan_line;
+ file_size = ngx_file_size(&file.info);
+
+ for ( ;; ) {
+
+ if (b.pos >= b.last) {
+ len = b.pos - start;
+
+ if (len) {
+ ngx_memmove(b.start, start, len);
+ }
+
+ size = (ssize_t) (file_size - file.offset);
+
+ if (size > b.end - (b.start + len)) {
+ size = b.end - (b.start + len);
+
+ } else if (size == 0) {
+ goto done;
+ }
+
+ n = ngx_read_file(&file, b.start + len, size, file.offset);
+ if (n == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_read_file_n, " \"%s\" failed",
+ filename.data);
+ goto fail;
+ }
+
+ if (n != size) {
+ ngx_log_error(NGX_LOG_ERR, cf->log, ngx_errno,
+ ngx_read_file_n, " returned only %z bytes "
+ "instead of %z", n, size);
+ goto fail;
+ }
+
+ b.pos = b.start + len;
+ b.last = b.pos + n;
+ start = b.start;
+ }
+
+ ch = *b.pos;
+
+ switch (state) {
+
+ case scan_line:
+ if (ch == ' ') {
+ break;
+ }
+
+ if (ch == '#') {
+ state = scan_skipline;
+ break;
+ }
+
+ if (ch != LF && ch != CR) {
+ start = b.pos;
+ state = scan_addr;
+ }
+
+ break;
+
+ case scan_skipline:
+ if (ch == LF || ch == CR) {
+ state = scan_line;
+ }
+
+ break;
+
+ case scan_addr:
+ if (ch == LF || ch == CR) {
+ state = scan_line;
+ break;
+ }
+
+ if (ch == ' ' || ch == '\t') {
+ if (s.data) {
+ ngx_free(s.data);
+ }
+
+ s.len = b.pos - start;
+
+ s.data = ngx_alloc(s.len, cf->log);
+ if (s.data == NULL) {
+ goto fail;
+ }
+
+ ngx_memcpy(s.data, start, s.len);
+
+ state = scan_hosts;
+ }
+
+ break;
+
+ case scan_hosts:
+ if (ch == LF || ch == CR) {
+ state = scan_line;
+ break;
+ }
+
+ if (ch == ' ' || ch == '\t') {
+ break;
+ }
+
+ start = b.pos;
+ state = scan_name;
+ break;
+
+ case scan_name:
+ if (ch == ' ' || ch == '\t' || ch == LF || ch == CR) {
+ rn = ngx_calloc(sizeof(ngx_resolver_node_t), cf->log);
+ if (rn == NULL) {
+ goto fail;
+ }
+
+ rn->nlen = b.pos - start;
+
+ rn->name = ngx_alloc(rn->nlen, cf->log);
+ if (rn->name == NULL) {
+ goto fail;
+ }
+
+ ngx_memcpy(rn->name, start, rn->nlen);
+
+ rn->ttl = NGX_MAX_UINT32_VALUE;
+ rn->valid = NGX_MAX_UINT32_VALUE;
+ rn->expire = NGX_MAX_UINT32_VALUE;
+ rn->node.key = ngx_crc32_short(rn->name, rn->nlen);
+
+ if (ngx_strlchr(s.data,
+ s.data + s.len, ':') != NULL)
+ {
+
+#if (NGX_HAVE_INET6)
+ if (!r->ipv6
+ || ngx_inet6_addr(s.data, s.len,
+ rn->u6.addr6.s6_addr) != NGX_OK)
+ {
+#endif
+
+ ngx_resolver_free_node(r, rn);
+ state = scan_skipline;
+ break;
+
+#if (NGX_HAVE_INET6)
+ }
+
+ rn->naddrs6 = 1;
+#endif
+
+ } else {
+ addr = ngx_inet_addr(s.data, s.len);
+ if (addr == INADDR_NONE) {
+ ngx_resolver_free_node(r, rn);
+ state = scan_skipline;
+ break;
+ }
+
+ rn->naddrs = 1;
+ rn->u.addr = addr;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, cf->log, 0,
+ "host \"%*s\" will resolve to \"%V\" "
+ "(hosts file at \"%V\")",
+ rn->nlen, rn->name, &s, &filename);
+
+ ngx_rbtree_insert(&r->name_rbtree, &rn->node);
+
+ ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+ if (ch == LF || ch == CR) {
+ state = scan_line;
+ break;
+ }
+
+ state = scan_hosts;
+ }
+
+ break;
+ }
+
+ b.pos++;
+ }
+
+fail:
+
+ rc = NGX_ERROR;
+
+done:
+
+ if (s.data) {
+ ngx_free(s.data);
+ }
+
+ if (b.start) {
+ ngx_free(b.start);
+ }
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n, " \"%s\" failed",
+ filename.data);
+ rc = NGX_ERROR;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
diff -r a2f5e25d6a28 -r 558041ef1d70 src/core/ngx_resolver.h
--- a/src/core/ngx_resolver.h Thu Aug 10 22:21:23 2017 +0300
+++ b/src/core/ngx_resolver.h Mon Feb 27 19:23:21 2017 -0800
@@ -146,6 +146,8 @@


struct ngx_resolver_s {
+ ngx_str_t hosts_file;
+
/* has to be pointer because of "incomplete type" */
ngx_event_t *event;
void *dummy;
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[PATCH] Resolver: parse hosts file entries

Thibault Charbonnier 1110 February 27, 2017 10:46PM

Re: [PATCH] Resolver: parse hosts file entries

Thibault Charbonnier 344 February 28, 2017 02:52AM

Re: [PATCH] Resolver: parse hosts file entries

Thibault Charbonnier 487 March 03, 2017 02:36PM

Re: [PATCH] Resolver: parse hosts file entries

Thibault Charbonnier 435 August 22, 2017 02:46AM

Re: [PATCH] Resolver: parse hosts file entries

ru@nginx.com 866 September 14, 2017 09:32AM

Re: [PATCH] Resolver: parse hosts file entries

Thibault Charbonnier 325 September 15, 2017 09:26PM

Re: [PATCH] Resolver: parse hosts file entries

Maxim Dounin 356 September 18, 2017 09:40AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 171
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready