@marcel91200 / caddy Mirror · read-only / commits / 83c85c53
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

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.

Matthew Holt committed Apr 26, 2020 at 22:28 UTC 83c85c53f583906e438bb9eb30fc0ab57bf59108
1 file changed +25 -2
modules/caddyhttp/server.go
+25 -2
@@ -21,6 +21,7 @@ import (
21 "net"
22 "net/http"
23 "net/url"
24 + "runtime"
25 "strings"
26 "time"
27
@@ -307,8 +308,30 @@ func (s *Server) hasListenerAddress(fullAddr string) bool {
308 continue
309 }
310
310 - // host must be the same and port must fall within port range
311 - if (thisAddrs.Host == laddrs.Host) &&
311 + // Apparently, Linux requires all bound ports to be distinct
312 + // *regardless of host interface* even if the addresses are
313 + // in fact different; binding "192.168.0.1:9000" and then
314 + // ":9000" will fail for ":9000" because "address is already
315 + // in use" even though it's not, and the same bindings work
316 + // fine on macOS. I also found on Linux that listening on
317 + // "[::]:9000" would fail with a similar error, except with
318 + // the address "0.0.0.0:9000", as if deliberately ignoring
319 + // that I specified the IPv6 interface explicitly. This seems
320 + // to be a major bug in the Linux network stack and I don't
321 + // know why it hasn't been fixed yet, so for now we have to
322 + // special-case ourselves around Linux like a doting parent.
323 + // The second issue seems very similar to a discussion here:
324 + // https://github.com/nodejs/node/issues/9390
325 + //
326 + // This is very easy to reproduce by creating an HTTP server
327 + // that listens to both addresses or just one with a host
328 + // interface; or for a more confusing reproduction, try
329 + // listening on "127.0.0.1:80" and ":443" and you'll see
330 + // the error, if you take away the GOOS condition below.
331 + //
332 + // So, an address is equivalent if the port is in the port
333 + // range, and if not on Linux, the host is the same... sigh.
334 + if (runtime.GOOS == "linux" || thisAddrs.Host == laddrs.Host) &&
335 (laddrs.StartPort <= thisAddrs.EndPort) &&
336 (laddrs.StartPort >= thisAddrs.StartPort) {
337 return true