diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index cd55520c..6a3f98bb 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -5,6 +5,7 @@ */ +#include #include #include #include @@ -15,6 +16,14 @@ #define NGX_RESOLVER_TCP_RSIZE (2 + 65535) #define NGX_RESOLVER_TCP_WSIZE 8192 +/* + * note that 2KB should be more than enough for majority of the + * resolv.conf files out there. it also acts as a safety guard to prevent + * abuse. + */ +#define NGX_RESOLVER_FILE_BUF_SIZE 2048 +#define NGX_RESOLVER_FILE_NAME "/etc/resolv.conf" + typedef struct { u_char ident_hi; @@ -131,6 +140,138 @@ static ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r, #endif +static ngx_int_t +ngx_resolver_read_resolv_conf(ngx_conf_t *cf, ngx_resolver_t *r, u_char *path) +{ + ngx_url_t u; + ngx_resolver_connection_t *rec; + ngx_fd_t fd; + ngx_file_t file; + u_char buf[NGX_RESOLVER_FILE_BUF_SIZE]; + ngx_uint_t address, j, total = 0; + ssize_t n, i; + enum { + sw_nameserver, + sw_address, + sw_skip + } state; + + file.name.data = path; + file.name.len = ngx_strlen(path); + + if (ngx_conf_full_name(cf->cycle, &file.name, 1) != NGX_OK) { + return NGX_ERROR; + } + + fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, + NGX_FILE_OPEN, 0); + + if (fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + + return NGX_ERROR; + } + + ngx_memzero(&file, sizeof(ngx_file_t)); + + file.fd = fd; + file.log = cf->log; + + state = sw_nameserver; + + n = ngx_read_file(&file, buf, NGX_RESOLVER_FILE_BUF_SIZE, 0); + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + if (n == 0) { + return NGX_OK; + } + + for (i = 0; i < n && total < MAXNS; /* void */) { + if (buf[i] == '#' || buf[i] == ';') { + state = sw_skip; + } + + switch (state) { + + case sw_nameserver: + + if ((size_t) n - i >= sizeof("nameserver ") - 1 + && ngx_memcmp(buf + i, "nameserver ", + sizeof("nameserver ") - 1) == 0) + { + state = sw_address; + i += sizeof("nameserver ") - 1; + address = i; + + continue; + } + + break; + + case sw_address: + + if (buf[i] == CR || buf[i] == LF || i == n - 1) { + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url.data = buf + address; + u.url.len = i - address; + u.default_port = 53; + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in resolver \"%V\"", + u.err, &u.url); + } + + return NGX_ERROR; + } + + rec = ngx_array_push_n(&r->connections, u.naddrs); + if (rec == NULL) { + return NGX_ERROR; + } + + ngx_memzero(rec, u.naddrs * sizeof(ngx_resolver_connection_t)); + + for (j = 0; j < u.naddrs; j++) { + 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; + } + + total++; + + state = sw_nameserver; + } + + break; + + case sw_skip: + if (buf[i] == CR || buf[i] == LF) { + state = sw_nameserver; + } + + break; + } + + i++; + } + + return NGX_OK; +} + + ngx_resolver_t * ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) { @@ -246,6 +387,33 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) } #endif + if (ngx_strncmp(names[i].data, "local=", 6) == 0) { + + if (ngx_strcmp(&names[i].data[6], "on") == 0) { + if (ngx_resolver_read_resolv_conf(cf, r, + (u_char *) NGX_RESOLVER_FILE_NAME) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unable to parse local resolver"); + return NULL; + } + + } else if (ngx_strcmp(&names[i].data[6], "off") != 0) { + if (ngx_resolver_read_resolv_conf(cf, r, + &names[i].data[6]) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unable to parse local resolver"); + return NULL; + } + + } + + continue; + } + ngx_memzero(&u, sizeof(ngx_url_t)); u.url = names[i];