From 83c85c53f583906e438bb9eb30fc0ab57bf59108 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Sun, 26 Apr 2020 22:28:49 -0600 Subject: caddyhttp: Fix listener overlap detection on Linux Sigh, apparently Linux is incapable of distinguishing host interfaces in socket addresses, even though it works fine on Mac. I suppose we just have to assume that any listeners with the same port are the same address, completely ignoring the host interface on Linux... oh well. --- modules/caddyhttp/server.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'modules/caddyhttp/server.go') diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go index 05b415c..29a1d2b 100644 --- a/modules/caddyhttp/server.go +++ b/modules/caddyhttp/server.go @@ -21,6 +21,7 @@ import ( "net" "net/http" "net/url" + "runtime" "strings" "time" @@ -307,8 +308,30 @@ func (s *Server) hasListenerAddress(fullAddr string) bool { continue } - // host must be the same and port must fall within port range - if (thisAddrs.Host == laddrs.Host) && + // Apparently, Linux requires all bound ports to be distinct + // *regardless of host interface* even if the addresses are + // in fact different; binding "192.168.0.1:9000" and then + // ":9000" will fail for ":9000" because "address is already + // in use" even though it's not, and the same bindings work + // fine on macOS. I also found on Linux that listening on + // "[::]:9000" would fail with a similar error, except with + // the address "0.0.0.0:9000", as if deliberately ignoring + // that I specified the IPv6 interface explicitly. This seems + // to be a major bug in the Linux network stack and I don't + // know why it hasn't been fixed yet, so for now we have to + // special-case ourselves around Linux like a doting parent. + // The second issue seems very similar to a discussion here: + // https://github.com/nodejs/node/issues/9390 + // + // This is very easy to reproduce by creating an HTTP server + // that listens to both addresses or just one with a host + // interface; or for a more confusing reproduction, try + // listening on "127.0.0.1:80" and ":443" and you'll see + // the error, if you take away the GOOS condition below. + // + // So, an address is equivalent if the port is in the port + // range, and if not on Linux, the host is the same... sigh. + if (runtime.GOOS == "linux" || thisAddrs.Host == laddrs.Host) && (laddrs.StartPort <= thisAddrs.EndPort) && (laddrs.StartPort >= thisAddrs.StartPort) { return true -- cgit v1.2.3