From 748cedfd756d7d5524df3cf0ef33073d9e45b714 Mon Sep 17 00:00:00 2001 From: spacewander Date: Fri, 23 Apr 2021 10:40:34 +0800 Subject: [PATCH] feat: patch tcp.sock.connect to use our DNS resolver Fix #3719 Signed-off-by: spacewander --- apisix/core/utils.lua | 31 ++++++++++++++++++++++- apisix/init.lua | 30 ++--------------------- apisix/patch.lua | 42 +++++++++++++++++++++++++++++++- apisix/plugins/traffic-split.lua | 3 +-- t/misc/patch.t | 25 +++++++++++++++++++ 5 files changed, 99 insertions(+), 32 deletions(-) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 921df6ae9831d..92dad3bcf4c50 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -16,6 +16,7 @@ -- local config_local = require("apisix.core.config_local") local core_str = require("apisix.core.string") +local json = require("apisix.core.json") local table = require("apisix.core.table") local log = require("apisix.core.log") local string = require("apisix.core.string") @@ -42,6 +43,7 @@ local hostname local dns_resolvers local current_inited_resolvers local current_dns_client +local dns_resolver local max_sleep_interval = 1 ffi.cdef[[ @@ -114,9 +116,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) @@ -124,6 +127,32 @@ function _M.get_resolver(resolvers) end +function _M.parse_args(args) + dns_resolver = args and args["dns_resolver"] + set_resolver(dns_resolver) + log.info("dns resolver", json.delay_encode(dns_resolver, true)) +end + + +function _M.parse_domain(host) + local ip_info, err = 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(dns_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 + + local function rfind_char(s, ch, idx) local b = str_byte(ch) for i = idx or #s, 1, -1 do diff --git a/apisix/init.lua b/apisix/init.lua index d71fd70c0aced..95f42a6eeffe9 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.utils.parse_args(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.utils.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 6bea36301aa3c..33ae443517a8c 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 utils = require("apisix.core.utils") + + 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 = utils.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 5364438e7ab3c..835ce1777ecb0 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.utils.parse_domain(node) if ip then return ip end diff --git a/t/misc/patch.t b/t/misc/patch.t index b94d5f5af0b97..16357153f22c9 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