diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index cd55520c..d8dc49e8 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,182 @@ 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, + size_t path_len) +{ + 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]; + u_char ipv6_buf[NGX_INET6_ADDRSTRLEN]; + ngx_uint_t address = 0, j, total = 0; + ssize_t n, i; + enum { + sw_nameserver, + sw_spaces, + sw_address, + sw_skip + } state; + + file.name.data = path; + file.name.len = path_len; + + 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 (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno, + ngx_read_file_n " \"%s\" failed", file.name.data); + } + + 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_spaces; + i += sizeof("nameserver") - 1; + + continue; + } + + break; + + case sw_spaces: + if (buf[i] != '\t' && buf[i] != ' ') { + address = i; + state = sw_address; + } + + 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 == n - 1) ? n - address : i - address; + u.default_port = 53; + + /* IPv6? */ + if (ngx_strlchr(u.url.data, u.url.data + u.url.len, + ':') != NULL) + { + if (u.url.len + 2 > sizeof(ipv6_buf)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "IPv6 resolver address is too long:" + " \"%V\"", &u.url); + + return NGX_ERROR; + } + + ipv6_buf[0] = '['; + ngx_memcpy(ipv6_buf + 1, u.url.data, u.url.len); + ipv6_buf[u.url.len + 1] = ']'; + + u.url.data = ipv6_buf; + u.url.len = u.url.len + 2; + } + + 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++; + +#if (NGX_DEBUG) + /* + * logs with level below NGX_LOG_NOTICE will not be printed + * in this early phase + */ + ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, + "parsed a resolver: \"%V\"", &u.url); +#endif + + 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 +431,37 @@ 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, + sizeof(NGX_RESOLVER_FILE_NAME) + - 1) + != 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], + names[i].len - 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];