github.com/moby/docker@v26.1.3+incompatible/cmd/docker-proxy/network_proxy_test.go (about) 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 }