You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2019/11/06 06:27:16 UTC

[incubator-apisix] branch master updated: fix: core.utils.parse_addr didn't parse IPv6 address correctly (#809)

This is an automated email from the ASF dual-hosted git repository.

membphis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new b7e2800  fix: core.utils.parse_addr didn't parse IPv6 address correctly (#809)
b7e2800 is described below

commit b7e280033bcdad46ef45656242cec4685161ba30
Author: 罗泽轩 <sp...@gmail.com>
AuthorDate: Wed Nov 6 14:27:08 2019 +0800

    fix: core.utils.parse_addr didn't parse IPv6 address correctly (#809)
---
 lua/apisix/balancer.lua   | 21 +++---------------
 lua/apisix/core/utils.lua | 55 ++++++++++++++++++++++++++++++++++++++++-------
 t/core/utils.t            | 27 +++++++++++++++++++++++
 3 files changed, 77 insertions(+), 26 deletions(-)

diff --git a/lua/apisix/balancer.lua b/lua/apisix/balancer.lua
index 8fa20c7..01cb0f5 100644
--- a/lua/apisix/balancer.lua
+++ b/lua/apisix/balancer.lua
@@ -19,13 +19,10 @@ local roundrobin  = require("resty.roundrobin")
 local resty_chash = require("resty.chash")
 local balancer    = require("ngx.balancer")
 local core        = require("apisix.core")
-local sub_str     = string.sub
-local find_str    = string.find
 local error       = error
 local str_char    = string.char
 local str_gsub    = string.gsub
 local pairs       = pairs
-local tonumber    = tonumber
 local tostring    = tostring
 local set_more_tries   = balancer.set_more_tries
 local get_last_failure = balancer.get_last_failure
@@ -50,18 +47,6 @@ local _M = {
 }
 
 
-local function parse_addr(addr)
-    local pos = find_str(addr, ":", 1, true)
-    if not pos then
-        return addr, 80
-    end
-
-    local host = sub_str(addr, 1, pos - 1)
-    local port = sub_str(addr, pos + 1)
-    return host, tonumber(port)
-end
-
-
 local function fetch_health_nodes(upstream, checker)
     if not checker then
         return upstream.nodes
@@ -71,7 +56,7 @@ local function fetch_health_nodes(upstream, checker)
     local up_nodes = core.table.new(0, #upstream.nodes)
 
     for addr, weight in pairs(upstream.nodes) do
-        local ip, port = parse_addr(addr)
+        local ip, port = core.utils.parse_addr(addr)
         local ok = checker:get_target_status(ip, port, host)
         if ok then
             up_nodes[addr] = weight
@@ -95,7 +80,7 @@ local function create_checker(upstream, healthcheck_parent)
     })
 
     for addr, weight in pairs(upstream.nodes) do
-        local ip, port = parse_addr(addr)
+        local ip, port = core.utils.parse_addr(addr)
         local ok, err = checker:add_target(ip, port, upstream.checks.host)
         if not ok then
             core.log.error("failed to add new health check target: ", addr,
@@ -267,7 +252,7 @@ local function pick_server(route, ctx)
         end
     end
 
-    local ip, port, err = parse_addr(server)
+    local ip, port, err = core.utils.parse_addr(server)
     ctx.balancer_ip = ip
     ctx.balancer_port = port
 
diff --git a/lua/apisix/core/utils.lua b/lua/apisix/core/utils.lua
index 8f68a14..3a5bd36 100644
--- a/lua/apisix/core/utils.lua
+++ b/lua/apisix/core/utils.lua
@@ -21,7 +21,7 @@ local ipmatcher= require("resty.ipmatcher")
 local open     = io.open
 local math     = math
 local sub_str  = string.sub
-local find_str = string.find
+local str_byte = string.byte
 local tonumber = tonumber
 
 
@@ -83,15 +83,54 @@ function _M.dns_parse(resolvers, domain)
 end
 
 
-function _M.parse_addr(addr)
-    local pos = find_str(addr, ":", 1, true)
-    if not pos then
-        return addr, 80
+local function rfind_char(s, ch, idx)
+    local b = str_byte(ch)
+    for i = idx or #s, 1, -1 do
+        if str_byte(s, i, i) == b then
+            return i
+        end
     end
+    return nil
+end
+
 
-    local host = sub_str(addr, 1, pos - 1)
-    local port = sub_str(addr, pos + 1)
-    return host, tonumber(port)
+-- parse_addr parses 'addr' into the host and the port parts. If the 'addr'
+-- doesn't have a port, 80 is used to return. For malformed 'addr', the entire
+-- 'addr' is returned as the host part. For IPv6 literal host, like [::1], the
+-- square brackets will be removed.
+function _M.parse_addr(addr)
+    local default_port = 80
+    if str_byte(addr, 1) == str_byte("[") then
+        -- IPv6 format
+        local right_bracket = str_byte("]")
+        local len = #addr
+        if str_byte(addr, len) == right_bracket then
+            -- addr in [ip:v6] format
+            return sub_str(addr, 2, len-1), default_port
+        else
+            local pos = rfind_char(addr, ":", #addr - 1)
+            if not pos or str_byte(addr, pos - 1) ~= right_bracket then
+                -- malformed addr
+                return addr, default_port
+            end
+
+            -- addr in [ip:v6]:port format
+            local host = sub_str(addr, 2, pos - 2)
+            local port = sub_str(addr, pos + 1)
+            return host, tonumber(port)
+        end
+
+    else
+        -- IPv4 format
+        local pos = rfind_char(addr, ":", #addr - 1)
+        if not pos then
+            return addr, default_port
+        end
+
+        local host = sub_str(addr, 1, pos - 1)
+        local port = sub_str(addr, pos + 1)
+        return host, tonumber(port)
+    end
 end
 
 
diff --git a/t/core/utils.t b/t/core/utils.t
index d4c3666..92e67bb 100644
--- a/t/core/utils.t
+++ b/t/core/utils.t
@@ -39,3 +39,30 @@ __DATA__
 GET /t
 --- response_body_like eval
 qr/random seed \d+\ntwice: false/
+
+
+
+=== TEST 2: parse_addr
+--- config
+    location /t {
+        content_by_lua_block {
+            local parse_addr = require("apisix.core.utils").parse_addr
+            local cases = {
+                {addr = "127.0.0.1", host = "127.0.0.1", port = 80},
+                {addr = "127.0.0.1:90", host = "127.0.0.1", port = 90},
+                {addr = "www.test.com", host = "www.test.com", port = 80},
+                {addr = "www.test.com:90", host = "www.test.com", port = 90},
+                {addr = "[127.0.0.1:90", host = "[127.0.0.1:90", port = 80},
+                {addr = "[::1]", host = "::1", port = 80},
+                {addr = "[::1]:1234", host = "::1", port = 1234},
+                {addr = "[::1234:1234]:12345", host = "::1234:1234", port = 12345},
+            }
+            for _, case in ipairs(cases) do
+                local host, port = parse_addr(case.addr)
+                assert(host == case.host, string.format("host %s mismatch %s", host, case.host))
+                assert(port == case.port, string.format("port %s mismatch %s", port, case.port))
+            end
+        }
+    }
+--- request
+GET /t