From 22ef69825f63d5c1534b14293181ed000f906789 Mon Sep 17 00:00:00 2001 From: spacewander Date: Fri, 23 Apr 2021 10:40:34 +0800 Subject: [PATCH 1/3] feat: patch tcp.sock.connect to use our DNS resolver Fix #3719 Signed-off-by: spacewander --- apisix/core.lua | 1 + apisix/core/resolver.lua | 51 ++++++++++++++++++++++++++++ apisix/core/utils.lua | 3 +- apisix/init.lua | 30 ++-------------- apisix/patch.lua | 42 ++++++++++++++++++++++- apisix/plugins/traffic-split.lua | 3 +- t/misc/patch.t | 25 ++++++++++++++ t/node/route-domain-with-local-dns.t | 4 +-- 8 files changed, 125 insertions(+), 34 deletions(-) create mode 100644 apisix/core/resolver.lua diff --git a/apisix/core.lua b/apisix/core.lua index b0c3da6e908b..0ef6ae9bb259 100644 --- a/apisix/core.lua +++ b/apisix/core.lua @@ -48,5 +48,6 @@ return { dns_client = require("apisix.core.dns.client"), etcd = require("apisix.core.etcd"), tablepool = require("tablepool"), + resolver = require("apisix.core.resolver"), empty_tab = {}, } diff --git a/apisix/core/resolver.lua b/apisix/core/resolver.lua new file mode 100644 index 000000000000..f2625f1eaa33 --- /dev/null +++ b/apisix/core/resolver.lua @@ -0,0 +1,51 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local json = require("apisix.core.json") +local log = require("apisix.core.log") +local utils = require("apisix.core.utils") + + +local _M = {} + + +function _M.init_resolver(args) + local dns_resolver = args and args["dns_resolver"] + utils.set_resolver(dns_resolver) + log.info("dns resolver", json.delay_encode(dns_resolver, true)) +end + + +function _M.parse_domain(host) + local ip_info, err = utils.dns_parse(host) + if not ip_info then + log.error("failed to parse domain: ", host, ", error: ",err) + return nil, err + end + + log.info("parse addr: ", json.delay_encode(ip_info)) + log.info("resolver: ", json.delay_encode(utils.get_resolver())) + log.info("host: ", host) + if ip_info.address then + log.info("dns resolver domain: ", host, " to ", ip_info.address) + return ip_info.address + end + + return nil, "failed to parse domain" +end + + +return _M diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 921df6ae9831..1b304f726ae6 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -114,9 +114,10 @@ end _M.dns_parse = dns_parse -function _M.set_resolver(resolvers) +local function set_resolver(resolvers) dns_resolvers = resolvers end +_M.set_resolver = set_resolver function _M.get_resolver(resolvers) diff --git a/apisix/init.lua b/apisix/init.lua index d71fd70c0ace..38a84608d8e1 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -46,17 +46,9 @@ if ngx.config.subsystem == "http" then end local load_balancer local local_conf -local dns_resolver local ver_header = "APISIX/" .. core.version.VERSION -local function parse_args(args) - dns_resolver = args and args["dns_resolver"] - core.utils.set_resolver(dns_resolver) - core.log.info("dns resolver", core.json.delay_encode(dns_resolver, true)) -end - - local _M = {version = 0.4} @@ -71,7 +63,7 @@ function _M.http_init(args) "maxrecord=8000", "sizemcode=64", "maxmcode=4000", "maxirconst=1000") - parse_args(args) + core.resolver.init_resolver(args) core.id.init() local process = require("ngx.process") @@ -154,24 +146,6 @@ function _M.http_ssl_phase() end -local function parse_domain(host) - local ip_info, err = core.utils.dns_parse(host) - if not ip_info then - core.log.error("failed to parse domain: ", host, ", error: ",err) - return nil, err - end - - core.log.info("parse addr: ", core.json.delay_encode(ip_info)) - core.log.info("resolver: ", core.json.delay_encode(dns_resolver)) - core.log.info("host: ", host) - if ip_info.address then - core.log.info("dns resolver domain: ", host, " to ", ip_info.address) - return ip_info.address - else - return nil, "failed to parse domain" - end -end -_M.parse_domain = parse_domain local function parse_domain_for_nodes(nodes) @@ -180,7 +154,7 @@ local function parse_domain_for_nodes(nodes) local host = node.host if not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then - local ip, err = parse_domain(host) + local ip, err = core.resolver.parse_domain(host) if ip then local new_node = core.table.clone(node) new_node.host = ip diff --git a/apisix/patch.lua b/apisix/patch.lua index 6bea36301aa3..66bf17d3e2cf 100644 --- a/apisix/patch.lua +++ b/apisix/patch.lua @@ -15,6 +15,7 @@ -- limitations under the License. -- local require = require +local ipmatcher = require("resty.ipmatcher") local socket = require("socket") local unix_socket = require("socket.unix") local ssl = require("ssl") @@ -45,6 +46,45 @@ local function get_local_conf() end +local patch_tcp_socket +do + local old_tcp_sock_connect + + local function new_tcp_sock_connect(sock, host, port, opts) + local core_str = require("apisix.core.string") + local resolver = require("apisix.core.resolver") + + if host then + if core_str.has_prefix(host, "unix:") then + if not opts then + -- workaround for https://github.com/openresty/lua-nginx-module/issues/860 + return old_tcp_sock_connect(sock, host) + end + + elseif not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then + local err + host, err = resolver.parse_domain(host) + if not host then + return nil, "failed to parse domain: " .. err + end + end + end + + return old_tcp_sock_connect(sock, host, port, opts) + end + + + function patch_tcp_socket(sock) + if not old_tcp_sock_connect then + old_tcp_sock_connect = sock.connect + end + + sock.connect = new_tcp_sock_connect + return sock + end +end + + local function flatten(args) local buf = new_tab(#args, 0) for i, v in ipairs(args) do @@ -255,7 +295,7 @@ function _M.patch() ngx_socket.tcp = function () local phase = get_phase() if phase ~= "init" and phase ~= "init_worker" then - return original_tcp() + return patch_tcp_socket(original_tcp()) end return luasocket_tcp() diff --git a/apisix/plugins/traffic-split.lua b/apisix/plugins/traffic-split.lua index 5364438e7ab3..6d2346714e58 100644 --- a/apisix/plugins/traffic-split.lua +++ b/apisix/plugins/traffic-split.lua @@ -17,7 +17,6 @@ local core = require("apisix.core") local upstream = require("apisix.upstream") local schema_def = require("apisix.schema_def") -local init = require("apisix.init") local roundrobin = require("resty.roundrobin") local ipmatcher = require("resty.ipmatcher") local expr = require("resty.expr.v1") @@ -130,7 +129,7 @@ local function parse_domain_for_node(node) if not ipmatcher.parse_ipv4(node) and not ipmatcher.parse_ipv6(node) then - local ip, err = init.parse_domain(node) + local ip, err = core.resolver.parse_domain(node) if ip then return ip end diff --git a/t/misc/patch.t b/t/misc/patch.t index b94d5f5af0b9..16357153f22c 100644 --- a/t/misc/patch.t +++ b/t/misc/patch.t @@ -126,3 +126,28 @@ end } --- error_log failed to read: timeout + + + +=== TEST 4: resolve host by ourselves +--- yaml_config +apisix: + node_listen: 1984 + enable_resolv_search_opt: true +--- config + location /t { + content_by_lua_block { + local http = require("resty.http") + local httpc = http.new() + local res, err = httpc:request_uri("http://apisix") + if not res then + ngx.log(ngx.ERR, err) + return + end + ngx.say(res.status) + } + } +--- request +GET /t +--- response_body +301 diff --git a/t/node/route-domain-with-local-dns.t b/t/node/route-domain-with-local-dns.t index d79dc2e0e28e..c7e041fcf8c2 100644 --- a/t/node/route-domain-with-local-dns.t +++ b/t/node/route-domain-with-local-dns.t @@ -62,7 +62,7 @@ GET /t --- response_body passed --- error_log eval -qr/.*parse_args\(\): dns resolver\[.+\]/ +qr/.*init_resolver\(\): dns resolver\[.+\]/ --- no_error_log [error] @@ -75,7 +75,7 @@ GET /not_found --- response_body {"error_msg":"404 Route Not Found"} --- error_log eval -qr/.*parse_args\(\): dns resolver\[.+\]/ +qr/.*init_resolver\(\): dns resolver\[.+\]/ --- no_error_log [error] From 37cf380b63f2998f034b98a709188598f7c910e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?= Date: Fri, 23 Apr 2021 17:56:24 +0800 Subject: [PATCH 2/3] Update apisix/core/resolver.lua Co-authored-by: Alex Zhang --- apisix/core/resolver.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/core/resolver.lua b/apisix/core/resolver.lua index f2625f1eaa33..d6b76cdd4694 100644 --- a/apisix/core/resolver.lua +++ b/apisix/core/resolver.lua @@ -25,7 +25,7 @@ local _M = {} function _M.init_resolver(args) local dns_resolver = args and args["dns_resolver"] utils.set_resolver(dns_resolver) - log.info("dns resolver", json.delay_encode(dns_resolver, true)) + log.info("dns resolver ", json.delay_encode(dns_resolver, true)) end From dab000865b2ca9aab07686b41a079412b277bb10 Mon Sep 17 00:00:00 2001 From: spacewander Date: Fri, 23 Apr 2021 18:25:14 +0800 Subject: [PATCH 3/3] fix test Signed-off-by: spacewander --- t/node/route-domain-with-local-dns.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/node/route-domain-with-local-dns.t b/t/node/route-domain-with-local-dns.t index c7e041fcf8c2..0ab68efe4fc9 100644 --- a/t/node/route-domain-with-local-dns.t +++ b/t/node/route-domain-with-local-dns.t @@ -62,7 +62,7 @@ GET /t --- response_body passed --- error_log eval -qr/.*init_resolver\(\): dns resolver\[.+\]/ +qr/.*init_resolver\(\): dns resolver \[.+\]/ --- no_error_log [error] @@ -75,7 +75,7 @@ GET /not_found --- response_body {"error_msg":"404 Route Not Found"} --- error_log eval -qr/.*init_resolver\(\): dns resolver\[.+\]/ +qr/.*init_resolver\(\): dns resolver \[.+\]/ --- no_error_log [error]