From c6757075d071c9b0b1d032007e774b3020ea1b09 Mon Sep 17 00:00:00 2001 From: David Scott Date: Thu, 8 Sep 2016 13:23:32 +0100 Subject: [PATCH] proxy: don't fail if the Listen in the VM fails with EADDRNOTAVAIL The purpose of the `slirp-proxy` is to expose ports on the Mac or Windows host. In d5bd7d690a03b37ed99e73237415b3dbd9b3c595 we added an additional `Listen` inside the VM for backwards compatibility with software that expected to be able to listen on `0.0.0.0` in one container and then access this easily from other containers using an IP bound to the VM (instead of using a first-class network to connect the containers or discovering a real IP of the host). Before this patch we could only expose ports on if the Listen succeeds on both the host and the VM. In practice this meant that we could only expose ports on `0.0.0.0` and `127.0.0.1`; attempts to expose ports on specific interfaces on the host would fail. This patch treats the EADDRNOTAVAIL error from the Listen inside the VM as a soft failure, and still attempts to Listen on the host. If the Listen on the host fails it is still a hard failure. This allows ports to be exposed on specific IPs used on the host. Fixes [docker/pinata#5080] Signed-off-by: David Scott --- alpine/packages/proxy/one.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/alpine/packages/proxy/one.go b/alpine/packages/proxy/one.go index e91aa30af..ff8e0ab38 100644 --- a/alpine/packages/proxy/one.go +++ b/alpine/packages/proxy/one.go @@ -8,6 +8,7 @@ import ( "os" "proxy/libproxy" "strings" + "syscall" ) func onePort() { @@ -17,7 +18,7 @@ func onePort() { var err error if localIP { - ipP, err = libproxy.NewIPProxy(host, container) + ipP, err = listenInVM(host, container) if err != nil { sendError(err) } @@ -40,6 +41,27 @@ func onePort() { os.Exit(0) } +// Best-effort attempt to listen on the address in the VM. This is for +// backwards compatibility with software that expects to be able to listen on +// 0.0.0.0 and then connect from within a container to the external port. +// If the address doesn't exist in the VM (i.e. it exists only on the host) +// then this is not a hard failure. +func listenInVM(host net.Addr, container net.Addr) (libproxy.Proxy, error) { + ipP, err := libproxy.NewIPProxy(host, container) + if err == nil { + return ipP, nil + } + if opError, ok := err.(*net.OpError); ok { + if syscallError, ok := opError.Err.(*os.SyscallError); ok { + if syscallError.Err == syscall.EADDRNOTAVAIL { + log.Printf("Address %s doesn't exist in the VM: only binding on the host", host) + return nil, nil // Non-fatal error + } + } + } + return nil, err +} + func exposePort(host net.Addr, container net.Addr) (*os.File, error) { name := host.Network() + ":" + host.String() + ":" + container.Network() + ":" + container.String() log.Printf("exposePort %s\n", name)