From 3135bb845026867bc2e2515ed80a902f908797ae Mon Sep 17 00:00:00 2001 From: Datong Sun Date: Thu, 15 Mar 2018 18:38:58 -0700 Subject: [PATCH] resolver: improved space handling. improved error handling and added IPv6 support. --- .../nginx-1.13.6-resolver_conf_parsing.patch | 72 +++++-- t/000-resolver.t | 187 ++++++++++++++++-- 2 files changed, 226 insertions(+), 33 deletions(-) diff --git a/patches/nginx-1.13.6-resolver_conf_parsing.patch b/patches/nginx-1.13.6-resolver_conf_parsing.patch index 7fc1d62..87cc53f 100644 --- a/patches/nginx-1.13.6-resolver_conf_parsing.patch +++ b/patches/nginx-1.13.6-resolver_conf_parsing.patch @@ -1,5 +1,5 @@ diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c -index cd55520c..6a3f98bb 100644 +index cd55520c..d91c406c 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -5,6 +5,7 @@ @@ -25,28 +25,31 @@ index cd55520c..6a3f98bb 100644 typedef struct { u_char ident_hi; -@@ -131,6 +140,138 @@ static ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r, +@@ -131,6 +140,180 @@ 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_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, j, total = 0; + ssize_t n, i; + enum { + sw_nameserver, ++ sw_spaces, + sw_address, + sw_skip + } state; + + file.name.data = path; -+ file.name.len = ngx_strlen(path); ++ file.name.len = path_len; + + if (ngx_conf_full_name(cf->cycle, &file.name, 1) != NGX_OK) { + return NGX_ERROR; @@ -71,6 +74,11 @@ index cd55520c..6a3f98bb 100644 + + 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); @@ -93,28 +101,53 @@ index cd55520c..6a3f98bb 100644 + + case sw_nameserver: + -+ if ((size_t) n - i >= sizeof("nameserver ") - 1 -+ && ngx_memcmp(buf + i, "nameserver ", -+ sizeof("nameserver ") - 1) == 0) ++ 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; ++ 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 - 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, @@ -141,6 +174,15 @@ index cd55520c..6a3f98bb 100644 + + 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; + } + @@ -164,7 +206,7 @@ index cd55520c..6a3f98bb 100644 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) +@@ -246,6 +429,35 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) } #endif @@ -172,7 +214,8 @@ index cd55520c..6a3f98bb 100644 + + if (ngx_strcmp(&names[i].data[6], "on") == 0) { + if (ngx_resolver_read_resolv_conf(cf, r, -+ (u_char *) NGX_RESOLVER_FILE_NAME) ++ (u_char *) NGX_RESOLVER_FILE_NAME, ++ sizeof(NGX_RESOLVER_FILE_NAME) - 1) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -182,7 +225,8 @@ index cd55520c..6a3f98bb 100644 + + } else if (ngx_strcmp(&names[i].data[6], "off") != 0) { + if (ngx_resolver_read_resolv_conf(cf, r, -+ &names[i].data[6]) ++ &names[i].data[6], ++ names[i].len - 6) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, diff --git a/t/000-resolver.t b/t/000-resolver.t index b7104e8..ffaff9b 100644 --- a/t/000-resolver.t +++ b/t/000-resolver.t @@ -4,15 +4,14 @@ use Test::Nginx::Socket::Lua; #worker_connections(1014); #master_process_enabled(1); -log_level('warn'); +log_level('debug'); -#repeat_each(120); -repeat_each(2); +repeat_each(1); -plan tests => repeat_each() * (blocks() * 2); +plan tests => repeat_each() * (blocks() * 5); #no_diff(); -#no_long_string(); +no_long_string(); run_tests(); @@ -28,7 +27,7 @@ __DATA__ local sock = ngx.socket.tcp() local ok, err = sock:connect("openresty.org", 80) if not ok then - ngx.say("failed to connect to ", server, ": ", err) + ngx.say("failed to connect to openresty.org: ", err) return end ngx.say("successfully connected to openresty.org") @@ -39,9 +38,11 @@ __DATA__ GET /t --- response_body successfully connected to openresty.org ---- no_error +--- no_error_log [error] [crit] +--- error_log +parsed a resolver: " @@ -55,7 +56,7 @@ successfully connected to openresty.org local sock = ngx.socket.tcp() local ok, err = sock:connect("openresty.org", 80) if not ok then - ngx.say("failed to connect to ", server, ": ", err) + ngx.say("failed to connect to openresty.org: ", err) return end ngx.say("successfully connected to openresty.org") @@ -68,10 +69,11 @@ nameser 8.8.8.8" --- request GET /t --- response_body -failed to connect to nil: no resolver defined to resolve "openresty.org" ---- no_error +failed to connect to openresty.org: no resolver defined to resolve "openresty.org" +--- no_error_log [error] [crit] +parsed a resolver: " @@ -85,7 +87,7 @@ failed to connect to nil: no resolver defined to resolve "openresty.org" local sock = ngx.socket.tcp() local ok, err = sock:connect("openresty.org", 80) if not ok then - ngx.say("failed to connect to ", server, ": ", err) + ngx.say("failed to connect to openresty.org: ", err) return end ngx.say("successfully connected to openresty.org") @@ -98,10 +100,11 @@ nameser 8.8.8.8" --- request GET /t --- response_body -failed to connect to nil: no resolver defined to resolve "openresty.org" ---- no_error +failed to connect to openresty.org: no resolver defined to resolve "openresty.org" +--- no_error_log [error] [crit] +parsed a resolver: " @@ -115,7 +118,7 @@ failed to connect to nil: no resolver defined to resolve "openresty.org" local sock = ngx.socket.tcp() local ok, err = sock:connect("openresty.org", 80) if not ok then - ngx.say("failed to connect to ", server, ": ", err) + ngx.say("failed to connect to openresty.org: ", err) return end ngx.say("successfully connected to openresty.org") @@ -131,9 +134,13 @@ nameserver 8.8.4.4" GET /t --- response_body successfully connected to openresty.org ---- no_error +--- no_error_log [error] [crit] +--- grep_error_log eval: qr/parsed a resolver: ".+"/ +--- grep_error_log_out +parsed a resolver: "8.8.8.8" +parsed a resolver: "8.8.4.4" @@ -147,7 +154,7 @@ successfully connected to openresty.org local sock = ngx.socket.tcp() local ok, err = sock:connect("openresty.org", 80) if not ok then - ngx.say("failed to connect to ", server, ": ", err) + ngx.say("failed to connect to openresty.org: ", err) return end ngx.say("successfully connected to openresty.org") @@ -161,9 +168,13 @@ domain example.com\r\nnameserver 8.8.8.8\r\nnameserver 8.8.4.4" GET /t --- response_body successfully connected to openresty.org ---- no_error +--- no_error_log [error] [crit] +--- grep_error_log eval: qr/parsed a resolver: ".+"/ +--- grep_error_log_out +parsed a resolver: "8.8.8.8" +parsed a resolver: "8.8.4.4" @@ -177,7 +188,7 @@ successfully connected to openresty.org local sock = ngx.socket.tcp() local ok, err = sock:connect("openresty.org", 80) if not ok then - ngx.say("failed to connect to ", server, ": ", err) + ngx.say("failed to connect to openresty.org: ", err) return end ngx.say("successfully connected to openresty.org") @@ -191,6 +202,144 @@ successfully connected to openresty.org GET /t --- response_body successfully connected to openresty.org ---- no_error +--- no_error_log [error] [crit] +--- grep_error_log eval: qr/parsed a resolver: ".+"/ +--- grep_error_log_out +parsed a resolver: "8.8.8.8" +parsed a resolver: "8.8.4.4" + + + +=== TEST 7: spaces and tabs before and after nameserver +--- config + resolver local=../html/resolv.conf ipv6=off; + resolver_timeout 5s; + + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("openresty.org", 80) + if not ok then + ngx.say("failed to connect to openresty.org: ", err) + return + end + ngx.say("successfully connected to openresty.org") + sock:close() + } + } +--- user_files eval +">>> resolv.conf +domain example.com + \t \t \tnameserver \t \t 8.8.8.8 + \t \t \tnameserver\t \t8.8.4.4" +--- request +GET /t +--- response_body +successfully connected to openresty.org +--- no_error_log +[error] +[crit] +--- grep_error_log eval: qr/parsed a resolver: ".+"/ +--- grep_error_log_out +parsed a resolver: "8.8.8.8" +parsed a resolver: "8.8.4.4" + + + +=== TEST 8: MAXNS is respected (on standard Glibc it is 3) +--- config + resolver local=../html/resolv.conf ipv6=off; + resolver_timeout 5s; + + location /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("openresty.org", 80) + if not ok then + ngx.say("failed to connect to openresty.org: ", err) + return + end + ngx.say("successfully connected to openresty.org") + sock:close() + } + } +--- user_files eval +">>> resolv.conf +domain example.com +nameserver 8.8.8.8 +nameserver 8.8.4.4 +nameserver 208.67.222.222 +nameserver 208.67.220.220" +--- request +GET /t +--- response_body +successfully connected to openresty.org +--- no_error_log +[error] +[crit] +--- grep_error_log eval: qr/parsed a resolver: ".+"/ +--- grep_error_log_out +parsed a resolver: "8.8.8.8" +parsed a resolver: "8.8.4.4" +parsed a resolver: "208.67.222.222" + + + + +=== TEST 9: IPv6 is supported +--- config + resolver local=../html/resolv.conf ipv6=off; + resolver_timeout 5s; + + location = /t { + content_by_lua_block { + ngx.say("done") + } + } +--- user_files eval +">>> resolv.conf +domain example.com +nameserver 2001:4860:4860::8888 +nameserver 2001:4860:4860::8844" +--- request +GET /t +--- response_body +done +--- no_error_log +[crit] +[error] +--- grep_error_log eval: qr/parsed a resolver: ".+"/ +--- grep_error_log_out +parsed a resolver: "[2001:4860:4860::8888]" +parsed a resolver: "[2001:4860:4860::8844]" + + + +=== TEST 10: IPv6 with malformed and long address +--- config + resolver local=../html/resolv.conf ipv6=off; + resolver_timeout 5s; + + location = /t { + content_by_lua_block { + ngx.say("done") + } + } +--- user_files eval +">>> resolv.conf +domain example.com +nameserver 2001:4860:4860::8888:2001:4860:4860::8888:2001 +nameserver 2001:4860:4860::8844" +--- request +GET /t +--- response_body +done +--- no_error_log +[crit] +[error] +--- must_die +--- error_log +IPv6 resolver address is too long: "2001:4860:4860::8888:2001:4860:4860::8888:2001" +unable to parse local resolver