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  }