diff --git a/hack/.vendor-helpers.sh b/hack/.vendor-helpers.sh index 94ed5cc5bb..82fe7b3b38 100755 --- a/hack/.vendor-helpers.sh +++ b/hack/.vendor-helpers.sh @@ -131,6 +131,9 @@ clean() { findArgs+=( -path "vendor/src/$import" ) done + # The docker proxy command is built from libnetwork + findArgs+=( -or -path vendor/src/github.com/docker/libnetwork/cmd/proxy ) + local IFS=$'\n' local prune=( $($find vendor -depth -type d -not '(' "${findArgs[@]}" ')') ) unset IFS diff --git a/hack/make/.binary-setup b/hack/make/.binary-setup index 90c8a336ea..e388c64485 100644 --- a/hack/make/.binary-setup +++ b/hack/make/.binary-setup @@ -2,3 +2,4 @@ DOCKER_CLIENT_BINARY_NAME='docker' DOCKER_DAEMON_BINARY_NAME='dockerd' +DOCKER_PROXY_BINARY_NAME='docker-proxy' diff --git a/hack/make/.build-deb/rules b/hack/make/.build-deb/rules index 2cd86691d3..2a4d6176ea 100755 --- a/hack/make/.build-deb/rules +++ b/hack/make/.build-deb/rules @@ -22,6 +22,7 @@ override_dh_auto_install: mkdir -p debian/docker-engine/usr/bin cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-client/docker)" debian/docker-engine/usr/bin/docker cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-daemon/dockerd)" debian/docker-engine/usr/bin/dockerd + cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-daemon/docker-proxy)" debian/docker-engine/usr/bin/docker-proxy cp -aT /usr/local/bin/containerd debian/docker-engine/usr/bin/docker-containerd cp -aT /usr/local/bin/containerd-shim debian/docker-engine/usr/bin/docker-containerd-shim cp -aT /usr/local/bin/ctr debian/docker-engine/usr/bin/docker-containerd-ctr diff --git a/hack/make/.build-rpm/docker-engine.spec b/hack/make/.build-rpm/docker-engine.spec index a433d54bc7..d3ae77f404 100644 --- a/hack/make/.build-rpm/docker-engine.spec +++ b/hack/make/.build-rpm/docker-engine.spec @@ -126,6 +126,7 @@ export DOCKER_GITCOMMIT=%{_gitcommit} install -d $RPM_BUILD_ROOT/%{_bindir} install -p -m 755 bundles/%{_origversion}/dynbinary-client/docker-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker install -p -m 755 bundles/%{_origversion}/dynbinary-daemon/dockerd-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/dockerd +install -p -m 755 bundles/%{_origversion}/dynbinary-daemon/docker-proxy-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker-proxy # install containerd install -p -m 755 /usr/local/bin/containerd $RPM_BUILD_ROOT/%{_bindir}/docker-containerd diff --git a/hack/make/binary-daemon b/hack/make/binary-daemon index bf2fb7b2ca..e75c44c587 100644 --- a/hack/make/binary-daemon +++ b/hack/make/binary-daemon @@ -9,5 +9,8 @@ set -e export BINARY_SHORT_NAME="$DOCKER_DAEMON_BINARY_NAME" export SOURCE_PATH='./cmd/dockerd' source "${MAKEDIR}/.binary" + export BINARY_SHORT_NAME="$DOCKER_PROXY_BINARY_NAME" + export SOURCE_PATH='./vendor/src/github.com/docker/libnetwork/cmd/proxy' + source "${MAKEDIR}/.binary" copy_containerd "$DEST" 'hash' ) diff --git a/hack/make/dynbinary-daemon b/hack/make/dynbinary-daemon index 398b951d85..cd7937859c 100644 --- a/hack/make/dynbinary-daemon +++ b/hack/make/dynbinary-daemon @@ -9,4 +9,7 @@ set -e export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here source "${MAKEDIR}/.binary" + export BINARY_SHORT_NAME='docker-proxy' + export SOURCE_PATH='./vendor/src/github.com/docker/libnetwork/cmd/proxy' + source "${MAKEDIR}/.binary" ) diff --git a/hack/make/gccgo b/hack/make/gccgo index 93e064a879..54c983eb2e 100644 --- a/hack/make/gccgo +++ b/hack/make/gccgo @@ -5,6 +5,9 @@ BINARY_NAME="dockerd-$VERSION" BINARY_EXTENSION="$(binary_extension)" BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION" +PROXY_NAME="docker-proxy-$VERSION" +PROXY_FULLNAME="$PROXY_NAME$BINARY_EXTENSION" + CLIENTBIN_NAME="docker-$VERSION" CLIENTBIN_FULLNAME="$CLIENTBIN_NAME$BINARY_EXTENSION" @@ -29,6 +32,21 @@ go build -compiler=gccgo \ echo "Created binary: $DEST/$BINARY_FULLNAME" ln -sf "$BINARY_FULLNAME" "$DEST/dockerd$BINARY_EXTENSION" +go build -compiler=gccgo \ + -o "$DEST/$PROXY_FULLNAME" \ + "${BUILDFLAGS[@]}" \ + -gccgoflags " + -g + $EXTLDFLAGS_STATIC + -Wl,--no-export-dynamic + -ldl + -pthread + " \ + ./vendor/src/github.com/docker/libnetwork/cmd/proxy + +echo "Created binary: $DEST/$PROXY_FULLNAME" +ln -sf "$PROXY_FULLNAME" "$DEST/docker-proxy$BINARY_EXTENSION" + copy_containerd "$DEST" "hash" hash_files "$DEST/$BINARY_FULLNAME" diff --git a/hack/make/install-binary-daemon b/hack/make/install-binary-daemon index 36b6c5e95a..e80d8431fd 100644 --- a/hack/make/install-binary-daemon +++ b/hack/make/install-binary-daemon @@ -7,4 +7,5 @@ rm -rf "$DEST" DEST="$(dirname $DEST)/binary-daemon" source "${MAKEDIR}/.binary-setup" install_binary "${DEST}/${DOCKER_DAEMON_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_PROXY_BINARY_NAME}" ) diff --git a/hack/make/tgz b/hack/make/tgz index 5b1663256b..b1abfef92f 100644 --- a/hack/make/tgz +++ b/hack/make/tgz @@ -18,6 +18,7 @@ for d in "$CROSS/"*/*; do BINARY_NAME="${DOCKER_CLIENT_BINARY_NAME}-$VERSION" DAEMON_BINARY_NAME="${DOCKER_DAEMON_BINARY_NAME}-$VERSION" + PROXY_BINARY_NAME="${DOCKER_PROXY_BINARY_NAME}-$VERSION" BINARY_EXTENSION="$(export GOOS && binary_extension)" if [ "$GOOS" = 'windows' ]; then # if windows use a zip, not tgz @@ -29,6 +30,7 @@ for d in "$CROSS/"*/*; do fi BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION" DAEMON_BINARY_FULLNAME="$DAEMON_BINARY_NAME$BINARY_EXTENSION" + PROXY_BINARY_FULLNAME="$PROXY_BINARY_NAME$BINARY_EXTENSION" mkdir -p "$DEST/$GOOS/$GOARCH" TGZ="$DEST/$GOOS/$GOARCH/$BINARY_NAME$BUNDLE_EXTENSION" @@ -47,6 +49,9 @@ for d in "$CROSS/"*/*; do if [ -f "$d/$DAEMON_BINARY_FULLNAME" ]; then cp -L "$d/$DAEMON_BINARY_FULLNAME" "$TAR_PATH/${DOCKER_DAEMON_BINARY_NAME}${BINARY_EXTENSION}" fi + if [ -f "$d/$PROXY_BINARY_FULLNAME" ]; then + cp -L "$d/$PROXY_BINARY_FULLNAME" "$TAR_PATH/${DOCKER_PROXY_BINARY_NAME}${BINARY_EXTENSION}" + fi # copy over all the containerd binaries copy_containerd $TAR_PATH diff --git a/pkg/proxy/network_proxy_test.go b/pkg/proxy/network_proxy_test.go deleted file mode 100644 index 9a73548019..0000000000 --- a/pkg/proxy/network_proxy_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package proxy - -import ( - "bytes" - "fmt" - "io" - "net" - "strings" - "testing" - "time" -) - -var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo") -var testBufSize = len(testBuf) - -type EchoServer interface { - Run() - Close() - LocalAddr() net.Addr -} - -type TCPEchoServer struct { - listener net.Listener - testCtx *testing.T -} - -type UDPEchoServer struct { - conn net.PacketConn - testCtx *testing.T -} - -func NewEchoServer(t *testing.T, proto, address string) EchoServer { - var server EchoServer - if strings.HasPrefix(proto, "tcp") { - listener, err := net.Listen(proto, address) - if err != nil { - t.Fatal(err) - } - server = &TCPEchoServer{listener: listener, testCtx: t} - } else { - socket, err := net.ListenPacket(proto, address) - if err != nil { - t.Fatal(err) - } - server = &UDPEchoServer{conn: socket, testCtx: t} - } - return server -} - -func (server *TCPEchoServer) Run() { - go func() { - for { - client, err := server.listener.Accept() - if err != nil { - return - } - go func(client net.Conn) { - if _, err := io.Copy(client, client); err != nil { - server.testCtx.Logf("can't echo to the client: %v\n", err.Error()) - } - client.Close() - }(client) - } - }() -} - -func (server *TCPEchoServer) LocalAddr() net.Addr { return server.listener.Addr() } -func (server *TCPEchoServer) Close() { server.listener.Close() } - -func (server *UDPEchoServer) Run() { - go func() { - readBuf := make([]byte, 1024) - for { - read, from, err := server.conn.ReadFrom(readBuf) - if err != nil { - return - } - for i := 0; i != read; { - written, err := server.conn.WriteTo(readBuf[i:read], from) - if err != nil { - break - } - i += written - } - } - }() -} - -func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() } -func (server *UDPEchoServer) Close() { server.conn.Close() } - -func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) { - defer proxy.Close() - go proxy.Run() - client, err := net.Dial(proto, addr) - if err != nil { - t.Fatalf("Can't connect to the proxy: %v", err) - } - defer client.Close() - client.SetDeadline(time.Now().Add(10 * time.Second)) - if _, err = client.Write(testBuf); err != nil { - t.Fatal(err) - } - recvBuf := make([]byte, testBufSize) - if _, err = client.Read(recvBuf); err != nil { - t.Fatal(err) - } - if !bytes.Equal(testBuf, recvBuf) { - t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) - } -} - -func testProxy(t *testing.T, proto string, proxy Proxy) { - testProxyAt(t, proto, proxy, proxy.FrontendAddr().String()) -} - -func TestTCP4Proxy(t *testing.T) { - backend := NewEchoServer(t, "tcp", "127.0.0.1:0") - defer backend.Close() - backend.Run() - frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} - proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) - if err != nil { - t.Fatal(err) - } - testProxy(t, "tcp", proxy) -} - -func TestTCP6Proxy(t *testing.T) { - backend := NewEchoServer(t, "tcp", "[::1]:0") - defer backend.Close() - backend.Run() - frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} - proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) - if err != nil { - t.Fatal(err) - } - testProxy(t, "tcp", proxy) -} - -func TestTCPDualStackProxy(t *testing.T) { - // If I understand `godoc -src net favoriteAddrFamily` (used by the - // net.Listen* functions) correctly this should work, but it doesn't. - t.Skip("No support for dual stack yet") - backend := NewEchoServer(t, "tcp", "[::1]:0") - defer backend.Close() - backend.Run() - frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} - proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) - if err != nil { - t.Fatal(err) - } - ipv4ProxyAddr := &net.TCPAddr{ - IP: net.IPv4(127, 0, 0, 1), - Port: proxy.FrontendAddr().(*net.TCPAddr).Port, - } - testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String()) -} - -func TestUDP4Proxy(t *testing.T) { - backend := NewEchoServer(t, "udp", "127.0.0.1:0") - defer backend.Close() - backend.Run() - frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} - proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) - if err != nil { - t.Fatal(err) - } - testProxy(t, "udp", proxy) -} - -func TestUDP6Proxy(t *testing.T) { - backend := NewEchoServer(t, "udp", "[::1]:0") - defer backend.Close() - backend.Run() - frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0} - proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) - if err != nil { - t.Fatal(err) - } - testProxy(t, "udp", proxy) -} - -func TestUDPWriteError(t *testing.T) { - frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} - // Hopefully, this port will be free: */ - backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587} - proxy, err := NewProxy(frontendAddr, backendAddr) - if err != nil { - t.Fatal(err) - } - defer proxy.Close() - go proxy.Run() - client, err := net.Dial("udp", "127.0.0.1:25587") - if err != nil { - t.Fatalf("Can't connect to the proxy: %v", err) - } - defer client.Close() - // Make sure the proxy doesn't stop when there is no actual backend: - client.Write(testBuf) - client.Write(testBuf) - backend := NewEchoServer(t, "udp", "127.0.0.1:25587") - defer backend.Close() - backend.Run() - client.SetDeadline(time.Now().Add(10 * time.Second)) - if _, err = client.Write(testBuf); err != nil { - t.Fatal(err) - } - recvBuf := make([]byte, testBufSize) - if _, err = client.Read(recvBuf); err != nil { - t.Fatal(err) - } - if !bytes.Equal(testBuf, recvBuf) { - t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) - } -} diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go deleted file mode 100644 index 4e24e5f6a8..0000000000 --- a/pkg/proxy/proxy.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package proxy provides a network Proxy interface and implementations for TCP -// and UDP. -package proxy - -import ( - "fmt" - "net" -) - -// Proxy defines the behavior of a proxy. It forwards traffic back and forth -// between two endpoints : the frontend and the backend. -// It can be used to do software port-mapping between two addresses. -// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000 -// to the backend (container) at 172.17.42.108:4000. -type Proxy interface { - // Run starts forwarding traffic back and forth between the front - // and back-end addresses. - Run() - // Close stops forwarding traffic and close both ends of the Proxy. - Close() - // FrontendAddr returns the address on which the proxy is listening. - FrontendAddr() net.Addr - // BackendAddr returns the proxied address. - BackendAddr() net.Addr -} - -// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr. -func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { - switch frontendAddr.(type) { - case *net.UDPAddr: - return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr)) - case *net.TCPAddr: - return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr)) - default: - panic(fmt.Errorf("Unsupported protocol")) - } -} diff --git a/pkg/proxy/stub_proxy.go b/pkg/proxy/stub_proxy.go deleted file mode 100644 index 571749e467..0000000000 --- a/pkg/proxy/stub_proxy.go +++ /dev/null @@ -1,31 +0,0 @@ -package proxy - -import ( - "net" -) - -// StubProxy is a proxy that is a stub (does nothing). -type StubProxy struct { - frontendAddr net.Addr - backendAddr net.Addr -} - -// Run does nothing. -func (p *StubProxy) Run() {} - -// Close does nothing. -func (p *StubProxy) Close() {} - -// FrontendAddr returns the frontend address. -func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr } - -// BackendAddr returns the backend address. -func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr } - -// NewStubProxy creates a new StubProxy -func NewStubProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { - return &StubProxy{ - frontendAddr: frontendAddr, - backendAddr: backendAddr, - }, nil -} diff --git a/pkg/proxy/tcp_proxy.go b/pkg/proxy/tcp_proxy.go deleted file mode 100644 index 8f42580c1b..0000000000 --- a/pkg/proxy/tcp_proxy.go +++ /dev/null @@ -1,96 +0,0 @@ -package proxy - -import ( - "io" - "net" - "sync" - "syscall" - - "github.com/Sirupsen/logrus" -) - -// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to -// handle TCP traffic forwarding between the frontend and backend addresses. -type TCPProxy struct { - listener *net.TCPListener - frontendAddr *net.TCPAddr - backendAddr *net.TCPAddr -} - -// NewTCPProxy creates a new TCPProxy. -func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) { - listener, err := net.ListenTCP("tcp", frontendAddr) - if err != nil { - return nil, err - } - // If the port in frontendAddr was 0 then ListenTCP will have a picked - // a port to listen on, hence the call to Addr to get that actual port: - return &TCPProxy{ - listener: listener, - frontendAddr: listener.Addr().(*net.TCPAddr), - backendAddr: backendAddr, - }, nil -} - -func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) { - backend, err := net.DialTCP("tcp", nil, proxy.backendAddr) - if err != nil { - logrus.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err) - client.Close() - return - } - - var wg sync.WaitGroup - var broker = func(to, from *net.TCPConn) { - if _, err := io.Copy(to, from); err != nil { - // If the socket we are writing to is shutdown with - // SHUT_WR, forward it to the other end of the pipe: - if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE { - from.CloseWrite() - } - } - to.CloseRead() - wg.Done() - } - - wg.Add(2) - go broker(client, backend) - go broker(backend, client) - - finish := make(chan struct{}) - go func() { - wg.Wait() - close(finish) - }() - - select { - case <-quit: - case <-finish: - } - client.Close() - backend.Close() - <-finish -} - -// Run starts forwarding the traffic using TCP. -func (proxy *TCPProxy) Run() { - quit := make(chan bool) - defer close(quit) - for { - client, err := proxy.listener.Accept() - if err != nil { - logrus.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) - return - } - go proxy.clientLoop(client.(*net.TCPConn), quit) - } -} - -// Close stops forwarding the traffic. -func (proxy *TCPProxy) Close() { proxy.listener.Close() } - -// FrontendAddr returns the TCP address on which the proxy is listening. -func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } - -// BackendAddr returns the TCP proxied address. -func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr } diff --git a/pkg/proxy/udp_proxy.go b/pkg/proxy/udp_proxy.go deleted file mode 100644 index b8375c374f..0000000000 --- a/pkg/proxy/udp_proxy.go +++ /dev/null @@ -1,169 +0,0 @@ -package proxy - -import ( - "encoding/binary" - "net" - "strings" - "sync" - "syscall" - "time" - - "github.com/Sirupsen/logrus" -) - -const ( - // UDPConnTrackTimeout is the timeout used for UDP connection tracking - UDPConnTrackTimeout = 90 * time.Second - // UDPBufSize is the buffer size for the UDP proxy - UDPBufSize = 65507 -) - -// A net.Addr where the IP is split into two fields so you can use it as a key -// in a map: -type connTrackKey struct { - IPHigh uint64 - IPLow uint64 - Port int -} - -func newConnTrackKey(addr *net.UDPAddr) *connTrackKey { - if len(addr.IP) == net.IPv4len { - return &connTrackKey{ - IPHigh: 0, - IPLow: uint64(binary.BigEndian.Uint32(addr.IP)), - Port: addr.Port, - } - } - return &connTrackKey{ - IPHigh: binary.BigEndian.Uint64(addr.IP[:8]), - IPLow: binary.BigEndian.Uint64(addr.IP[8:]), - Port: addr.Port, - } -} - -type connTrackMap map[connTrackKey]*net.UDPConn - -// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy -// interface to handle UDP traffic forwarding between the frontend and backend -// addresses. -type UDPProxy struct { - listener *net.UDPConn - frontendAddr *net.UDPAddr - backendAddr *net.UDPAddr - connTrackTable connTrackMap - connTrackLock sync.Mutex -} - -// NewUDPProxy creates a new UDPProxy. -func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) { - listener, err := net.ListenUDP("udp", frontendAddr) - if err != nil { - return nil, err - } - return &UDPProxy{ - listener: listener, - frontendAddr: listener.LocalAddr().(*net.UDPAddr), - backendAddr: backendAddr, - connTrackTable: make(connTrackMap), - }, nil -} - -func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) { - defer func() { - proxy.connTrackLock.Lock() - delete(proxy.connTrackTable, *clientKey) - proxy.connTrackLock.Unlock() - proxyConn.Close() - }() - - readBuf := make([]byte, UDPBufSize) - for { - proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout)) - again: - read, err := proxyConn.Read(readBuf) - if err != nil { - if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED { - // This will happen if the last write failed - // (e.g: nothing is actually listening on the - // proxied port on the container), ignore it - // and continue until UDPConnTrackTimeout - // expires: - goto again - } - return - } - for i := 0; i != read; { - written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr) - if err != nil { - return - } - i += written - } - } -} - -// Run starts forwarding the traffic using UDP. -func (proxy *UDPProxy) Run() { - readBuf := make([]byte, UDPBufSize) - for { - read, from, err := proxy.listener.ReadFromUDP(readBuf) - if err != nil { - // NOTE: Apparently ReadFrom doesn't return - // ECONNREFUSED like Read do (see comment in - // UDPProxy.replyLoop) - if !isClosedError(err) { - logrus.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) - } - break - } - - fromKey := newConnTrackKey(from) - proxy.connTrackLock.Lock() - proxyConn, hit := proxy.connTrackTable[*fromKey] - if !hit { - proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr) - if err != nil { - logrus.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err) - proxy.connTrackLock.Unlock() - continue - } - proxy.connTrackTable[*fromKey] = proxyConn - go proxy.replyLoop(proxyConn, from, fromKey) - } - proxy.connTrackLock.Unlock() - for i := 0; i != read; { - written, err := proxyConn.Write(readBuf[i:read]) - if err != nil { - logrus.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err) - break - } - i += written - } - } -} - -// Close stops forwarding the traffic. -func (proxy *UDPProxy) Close() { - proxy.listener.Close() - proxy.connTrackLock.Lock() - defer proxy.connTrackLock.Unlock() - for _, conn := range proxy.connTrackTable { - conn.Close() - } -} - -// FrontendAddr returns the UDP address on which the proxy is listening. -func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } - -// BackendAddr returns the proxied UDP address. -func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr } - -func isClosedError(err error) bool { - /* This comparison is ugly, but unfortunately, net.go doesn't export errClosing. - * See: - * http://golang.org/src/pkg/net/net.go - * https://code.google.com/p/go/issues/detail?id=4337 - * https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ - */ - return strings.HasSuffix(err.Error(), "use of closed network connection") -}