From a1ad20e4720391d847c58fcdcef7c8d9c287faf2 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 15 Sep 2022 16:03:24 +0200 Subject: [PATCH] httpcaddyfile: Fix bind when IPv6 is specified with network (#4950) * fix listening on IPv6 addresses: use net.JoinHostPort Commit 1e18afb5c862d62be130d563785de5c58f08ae8e broke my caddy setup. This commit fixes it. * Refactor solution; simplify, add descriptive comment * Move network to host, not copy Co-authored-by: Matthew Holt --- caddyconfig/httpcaddyfile/addresses.go | 36 +++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/caddyconfig/httpcaddyfile/addresses.go b/caddyconfig/httpcaddyfile/addresses.go index e7a7cdb0..93bad27d 100644 --- a/caddyconfig/httpcaddyfile/addresses.go +++ b/caddyconfig/httpcaddyfile/addresses.go @@ -36,12 +36,12 @@ import ( // server block that share the same address stay grouped together so the config // isn't repeated unnecessarily. For example, this Caddyfile: // -// example.com { -// bind 127.0.0.1 -// } -// www.example.com, example.net/path, localhost:9999 { -// bind 127.0.0.1 1.2.3.4 -// } +// example.com { +// bind 127.0.0.1 +// } +// www.example.com, example.net/path, localhost:9999 { +// bind 127.0.0.1 1.2.3.4 +// } // // has two server blocks to start with. But expressed in this Caddyfile are // actually 4 listener addresses: 127.0.0.1:443, 1.2.3.4:443, 127.0.0.1:9999, @@ -219,7 +219,7 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str return nil, fmt.Errorf("[%s] scheme and port violate convention", key) } - // the bind directive specifies hosts, but is optional + // the bind directive specifies hosts (and potentially network), but is optional lnHosts := make([]string, 0, len(sblock.pile["bind"])) for _, cfgVal := range sblock.pile["bind"] { lnHosts = append(lnHosts, cfgVal.Value.([]string)...) @@ -234,11 +234,23 @@ func (st *ServerType) listenerAddrsForServerBlockKey(sblock serverBlock, key str // use a map to prevent duplication listeners := make(map[string]struct{}) - for _, host := range lnHosts { - // host can have network + host (e.g. "tcp6/localhost") but - // will/should not have port information because this usually - // comes from the bind directive, so we append the port - addr, err := caddy.ParseNetworkAddress(host + ":" + lnPort) + for _, lnHost := range lnHosts { + // normally we would simply append the port, + // but if lnHost is IPv6, we need to ensure it + // is enclosed in [ ]; net.JoinHostPort does + // this for us, but lnHost might also have a + // network type in front (e.g. "tcp/") leading + // to "[tcp/::1]" which causes parsing failures + // later; what we need is "tcp/[::1]", so we have + // to split the network and host, then re-combine + network, host, ok := strings.Cut(lnHost, "/") + if !ok { + host = network + network = "" + } + host = strings.Trim(host, "[]") // IPv6 + networkAddr := caddy.JoinNetworkAddress(network, host, lnPort) + addr, err := caddy.ParseNetworkAddress(networkAddr) if err != nil { return nil, fmt.Errorf("parsing network address: %v", err) }