Browse code

Only build docker-proxy for Linux

Signed-off-by: Rob Murray <rob.murray@docker.com>

Rob Murray authored on 2024/08/05 20:41:38
Showing 16 changed files
... ...
@@ -620,7 +620,7 @@ RUN --mount=type=bind,target=.,rw \
620 620
   xx-go --wrap
621 621
   PKG_CONFIG=$(xx-go env PKG_CONFIG) ./hack/make.sh $target
622 622
   xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/dockerd$([ "$(xx-info os)" = "windows" ] && echo ".exe")
623
-  xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/docker-proxy$([ "$(xx-info os)" = "windows" ] && echo ".exe")
623
+  [ "$(xx-info os)" != "linux" ] || xx-verify $([ "$DOCKER_STATIC" = "1" ] && echo "--static") /tmp/bundles/${target}-daemon/docker-proxy
624 624
   mkdir /build
625 625
   mv /tmp/bundles/${target}-daemon/* /build/
626 626
 EOT
627 627
deleted file mode 100644
... ...
@@ -1,5 +0,0 @@
1
-//go:generate go-winres make --arch=386,amd64,arm,arm64 --in=../../cli/winresources/docker-proxy/winres.json --out=../../cli/winresources/docker-proxy/resource
2
-
3
-package main
4
-
5
-import _ "github.com/docker/docker/cli/winresources/docker-proxy"
6 1
deleted file mode 100644
... ...
@@ -1,80 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"flag"
5
-	"fmt"
6
-	"log"
7
-	"net"
8
-	"os"
9
-	"os/signal"
10
-	"syscall"
11
-
12
-	"github.com/docker/docker/dockerversion"
13
-	"github.com/ishidawataru/sctp"
14
-)
15
-
16
-func main() {
17
-	f := os.NewFile(3, "signal-parent")
18
-	host, container := parseFlags()
19
-
20
-	p, err := NewProxy(host, container)
21
-	if err != nil {
22
-		fmt.Fprintf(f, "1\n%s", err)
23
-		f.Close()
24
-		os.Exit(1)
25
-	}
26
-	go handleStopSignals(p)
27
-	fmt.Fprint(f, "0\n")
28
-	f.Close()
29
-
30
-	// Run will block until the proxy stops
31
-	p.Run()
32
-}
33
-
34
-// parseFlags parses the flags passed on reexec to create the TCP/UDP/SCTP
35
-// net.Addrs to map the host and container ports.
36
-func parseFlags() (host net.Addr, container net.Addr) {
37
-	var (
38
-		proto         = flag.String("proto", "tcp", "proxy protocol")
39
-		hostIP        = flag.String("host-ip", "", "host ip")
40
-		hostPort      = flag.Int("host-port", -1, "host port")
41
-		containerIP   = flag.String("container-ip", "", "container ip")
42
-		containerPort = flag.Int("container-port", -1, "container port")
43
-		printVer      = flag.Bool("v", false, "print version information and quit")
44
-		printVersion  = flag.Bool("version", false, "print version information and quit")
45
-	)
46
-
47
-	flag.Parse()
48
-
49
-	if *printVer || *printVersion {
50
-		fmt.Printf("docker-proxy (commit %s) version %s\n", dockerversion.GitCommit, dockerversion.Version)
51
-		os.Exit(0)
52
-	}
53
-
54
-	switch *proto {
55
-	case "tcp":
56
-		host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
57
-		container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
58
-	case "udp":
59
-		host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
60
-		container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
61
-	case "sctp":
62
-		host = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*hostIP)}}, Port: *hostPort}
63
-		container = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*containerIP)}}, Port: *containerPort}
64
-	default:
65
-		log.Fatalf("unsupported protocol %s", *proto)
66
-	}
67
-
68
-	return host, container
69
-}
70
-
71
-func handleStopSignals(p Proxy) {
72
-	s := make(chan os.Signal, 10)
73
-	signal.Notify(s, os.Interrupt, syscall.SIGTERM)
74
-
75
-	for range s {
76
-		p.Close()
77
-
78
-		os.Exit(0)
79
-	}
80
-}
81 1
new file mode 100644
... ...
@@ -0,0 +1,80 @@
0
+package main
1
+
2
+import (
3
+	"flag"
4
+	"fmt"
5
+	"log"
6
+	"net"
7
+	"os"
8
+	"os/signal"
9
+	"syscall"
10
+
11
+	"github.com/docker/docker/dockerversion"
12
+	"github.com/ishidawataru/sctp"
13
+)
14
+
15
+func main() {
16
+	f := os.NewFile(3, "signal-parent")
17
+	host, container := parseFlags()
18
+
19
+	p, err := NewProxy(host, container)
20
+	if err != nil {
21
+		fmt.Fprintf(f, "1\n%s", err)
22
+		f.Close()
23
+		os.Exit(1)
24
+	}
25
+	go handleStopSignals(p)
26
+	fmt.Fprint(f, "0\n")
27
+	f.Close()
28
+
29
+	// Run will block until the proxy stops
30
+	p.Run()
31
+}
32
+
33
+// parseFlags parses the flags passed on reexec to create the TCP/UDP/SCTP
34
+// net.Addrs to map the host and container ports.
35
+func parseFlags() (host net.Addr, container net.Addr) {
36
+	var (
37
+		proto         = flag.String("proto", "tcp", "proxy protocol")
38
+		hostIP        = flag.String("host-ip", "", "host ip")
39
+		hostPort      = flag.Int("host-port", -1, "host port")
40
+		containerIP   = flag.String("container-ip", "", "container ip")
41
+		containerPort = flag.Int("container-port", -1, "container port")
42
+		printVer      = flag.Bool("v", false, "print version information and quit")
43
+		printVersion  = flag.Bool("version", false, "print version information and quit")
44
+	)
45
+
46
+	flag.Parse()
47
+
48
+	if *printVer || *printVersion {
49
+		fmt.Printf("docker-proxy (commit %s) version %s\n", dockerversion.GitCommit, dockerversion.Version)
50
+		os.Exit(0)
51
+	}
52
+
53
+	switch *proto {
54
+	case "tcp":
55
+		host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
56
+		container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
57
+	case "udp":
58
+		host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
59
+		container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
60
+	case "sctp":
61
+		host = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*hostIP)}}, Port: *hostPort}
62
+		container = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*containerIP)}}, Port: *containerPort}
63
+	default:
64
+		log.Fatalf("unsupported protocol %s", *proto)
65
+	}
66
+
67
+	return host, container
68
+}
69
+
70
+func handleStopSignals(p Proxy) {
71
+	s := make(chan os.Signal, 10)
72
+	signal.Notify(s, os.Interrupt, syscall.SIGTERM)
73
+
74
+	for range s {
75
+		p.Close()
76
+
77
+		os.Exit(0)
78
+	}
79
+}
0 80
new file mode 100644
... ...
@@ -0,0 +1,312 @@
0
+package main
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io"
6
+	"net"
7
+	"runtime"
8
+	"strings"
9
+	"testing"
10
+	"time"
11
+
12
+	"github.com/ishidawataru/sctp"
13
+	"gotest.tools/v3/skip"
14
+)
15
+
16
+var (
17
+	testBuf     = []byte("Buffalo1 buffalo2 Buffalo3 buffalo4 buffalo5 buffalo6 Buffalo7 buffalo8")
18
+	testBufSize = len(testBuf)
19
+)
20
+
21
+type EchoServer interface {
22
+	Run()
23
+	Close()
24
+	LocalAddr() net.Addr
25
+}
26
+
27
+type EchoServerOptions struct {
28
+	TCPHalfClose bool
29
+}
30
+
31
+type StreamEchoServer struct {
32
+	listener net.Listener
33
+	testCtx  *testing.T
34
+	opts     EchoServerOptions
35
+}
36
+
37
+type UDPEchoServer struct {
38
+	conn    net.PacketConn
39
+	testCtx *testing.T
40
+}
41
+
42
+func NewEchoServer(t *testing.T, proto, address string, opts EchoServerOptions) EchoServer {
43
+	var server EchoServer
44
+	if !strings.HasPrefix(proto, "tcp") && opts.TCPHalfClose {
45
+		t.Fatalf("TCPHalfClose is not supported for %s", proto)
46
+	}
47
+
48
+	switch {
49
+	case strings.HasPrefix(proto, "tcp"):
50
+		listener, err := net.Listen(proto, address)
51
+		if err != nil {
52
+			t.Fatal(err)
53
+		}
54
+		server = &StreamEchoServer{listener: listener, testCtx: t, opts: opts}
55
+	case strings.HasPrefix(proto, "udp"):
56
+		socket, err := net.ListenPacket(proto, address)
57
+		if err != nil {
58
+			t.Fatal(err)
59
+		}
60
+		server = &UDPEchoServer{conn: socket, testCtx: t}
61
+	case strings.HasPrefix(proto, "sctp"):
62
+		addr, err := sctp.ResolveSCTPAddr(proto, address)
63
+		if err != nil {
64
+			t.Fatal(err)
65
+		}
66
+		listener, err := sctp.ListenSCTP(proto, addr)
67
+		if err != nil {
68
+			t.Fatal(err)
69
+		}
70
+		server = &StreamEchoServer{listener: listener, testCtx: t}
71
+	default:
72
+		t.Fatalf("unknown protocol: %s", proto)
73
+	}
74
+	return server
75
+}
76
+
77
+func (server *StreamEchoServer) Run() {
78
+	go func() {
79
+		for {
80
+			client, err := server.listener.Accept()
81
+			if err != nil {
82
+				return
83
+			}
84
+			go func(client net.Conn) {
85
+				if server.opts.TCPHalfClose {
86
+					data, err := io.ReadAll(client)
87
+					if err != nil {
88
+						server.testCtx.Logf("io.ReadAll() failed for the client: %v\n", err.Error())
89
+					}
90
+					if _, err := client.Write(data); err != nil {
91
+						server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
92
+					}
93
+					client.(*net.TCPConn).CloseWrite()
94
+				} else {
95
+					if _, err := io.Copy(client, client); err != nil {
96
+						server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
97
+					}
98
+					client.Close()
99
+				}
100
+			}(client)
101
+		}
102
+	}()
103
+}
104
+
105
+func (server *StreamEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
106
+func (server *StreamEchoServer) Close()              { server.listener.Close() }
107
+
108
+func (server *UDPEchoServer) Run() {
109
+	go func() {
110
+		readBuf := make([]byte, 1024)
111
+		for {
112
+			read, from, err := server.conn.ReadFrom(readBuf)
113
+			if err != nil {
114
+				return
115
+			}
116
+			for i := 0; i != read; {
117
+				written, err := server.conn.WriteTo(readBuf[i:read], from)
118
+				if err != nil {
119
+					break
120
+				}
121
+				i += written
122
+			}
123
+		}
124
+	}()
125
+}
126
+
127
+func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() }
128
+func (server *UDPEchoServer) Close()              { server.conn.Close() }
129
+
130
+func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) {
131
+	defer proxy.Close()
132
+	go proxy.Run()
133
+	var client net.Conn
134
+	var err error
135
+	if strings.HasPrefix(proto, "sctp") {
136
+		var a *sctp.SCTPAddr
137
+		a, err = sctp.ResolveSCTPAddr(proto, addr)
138
+		if err != nil {
139
+			t.Fatal(err)
140
+		}
141
+		client, err = sctp.DialSCTP(proto, nil, a)
142
+	} else {
143
+		client, err = net.Dial(proto, addr)
144
+	}
145
+
146
+	if err != nil {
147
+		t.Fatalf("Can't connect to the proxy: %v", err)
148
+	}
149
+	defer client.Close()
150
+	client.SetDeadline(time.Now().Add(10 * time.Second))
151
+	if _, err = client.Write(testBuf); err != nil {
152
+		t.Fatal(err)
153
+	}
154
+	if halfClose {
155
+		if proto != "tcp" {
156
+			t.Fatalf("halfClose is not supported for %s", proto)
157
+		}
158
+		client.(*net.TCPConn).CloseWrite()
159
+	}
160
+	recvBuf := make([]byte, testBufSize)
161
+	if _, err = client.Read(recvBuf); err != nil {
162
+		t.Fatal(err)
163
+	}
164
+	if !bytes.Equal(testBuf, recvBuf) {
165
+		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
166
+	}
167
+}
168
+
169
+func testProxy(t *testing.T, proto string, proxy Proxy, halfClose bool) {
170
+	testProxyAt(t, proto, proxy, proxy.FrontendAddr().String(), halfClose)
171
+}
172
+
173
+func testTCP4Proxy(t *testing.T, halfClose bool) {
174
+	backend := NewEchoServer(t, "tcp", "127.0.0.1:0", EchoServerOptions{TCPHalfClose: halfClose})
175
+	defer backend.Close()
176
+	backend.Run()
177
+	frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
178
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
179
+	if err != nil {
180
+		t.Fatal(err)
181
+	}
182
+	testProxy(t, "tcp", proxy, halfClose)
183
+}
184
+
185
+func TestTCP4Proxy(t *testing.T) {
186
+	testTCP4Proxy(t, false)
187
+}
188
+
189
+func TestTCP4ProxyHalfClose(t *testing.T) {
190
+	testTCP4Proxy(t, true)
191
+}
192
+
193
+func TestTCP6Proxy(t *testing.T) {
194
+	t.Skip("Need to start CI docker with --ipv6")
195
+	backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{})
196
+	defer backend.Close()
197
+	backend.Run()
198
+	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
199
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
200
+	if err != nil {
201
+		t.Fatal(err)
202
+	}
203
+	testProxy(t, "tcp", proxy, false)
204
+}
205
+
206
+func TestTCPDualStackProxy(t *testing.T) {
207
+	// If I understand `godoc -src net favoriteAddrFamily` (used by the
208
+	// net.Listen* functions) correctly this should work, but it doesn't.
209
+	t.Skip("No support for dual stack yet")
210
+	backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{})
211
+	defer backend.Close()
212
+	backend.Run()
213
+	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
214
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
215
+	if err != nil {
216
+		t.Fatal(err)
217
+	}
218
+	ipv4ProxyAddr := &net.TCPAddr{
219
+		IP:   net.IPv4(127, 0, 0, 1),
220
+		Port: proxy.FrontendAddr().(*net.TCPAddr).Port,
221
+	}
222
+	testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String(), false)
223
+}
224
+
225
+func TestUDP4Proxy(t *testing.T) {
226
+	backend := NewEchoServer(t, "udp", "127.0.0.1:0", EchoServerOptions{})
227
+	defer backend.Close()
228
+	backend.Run()
229
+	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
230
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
231
+	if err != nil {
232
+		t.Fatal(err)
233
+	}
234
+	testProxy(t, "udp", proxy, false)
235
+}
236
+
237
+func TestUDP6Proxy(t *testing.T) {
238
+	t.Skip("Need to start CI docker with --ipv6")
239
+	backend := NewEchoServer(t, "udp", "[::1]:0", EchoServerOptions{})
240
+	defer backend.Close()
241
+	backend.Run()
242
+	frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0}
243
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
244
+	if err != nil {
245
+		t.Fatal(err)
246
+	}
247
+	testProxy(t, "udp", proxy, false)
248
+}
249
+
250
+func TestUDPWriteError(t *testing.T) {
251
+	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
252
+	// Hopefully, this port will be free: */
253
+	backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587}
254
+	proxy, err := NewProxy(frontendAddr, backendAddr)
255
+	if err != nil {
256
+		t.Fatal(err)
257
+	}
258
+	defer proxy.Close()
259
+	go proxy.Run()
260
+	client, err := net.Dial("udp", "127.0.0.1:25587")
261
+	if err != nil {
262
+		t.Fatalf("Can't connect to the proxy: %v", err)
263
+	}
264
+	defer client.Close()
265
+	// Make sure the proxy doesn't stop when there is no actual backend:
266
+	client.Write(testBuf)
267
+	client.Write(testBuf)
268
+	backend := NewEchoServer(t, "udp", "127.0.0.1:25587", EchoServerOptions{})
269
+	defer backend.Close()
270
+	backend.Run()
271
+	client.SetDeadline(time.Now().Add(10 * time.Second))
272
+	if _, err = client.Write(testBuf); err != nil {
273
+		t.Fatal(err)
274
+	}
275
+	recvBuf := make([]byte, testBufSize)
276
+	if _, err = client.Read(recvBuf); err != nil {
277
+		t.Fatal(err)
278
+	}
279
+	if !bytes.Equal(testBuf, recvBuf) {
280
+		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
281
+	}
282
+}
283
+
284
+func TestSCTP4Proxy(t *testing.T) {
285
+	skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows")
286
+
287
+	backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
288
+	defer backend.Close()
289
+	backend.Run()
290
+	frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0}
291
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
292
+	if err != nil {
293
+		t.Fatal(err)
294
+	}
295
+	testProxy(t, "sctp", proxy, false)
296
+}
297
+
298
+func TestSCTP6Proxy(t *testing.T) {
299
+	t.Skip("Need to start CI docker with --ipv6")
300
+	skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows")
301
+
302
+	backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
303
+	defer backend.Close()
304
+	backend.Run()
305
+	frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0}
306
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
307
+	if err != nil {
308
+		t.Fatal(err)
309
+	}
310
+	testProxy(t, "sctp", proxy, false)
311
+}
0 312
deleted file mode 100644
... ...
@@ -1,312 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"bytes"
5
-	"fmt"
6
-	"io"
7
-	"net"
8
-	"runtime"
9
-	"strings"
10
-	"testing"
11
-	"time"
12
-
13
-	"github.com/ishidawataru/sctp"
14
-	"gotest.tools/v3/skip"
15
-)
16
-
17
-var (
18
-	testBuf     = []byte("Buffalo1 buffalo2 Buffalo3 buffalo4 buffalo5 buffalo6 Buffalo7 buffalo8")
19
-	testBufSize = len(testBuf)
20
-)
21
-
22
-type EchoServer interface {
23
-	Run()
24
-	Close()
25
-	LocalAddr() net.Addr
26
-}
27
-
28
-type EchoServerOptions struct {
29
-	TCPHalfClose bool
30
-}
31
-
32
-type StreamEchoServer struct {
33
-	listener net.Listener
34
-	testCtx  *testing.T
35
-	opts     EchoServerOptions
36
-}
37
-
38
-type UDPEchoServer struct {
39
-	conn    net.PacketConn
40
-	testCtx *testing.T
41
-}
42
-
43
-func NewEchoServer(t *testing.T, proto, address string, opts EchoServerOptions) EchoServer {
44
-	var server EchoServer
45
-	if !strings.HasPrefix(proto, "tcp") && opts.TCPHalfClose {
46
-		t.Fatalf("TCPHalfClose is not supported for %s", proto)
47
-	}
48
-
49
-	switch {
50
-	case strings.HasPrefix(proto, "tcp"):
51
-		listener, err := net.Listen(proto, address)
52
-		if err != nil {
53
-			t.Fatal(err)
54
-		}
55
-		server = &StreamEchoServer{listener: listener, testCtx: t, opts: opts}
56
-	case strings.HasPrefix(proto, "udp"):
57
-		socket, err := net.ListenPacket(proto, address)
58
-		if err != nil {
59
-			t.Fatal(err)
60
-		}
61
-		server = &UDPEchoServer{conn: socket, testCtx: t}
62
-	case strings.HasPrefix(proto, "sctp"):
63
-		addr, err := sctp.ResolveSCTPAddr(proto, address)
64
-		if err != nil {
65
-			t.Fatal(err)
66
-		}
67
-		listener, err := sctp.ListenSCTP(proto, addr)
68
-		if err != nil {
69
-			t.Fatal(err)
70
-		}
71
-		server = &StreamEchoServer{listener: listener, testCtx: t}
72
-	default:
73
-		t.Fatalf("unknown protocol: %s", proto)
74
-	}
75
-	return server
76
-}
77
-
78
-func (server *StreamEchoServer) Run() {
79
-	go func() {
80
-		for {
81
-			client, err := server.listener.Accept()
82
-			if err != nil {
83
-				return
84
-			}
85
-			go func(client net.Conn) {
86
-				if server.opts.TCPHalfClose {
87
-					data, err := io.ReadAll(client)
88
-					if err != nil {
89
-						server.testCtx.Logf("io.ReadAll() failed for the client: %v\n", err.Error())
90
-					}
91
-					if _, err := client.Write(data); err != nil {
92
-						server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
93
-					}
94
-					client.(*net.TCPConn).CloseWrite()
95
-				} else {
96
-					if _, err := io.Copy(client, client); err != nil {
97
-						server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
98
-					}
99
-					client.Close()
100
-				}
101
-			}(client)
102
-		}
103
-	}()
104
-}
105
-
106
-func (server *StreamEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
107
-func (server *StreamEchoServer) Close()              { server.listener.Close() }
108
-
109
-func (server *UDPEchoServer) Run() {
110
-	go func() {
111
-		readBuf := make([]byte, 1024)
112
-		for {
113
-			read, from, err := server.conn.ReadFrom(readBuf)
114
-			if err != nil {
115
-				return
116
-			}
117
-			for i := 0; i != read; {
118
-				written, err := server.conn.WriteTo(readBuf[i:read], from)
119
-				if err != nil {
120
-					break
121
-				}
122
-				i += written
123
-			}
124
-		}
125
-	}()
126
-}
127
-
128
-func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() }
129
-func (server *UDPEchoServer) Close()              { server.conn.Close() }
130
-
131
-func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) {
132
-	defer proxy.Close()
133
-	go proxy.Run()
134
-	var client net.Conn
135
-	var err error
136
-	if strings.HasPrefix(proto, "sctp") {
137
-		var a *sctp.SCTPAddr
138
-		a, err = sctp.ResolveSCTPAddr(proto, addr)
139
-		if err != nil {
140
-			t.Fatal(err)
141
-		}
142
-		client, err = sctp.DialSCTP(proto, nil, a)
143
-	} else {
144
-		client, err = net.Dial(proto, addr)
145
-	}
146
-
147
-	if err != nil {
148
-		t.Fatalf("Can't connect to the proxy: %v", err)
149
-	}
150
-	defer client.Close()
151
-	client.SetDeadline(time.Now().Add(10 * time.Second))
152
-	if _, err = client.Write(testBuf); err != nil {
153
-		t.Fatal(err)
154
-	}
155
-	if halfClose {
156
-		if proto != "tcp" {
157
-			t.Fatalf("halfClose is not supported for %s", proto)
158
-		}
159
-		client.(*net.TCPConn).CloseWrite()
160
-	}
161
-	recvBuf := make([]byte, testBufSize)
162
-	if _, err = client.Read(recvBuf); err != nil {
163
-		t.Fatal(err)
164
-	}
165
-	if !bytes.Equal(testBuf, recvBuf) {
166
-		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
167
-	}
168
-}
169
-
170
-func testProxy(t *testing.T, proto string, proxy Proxy, halfClose bool) {
171
-	testProxyAt(t, proto, proxy, proxy.FrontendAddr().String(), halfClose)
172
-}
173
-
174
-func testTCP4Proxy(t *testing.T, halfClose bool) {
175
-	backend := NewEchoServer(t, "tcp", "127.0.0.1:0", EchoServerOptions{TCPHalfClose: halfClose})
176
-	defer backend.Close()
177
-	backend.Run()
178
-	frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
179
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
180
-	if err != nil {
181
-		t.Fatal(err)
182
-	}
183
-	testProxy(t, "tcp", proxy, halfClose)
184
-}
185
-
186
-func TestTCP4Proxy(t *testing.T) {
187
-	testTCP4Proxy(t, false)
188
-}
189
-
190
-func TestTCP4ProxyHalfClose(t *testing.T) {
191
-	testTCP4Proxy(t, true)
192
-}
193
-
194
-func TestTCP6Proxy(t *testing.T) {
195
-	t.Skip("Need to start CI docker with --ipv6")
196
-	backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{})
197
-	defer backend.Close()
198
-	backend.Run()
199
-	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
200
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
201
-	if err != nil {
202
-		t.Fatal(err)
203
-	}
204
-	testProxy(t, "tcp", proxy, false)
205
-}
206
-
207
-func TestTCPDualStackProxy(t *testing.T) {
208
-	// If I understand `godoc -src net favoriteAddrFamily` (used by the
209
-	// net.Listen* functions) correctly this should work, but it doesn't.
210
-	t.Skip("No support for dual stack yet")
211
-	backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{})
212
-	defer backend.Close()
213
-	backend.Run()
214
-	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
215
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
216
-	if err != nil {
217
-		t.Fatal(err)
218
-	}
219
-	ipv4ProxyAddr := &net.TCPAddr{
220
-		IP:   net.IPv4(127, 0, 0, 1),
221
-		Port: proxy.FrontendAddr().(*net.TCPAddr).Port,
222
-	}
223
-	testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String(), false)
224
-}
225
-
226
-func TestUDP4Proxy(t *testing.T) {
227
-	backend := NewEchoServer(t, "udp", "127.0.0.1:0", EchoServerOptions{})
228
-	defer backend.Close()
229
-	backend.Run()
230
-	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
231
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
232
-	if err != nil {
233
-		t.Fatal(err)
234
-	}
235
-	testProxy(t, "udp", proxy, false)
236
-}
237
-
238
-func TestUDP6Proxy(t *testing.T) {
239
-	t.Skip("Need to start CI docker with --ipv6")
240
-	backend := NewEchoServer(t, "udp", "[::1]:0", EchoServerOptions{})
241
-	defer backend.Close()
242
-	backend.Run()
243
-	frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0}
244
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
245
-	if err != nil {
246
-		t.Fatal(err)
247
-	}
248
-	testProxy(t, "udp", proxy, false)
249
-}
250
-
251
-func TestUDPWriteError(t *testing.T) {
252
-	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
253
-	// Hopefully, this port will be free: */
254
-	backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587}
255
-	proxy, err := NewProxy(frontendAddr, backendAddr)
256
-	if err != nil {
257
-		t.Fatal(err)
258
-	}
259
-	defer proxy.Close()
260
-	go proxy.Run()
261
-	client, err := net.Dial("udp", "127.0.0.1:25587")
262
-	if err != nil {
263
-		t.Fatalf("Can't connect to the proxy: %v", err)
264
-	}
265
-	defer client.Close()
266
-	// Make sure the proxy doesn't stop when there is no actual backend:
267
-	client.Write(testBuf)
268
-	client.Write(testBuf)
269
-	backend := NewEchoServer(t, "udp", "127.0.0.1:25587", EchoServerOptions{})
270
-	defer backend.Close()
271
-	backend.Run()
272
-	client.SetDeadline(time.Now().Add(10 * time.Second))
273
-	if _, err = client.Write(testBuf); err != nil {
274
-		t.Fatal(err)
275
-	}
276
-	recvBuf := make([]byte, testBufSize)
277
-	if _, err = client.Read(recvBuf); err != nil {
278
-		t.Fatal(err)
279
-	}
280
-	if !bytes.Equal(testBuf, recvBuf) {
281
-		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
282
-	}
283
-}
284
-
285
-func TestSCTP4Proxy(t *testing.T) {
286
-	skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows")
287
-
288
-	backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
289
-	defer backend.Close()
290
-	backend.Run()
291
-	frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0}
292
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
293
-	if err != nil {
294
-		t.Fatal(err)
295
-	}
296
-	testProxy(t, "sctp", proxy, false)
297
-}
298
-
299
-func TestSCTP6Proxy(t *testing.T) {
300
-	t.Skip("Need to start CI docker with --ipv6")
301
-	skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows")
302
-
303
-	backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
304
-	defer backend.Close()
305
-	backend.Run()
306
-	frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0}
307
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
308
-	if err != nil {
309
-		t.Fatal(err)
310
-	}
311
-	testProxy(t, "sctp", proxy, false)
312
-}
313 1
deleted file mode 100644
... ...
@@ -1,50 +0,0 @@
1
-// docker-proxy provides a network Proxy interface and implementations for TCP
2
-// and UDP.
3
-package main
4
-
5
-import (
6
-	"net"
7
-
8
-	"github.com/ishidawataru/sctp"
9
-)
10
-
11
-// ipVersion refers to IP version - v4 or v6
12
-type ipVersion string
13
-
14
-const (
15
-	// IPv4 is version 4
16
-	ipv4 ipVersion = "4"
17
-	// IPv4 is version 6
18
-	ipv6 ipVersion = "6"
19
-)
20
-
21
-// Proxy defines the behavior of a proxy. It forwards traffic back and forth
22
-// between two endpoints : the frontend and the backend.
23
-// It can be used to do software port-mapping between two addresses.
24
-// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000
25
-// to the backend (container) at 172.17.42.108:4000.
26
-type Proxy interface {
27
-	// Run starts forwarding traffic back and forth between the front
28
-	// and back-end addresses.
29
-	Run()
30
-	// Close stops forwarding traffic and close both ends of the Proxy.
31
-	Close()
32
-	// FrontendAddr returns the address on which the proxy is listening.
33
-	FrontendAddr() net.Addr
34
-	// BackendAddr returns the proxied address.
35
-	BackendAddr() net.Addr
36
-}
37
-
38
-// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
39
-func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
40
-	switch frontendAddr.(type) {
41
-	case *net.UDPAddr:
42
-		return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
43
-	case *net.TCPAddr:
44
-		return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
45
-	case *sctp.SCTPAddr:
46
-		return NewSCTPProxy(frontendAddr.(*sctp.SCTPAddr), backendAddr.(*sctp.SCTPAddr))
47
-	default:
48
-		panic("Unsupported protocol")
49
-	}
50
-}
51 1
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+// docker-proxy provides a network Proxy interface and implementations for TCP
1
+// and UDP.
2
+package main
3
+
4
+import (
5
+	"net"
6
+
7
+	"github.com/ishidawataru/sctp"
8
+)
9
+
10
+// ipVersion refers to IP version - v4 or v6
11
+type ipVersion string
12
+
13
+const (
14
+	// IPv4 is version 4
15
+	ipv4 ipVersion = "4"
16
+	// IPv4 is version 6
17
+	ipv6 ipVersion = "6"
18
+)
19
+
20
+// Proxy defines the behavior of a proxy. It forwards traffic back and forth
21
+// between two endpoints : the frontend and the backend.
22
+// It can be used to do software port-mapping between two addresses.
23
+// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000
24
+// to the backend (container) at 172.17.42.108:4000.
25
+type Proxy interface {
26
+	// Run starts forwarding traffic back and forth between the front
27
+	// and back-end addresses.
28
+	Run()
29
+	// Close stops forwarding traffic and close both ends of the Proxy.
30
+	Close()
31
+	// FrontendAddr returns the address on which the proxy is listening.
32
+	FrontendAddr() net.Addr
33
+	// BackendAddr returns the proxied address.
34
+	BackendAddr() net.Addr
35
+}
36
+
37
+// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
38
+func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
39
+	switch frontendAddr.(type) {
40
+	case *net.UDPAddr:
41
+		return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
42
+	case *net.TCPAddr:
43
+		return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
44
+	case *sctp.SCTPAddr:
45
+		return NewSCTPProxy(frontendAddr.(*sctp.SCTPAddr), backendAddr.(*sctp.SCTPAddr))
46
+	default:
47
+		panic("Unsupported protocol")
48
+	}
49
+}
0 50
deleted file mode 100644
... ...
@@ -1,98 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"io"
5
-	"log"
6
-	"net"
7
-	"sync"
8
-
9
-	"github.com/ishidawataru/sctp"
10
-)
11
-
12
-// SCTPProxy is a proxy for SCTP connections. It implements the Proxy interface to
13
-// handle SCTP traffic forwarding between the frontend and backend addresses.
14
-type SCTPProxy struct {
15
-	listener     *sctp.SCTPListener
16
-	frontendAddr *sctp.SCTPAddr
17
-	backendAddr  *sctp.SCTPAddr
18
-}
19
-
20
-// NewSCTPProxy creates a new SCTPProxy.
21
-func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) {
22
-	// detect version of hostIP to bind only to correct version
23
-	ipVersion := ipv4
24
-	if frontendAddr.IPAddrs[0].IP.To4() == nil {
25
-		ipVersion = ipv6
26
-	}
27
-	listener, err := sctp.ListenSCTP("sctp"+string(ipVersion), frontendAddr)
28
-	if err != nil {
29
-		return nil, err
30
-	}
31
-	// If the port in frontendAddr was 0 then ListenSCTP will have a picked
32
-	// a port to listen on, hence the call to Addr to get that actual port:
33
-	return &SCTPProxy{
34
-		listener:     listener,
35
-		frontendAddr: listener.Addr().(*sctp.SCTPAddr),
36
-		backendAddr:  backendAddr,
37
-	}, nil
38
-}
39
-
40
-func (proxy *SCTPProxy) clientLoop(client *sctp.SCTPConn, quit chan bool) {
41
-	backend, err := sctp.DialSCTP("sctp", nil, proxy.backendAddr)
42
-	if err != nil {
43
-		log.Printf("Can't forward traffic to backend sctp/%v: %s\n", proxy.backendAddr, err)
44
-		client.Close()
45
-		return
46
-	}
47
-	clientC := sctp.NewSCTPSndRcvInfoWrappedConn(client)
48
-	backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend)
49
-
50
-	var wg sync.WaitGroup
51
-	broker := func(to, from net.Conn) {
52
-		io.Copy(to, from)
53
-		from.Close()
54
-		to.Close()
55
-		wg.Done()
56
-	}
57
-
58
-	wg.Add(2)
59
-	go broker(clientC, backendC)
60
-	go broker(backendC, clientC)
61
-
62
-	finish := make(chan struct{})
63
-	go func() {
64
-		wg.Wait()
65
-		close(finish)
66
-	}()
67
-
68
-	select {
69
-	case <-quit:
70
-	case <-finish:
71
-	}
72
-	clientC.Close()
73
-	backendC.Close()
74
-	<-finish
75
-}
76
-
77
-// Run starts forwarding the traffic using SCTP.
78
-func (proxy *SCTPProxy) Run() {
79
-	quit := make(chan bool)
80
-	defer close(quit)
81
-	for {
82
-		client, err := proxy.listener.Accept()
83
-		if err != nil {
84
-			log.Printf("Stopping proxy on sctp/%v for sctp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
85
-			return
86
-		}
87
-		go proxy.clientLoop(client.(*sctp.SCTPConn), quit)
88
-	}
89
-}
90
-
91
-// Close stops forwarding the traffic.
92
-func (proxy *SCTPProxy) Close() { proxy.listener.Close() }
93
-
94
-// FrontendAddr returns the SCTP address on which the proxy is listening.
95
-func (proxy *SCTPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
96
-
97
-// BackendAddr returns the SCTP proxied address.
98
-func (proxy *SCTPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
99 1
new file mode 100644
... ...
@@ -0,0 +1,98 @@
0
+package main
1
+
2
+import (
3
+	"io"
4
+	"log"
5
+	"net"
6
+	"sync"
7
+
8
+	"github.com/ishidawataru/sctp"
9
+)
10
+
11
+// SCTPProxy is a proxy for SCTP connections. It implements the Proxy interface to
12
+// handle SCTP traffic forwarding between the frontend and backend addresses.
13
+type SCTPProxy struct {
14
+	listener     *sctp.SCTPListener
15
+	frontendAddr *sctp.SCTPAddr
16
+	backendAddr  *sctp.SCTPAddr
17
+}
18
+
19
+// NewSCTPProxy creates a new SCTPProxy.
20
+func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) {
21
+	// detect version of hostIP to bind only to correct version
22
+	ipVersion := ipv4
23
+	if frontendAddr.IPAddrs[0].IP.To4() == nil {
24
+		ipVersion = ipv6
25
+	}
26
+	listener, err := sctp.ListenSCTP("sctp"+string(ipVersion), frontendAddr)
27
+	if err != nil {
28
+		return nil, err
29
+	}
30
+	// If the port in frontendAddr was 0 then ListenSCTP will have a picked
31
+	// a port to listen on, hence the call to Addr to get that actual port:
32
+	return &SCTPProxy{
33
+		listener:     listener,
34
+		frontendAddr: listener.Addr().(*sctp.SCTPAddr),
35
+		backendAddr:  backendAddr,
36
+	}, nil
37
+}
38
+
39
+func (proxy *SCTPProxy) clientLoop(client *sctp.SCTPConn, quit chan bool) {
40
+	backend, err := sctp.DialSCTP("sctp", nil, proxy.backendAddr)
41
+	if err != nil {
42
+		log.Printf("Can't forward traffic to backend sctp/%v: %s\n", proxy.backendAddr, err)
43
+		client.Close()
44
+		return
45
+	}
46
+	clientC := sctp.NewSCTPSndRcvInfoWrappedConn(client)
47
+	backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend)
48
+
49
+	var wg sync.WaitGroup
50
+	broker := func(to, from net.Conn) {
51
+		io.Copy(to, from)
52
+		from.Close()
53
+		to.Close()
54
+		wg.Done()
55
+	}
56
+
57
+	wg.Add(2)
58
+	go broker(clientC, backendC)
59
+	go broker(backendC, clientC)
60
+
61
+	finish := make(chan struct{})
62
+	go func() {
63
+		wg.Wait()
64
+		close(finish)
65
+	}()
66
+
67
+	select {
68
+	case <-quit:
69
+	case <-finish:
70
+	}
71
+	clientC.Close()
72
+	backendC.Close()
73
+	<-finish
74
+}
75
+
76
+// Run starts forwarding the traffic using SCTP.
77
+func (proxy *SCTPProxy) Run() {
78
+	quit := make(chan bool)
79
+	defer close(quit)
80
+	for {
81
+		client, err := proxy.listener.Accept()
82
+		if err != nil {
83
+			log.Printf("Stopping proxy on sctp/%v for sctp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
84
+			return
85
+		}
86
+		go proxy.clientLoop(client.(*sctp.SCTPConn), quit)
87
+	}
88
+}
89
+
90
+// Close stops forwarding the traffic.
91
+func (proxy *SCTPProxy) Close() { proxy.listener.Close() }
92
+
93
+// FrontendAddr returns the SCTP address on which the proxy is listening.
94
+func (proxy *SCTPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
95
+
96
+// BackendAddr returns the SCTP proxied address.
97
+func (proxy *SCTPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
0 98
deleted file mode 100644
... ...
@@ -1,94 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"io"
5
-	"log"
6
-	"net"
7
-	"sync"
8
-)
9
-
10
-// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
11
-// handle TCP traffic forwarding between the frontend and backend addresses.
12
-type TCPProxy struct {
13
-	listener     *net.TCPListener
14
-	frontendAddr *net.TCPAddr
15
-	backendAddr  *net.TCPAddr
16
-}
17
-
18
-// NewTCPProxy creates a new TCPProxy.
19
-func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
20
-	// detect version of hostIP to bind only to correct version
21
-	ipVersion := ipv4
22
-	if frontendAddr.IP.To4() == nil {
23
-		ipVersion = ipv6
24
-	}
25
-	listener, err := net.ListenTCP("tcp"+string(ipVersion), frontendAddr)
26
-	if err != nil {
27
-		return nil, err
28
-	}
29
-	// If the port in frontendAddr was 0 then ListenTCP will have a picked
30
-	// a port to listen on, hence the call to Addr to get that actual port:
31
-	return &TCPProxy{
32
-		listener:     listener,
33
-		frontendAddr: listener.Addr().(*net.TCPAddr),
34
-		backendAddr:  backendAddr,
35
-	}, nil
36
-}
37
-
38
-func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
39
-	backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
40
-	if err != nil {
41
-		log.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
42
-		client.Close()
43
-		return
44
-	}
45
-
46
-	var wg sync.WaitGroup
47
-	broker := func(to, from *net.TCPConn) {
48
-		io.Copy(to, from)
49
-		from.CloseRead()
50
-		to.CloseWrite()
51
-		wg.Done()
52
-	}
53
-
54
-	wg.Add(2)
55
-	go broker(client, backend)
56
-	go broker(backend, client)
57
-
58
-	finish := make(chan struct{})
59
-	go func() {
60
-		wg.Wait()
61
-		close(finish)
62
-	}()
63
-
64
-	select {
65
-	case <-quit:
66
-	case <-finish:
67
-	}
68
-	client.Close()
69
-	backend.Close()
70
-	<-finish
71
-}
72
-
73
-// Run starts forwarding the traffic using TCP.
74
-func (proxy *TCPProxy) Run() {
75
-	quit := make(chan bool)
76
-	defer close(quit)
77
-	for {
78
-		client, err := proxy.listener.Accept()
79
-		if err != nil {
80
-			log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
81
-			return
82
-		}
83
-		go proxy.clientLoop(client.(*net.TCPConn), quit)
84
-	}
85
-}
86
-
87
-// Close stops forwarding the traffic.
88
-func (proxy *TCPProxy) Close() { proxy.listener.Close() }
89
-
90
-// FrontendAddr returns the TCP address on which the proxy is listening.
91
-func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
92
-
93
-// BackendAddr returns the TCP proxied address.
94
-func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
95 1
new file mode 100644
... ...
@@ -0,0 +1,94 @@
0
+package main
1
+
2
+import (
3
+	"io"
4
+	"log"
5
+	"net"
6
+	"sync"
7
+)
8
+
9
+// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
10
+// handle TCP traffic forwarding between the frontend and backend addresses.
11
+type TCPProxy struct {
12
+	listener     *net.TCPListener
13
+	frontendAddr *net.TCPAddr
14
+	backendAddr  *net.TCPAddr
15
+}
16
+
17
+// NewTCPProxy creates a new TCPProxy.
18
+func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
19
+	// detect version of hostIP to bind only to correct version
20
+	ipVersion := ipv4
21
+	if frontendAddr.IP.To4() == nil {
22
+		ipVersion = ipv6
23
+	}
24
+	listener, err := net.ListenTCP("tcp"+string(ipVersion), frontendAddr)
25
+	if err != nil {
26
+		return nil, err
27
+	}
28
+	// If the port in frontendAddr was 0 then ListenTCP will have a picked
29
+	// a port to listen on, hence the call to Addr to get that actual port:
30
+	return &TCPProxy{
31
+		listener:     listener,
32
+		frontendAddr: listener.Addr().(*net.TCPAddr),
33
+		backendAddr:  backendAddr,
34
+	}, nil
35
+}
36
+
37
+func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
38
+	backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
39
+	if err != nil {
40
+		log.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
41
+		client.Close()
42
+		return
43
+	}
44
+
45
+	var wg sync.WaitGroup
46
+	broker := func(to, from *net.TCPConn) {
47
+		io.Copy(to, from)
48
+		from.CloseRead()
49
+		to.CloseWrite()
50
+		wg.Done()
51
+	}
52
+
53
+	wg.Add(2)
54
+	go broker(client, backend)
55
+	go broker(backend, client)
56
+
57
+	finish := make(chan struct{})
58
+	go func() {
59
+		wg.Wait()
60
+		close(finish)
61
+	}()
62
+
63
+	select {
64
+	case <-quit:
65
+	case <-finish:
66
+	}
67
+	client.Close()
68
+	backend.Close()
69
+	<-finish
70
+}
71
+
72
+// Run starts forwarding the traffic using TCP.
73
+func (proxy *TCPProxy) Run() {
74
+	quit := make(chan bool)
75
+	defer close(quit)
76
+	for {
77
+		client, err := proxy.listener.Accept()
78
+		if err != nil {
79
+			log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
80
+			return
81
+		}
82
+		go proxy.clientLoop(client.(*net.TCPConn), quit)
83
+	}
84
+}
85
+
86
+// Close stops forwarding the traffic.
87
+func (proxy *TCPProxy) Close() { proxy.listener.Close() }
88
+
89
+// FrontendAddr returns the TCP address on which the proxy is listening.
90
+func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
91
+
92
+// BackendAddr returns the TCP proxied address.
93
+func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
0 94
deleted file mode 100644
... ...
@@ -1,173 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"encoding/binary"
5
-	"log"
6
-	"net"
7
-	"strings"
8
-	"sync"
9
-	"syscall"
10
-	"time"
11
-)
12
-
13
-const (
14
-	// UDPConnTrackTimeout is the timeout used for UDP connection tracking
15
-	UDPConnTrackTimeout = 90 * time.Second
16
-	// UDPBufSize is the buffer size for the UDP proxy
17
-	UDPBufSize = 65507
18
-)
19
-
20
-// A net.Addr where the IP is split into two fields so you can use it as a key
21
-// in a map:
22
-type connTrackKey struct {
23
-	IPHigh uint64
24
-	IPLow  uint64
25
-	Port   int
26
-}
27
-
28
-func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
29
-	if len(addr.IP) == net.IPv4len {
30
-		return &connTrackKey{
31
-			IPHigh: 0,
32
-			IPLow:  uint64(binary.BigEndian.Uint32(addr.IP)),
33
-			Port:   addr.Port,
34
-		}
35
-	}
36
-	return &connTrackKey{
37
-		IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
38
-		IPLow:  binary.BigEndian.Uint64(addr.IP[8:]),
39
-		Port:   addr.Port,
40
-	}
41
-}
42
-
43
-type connTrackMap map[connTrackKey]*net.UDPConn
44
-
45
-// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy
46
-// interface to handle UDP traffic forwarding between the frontend and backend
47
-// addresses.
48
-type UDPProxy struct {
49
-	listener       *net.UDPConn
50
-	frontendAddr   *net.UDPAddr
51
-	backendAddr    *net.UDPAddr
52
-	connTrackTable connTrackMap
53
-	connTrackLock  sync.Mutex
54
-}
55
-
56
-// NewUDPProxy creates a new UDPProxy.
57
-func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
58
-	// detect version of hostIP to bind only to correct version
59
-	ipVersion := ipv4
60
-	if frontendAddr.IP.To4() == nil {
61
-		ipVersion = ipv6
62
-	}
63
-	listener, err := net.ListenUDP("udp"+string(ipVersion), frontendAddr)
64
-	if err != nil {
65
-		return nil, err
66
-	}
67
-	return &UDPProxy{
68
-		listener:       listener,
69
-		frontendAddr:   listener.LocalAddr().(*net.UDPAddr),
70
-		backendAddr:    backendAddr,
71
-		connTrackTable: make(connTrackMap),
72
-	}, nil
73
-}
74
-
75
-func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
76
-	defer func() {
77
-		proxy.connTrackLock.Lock()
78
-		delete(proxy.connTrackTable, *clientKey)
79
-		proxy.connTrackLock.Unlock()
80
-		proxyConn.Close()
81
-	}()
82
-
83
-	readBuf := make([]byte, UDPBufSize)
84
-	for {
85
-		proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
86
-	again:
87
-		read, err := proxyConn.Read(readBuf)
88
-		if err != nil {
89
-			if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
90
-				// This will happen if the last write failed
91
-				// (e.g: nothing is actually listening on the
92
-				// proxied port on the container), ignore it
93
-				// and continue until UDPConnTrackTimeout
94
-				// expires:
95
-				goto again
96
-			}
97
-			return
98
-		}
99
-		for i := 0; i != read; {
100
-			written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
101
-			if err != nil {
102
-				return
103
-			}
104
-			i += written
105
-		}
106
-	}
107
-}
108
-
109
-// Run starts forwarding the traffic using UDP.
110
-func (proxy *UDPProxy) Run() {
111
-	readBuf := make([]byte, UDPBufSize)
112
-	for {
113
-		read, from, err := proxy.listener.ReadFromUDP(readBuf)
114
-		if err != nil {
115
-			// NOTE: Apparently ReadFrom doesn't return
116
-			// ECONNREFUSED like Read do (see comment in
117
-			// UDPProxy.replyLoop)
118
-			if !isClosedError(err) {
119
-				log.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
120
-			}
121
-			break
122
-		}
123
-
124
-		fromKey := newConnTrackKey(from)
125
-		proxy.connTrackLock.Lock()
126
-		proxyConn, hit := proxy.connTrackTable[*fromKey]
127
-		if !hit {
128
-			proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
129
-			if err != nil {
130
-				log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
131
-				proxy.connTrackLock.Unlock()
132
-				continue
133
-			}
134
-			proxy.connTrackTable[*fromKey] = proxyConn
135
-			go proxy.replyLoop(proxyConn, from, fromKey)
136
-		}
137
-		proxy.connTrackLock.Unlock()
138
-		for i := 0; i != read; {
139
-			written, err := proxyConn.Write(readBuf[i:read])
140
-			if err != nil {
141
-				log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
142
-				break
143
-			}
144
-			i += written
145
-		}
146
-	}
147
-}
148
-
149
-// Close stops forwarding the traffic.
150
-func (proxy *UDPProxy) Close() {
151
-	proxy.listener.Close()
152
-	proxy.connTrackLock.Lock()
153
-	defer proxy.connTrackLock.Unlock()
154
-	for _, conn := range proxy.connTrackTable {
155
-		conn.Close()
156
-	}
157
-}
158
-
159
-// FrontendAddr returns the UDP address on which the proxy is listening.
160
-func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
161
-
162
-// BackendAddr returns the proxied UDP address.
163
-func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
164
-
165
-func isClosedError(err error) bool {
166
-	/* This comparison is ugly, but unfortunately, net.go doesn't export errClosing.
167
-	 * See:
168
-	 * http://golang.org/src/pkg/net/net.go
169
-	 * https://code.google.com/p/go/issues/detail?id=4337
170
-	 * https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ
171
-	 */
172
-	return strings.HasSuffix(err.Error(), "use of closed network connection")
173
-}
174 1
new file mode 100644
... ...
@@ -0,0 +1,173 @@
0
+package main
1
+
2
+import (
3
+	"encoding/binary"
4
+	"log"
5
+	"net"
6
+	"strings"
7
+	"sync"
8
+	"syscall"
9
+	"time"
10
+)
11
+
12
+const (
13
+	// UDPConnTrackTimeout is the timeout used for UDP connection tracking
14
+	UDPConnTrackTimeout = 90 * time.Second
15
+	// UDPBufSize is the buffer size for the UDP proxy
16
+	UDPBufSize = 65507
17
+)
18
+
19
+// A net.Addr where the IP is split into two fields so you can use it as a key
20
+// in a map:
21
+type connTrackKey struct {
22
+	IPHigh uint64
23
+	IPLow  uint64
24
+	Port   int
25
+}
26
+
27
+func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
28
+	if len(addr.IP) == net.IPv4len {
29
+		return &connTrackKey{
30
+			IPHigh: 0,
31
+			IPLow:  uint64(binary.BigEndian.Uint32(addr.IP)),
32
+			Port:   addr.Port,
33
+		}
34
+	}
35
+	return &connTrackKey{
36
+		IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
37
+		IPLow:  binary.BigEndian.Uint64(addr.IP[8:]),
38
+		Port:   addr.Port,
39
+	}
40
+}
41
+
42
+type connTrackMap map[connTrackKey]*net.UDPConn
43
+
44
+// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy
45
+// interface to handle UDP traffic forwarding between the frontend and backend
46
+// addresses.
47
+type UDPProxy struct {
48
+	listener       *net.UDPConn
49
+	frontendAddr   *net.UDPAddr
50
+	backendAddr    *net.UDPAddr
51
+	connTrackTable connTrackMap
52
+	connTrackLock  sync.Mutex
53
+}
54
+
55
+// NewUDPProxy creates a new UDPProxy.
56
+func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
57
+	// detect version of hostIP to bind only to correct version
58
+	ipVersion := ipv4
59
+	if frontendAddr.IP.To4() == nil {
60
+		ipVersion = ipv6
61
+	}
62
+	listener, err := net.ListenUDP("udp"+string(ipVersion), frontendAddr)
63
+	if err != nil {
64
+		return nil, err
65
+	}
66
+	return &UDPProxy{
67
+		listener:       listener,
68
+		frontendAddr:   listener.LocalAddr().(*net.UDPAddr),
69
+		backendAddr:    backendAddr,
70
+		connTrackTable: make(connTrackMap),
71
+	}, nil
72
+}
73
+
74
+func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
75
+	defer func() {
76
+		proxy.connTrackLock.Lock()
77
+		delete(proxy.connTrackTable, *clientKey)
78
+		proxy.connTrackLock.Unlock()
79
+		proxyConn.Close()
80
+	}()
81
+
82
+	readBuf := make([]byte, UDPBufSize)
83
+	for {
84
+		proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
85
+	again:
86
+		read, err := proxyConn.Read(readBuf)
87
+		if err != nil {
88
+			if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
89
+				// This will happen if the last write failed
90
+				// (e.g: nothing is actually listening on the
91
+				// proxied port on the container), ignore it
92
+				// and continue until UDPConnTrackTimeout
93
+				// expires:
94
+				goto again
95
+			}
96
+			return
97
+		}
98
+		for i := 0; i != read; {
99
+			written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
100
+			if err != nil {
101
+				return
102
+			}
103
+			i += written
104
+		}
105
+	}
106
+}
107
+
108
+// Run starts forwarding the traffic using UDP.
109
+func (proxy *UDPProxy) Run() {
110
+	readBuf := make([]byte, UDPBufSize)
111
+	for {
112
+		read, from, err := proxy.listener.ReadFromUDP(readBuf)
113
+		if err != nil {
114
+			// NOTE: Apparently ReadFrom doesn't return
115
+			// ECONNREFUSED like Read do (see comment in
116
+			// UDPProxy.replyLoop)
117
+			if !isClosedError(err) {
118
+				log.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
119
+			}
120
+			break
121
+		}
122
+
123
+		fromKey := newConnTrackKey(from)
124
+		proxy.connTrackLock.Lock()
125
+		proxyConn, hit := proxy.connTrackTable[*fromKey]
126
+		if !hit {
127
+			proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
128
+			if err != nil {
129
+				log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
130
+				proxy.connTrackLock.Unlock()
131
+				continue
132
+			}
133
+			proxy.connTrackTable[*fromKey] = proxyConn
134
+			go proxy.replyLoop(proxyConn, from, fromKey)
135
+		}
136
+		proxy.connTrackLock.Unlock()
137
+		for i := 0; i != read; {
138
+			written, err := proxyConn.Write(readBuf[i:read])
139
+			if err != nil {
140
+				log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
141
+				break
142
+			}
143
+			i += written
144
+		}
145
+	}
146
+}
147
+
148
+// Close stops forwarding the traffic.
149
+func (proxy *UDPProxy) Close() {
150
+	proxy.listener.Close()
151
+	proxy.connTrackLock.Lock()
152
+	defer proxy.connTrackLock.Unlock()
153
+	for _, conn := range proxy.connTrackTable {
154
+		conn.Close()
155
+	}
156
+}
157
+
158
+// FrontendAddr returns the UDP address on which the proxy is listening.
159
+func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
160
+
161
+// BackendAddr returns the proxied UDP address.
162
+func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
163
+
164
+func isClosedError(err error) bool {
165
+	/* This comparison is ugly, but unfortunately, net.go doesn't export errClosing.
166
+	 * See:
167
+	 * http://golang.org/src/pkg/net/net.go
168
+	 * https://code.google.com/p/go/issues/detail?id=4337
169
+	 * https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ
170
+	 */
171
+	return strings.HasSuffix(err.Error(), "use of closed network connection")
172
+}
... ...
@@ -2,7 +2,8 @@
2 2
 
3 3
 set -e
4 4
 
5
-(
5
+# docker-proxy is Linux only
6
+[ "$(go env GOOS)" != 'linux' ] || (
6 7
 	export CGO_ENABLED=0
7 8
 
8 9
 	DOCKER_STATIC=1
... ...
@@ -2,7 +2,8 @@
2 2
 
3 3
 set -e
4 4
 
5
-(
5
+# docker-proxy is Linux only
6
+[ "$(go env GOOS)" != 'linux' ] || (
6 7
 	export LDFLAGS_STATIC=''
7 8
 	export BUILDFLAGS=("${BUILDFLAGS[@]/netgo /}")        # disable netgo, since we don't need it for a dynamic binary
8 9
 	export BUILDFLAGS=("${BUILDFLAGS[@]/osusergo /}")     # ditto for osusergo