github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/vm/proxyapp/proxyappclient_tcp_test.go (about)

     1  // Copyright 2022 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package proxyapp
     5  
     6  import (
     7  	"crypto/rand"
     8  	"crypto/rsa"
     9  	"crypto/tls"
    10  	"crypto/x509"
    11  	"encoding/pem"
    12  	"math/big"
    13  	"net"
    14  	"net/rpc"
    15  	"net/rpc/jsonrpc"
    16  	"net/url"
    17  	"os"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/google/syzkaller/vm/proxyapp/proxyrpc"
    23  	"github.com/google/syzkaller/vm/vmimpl"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/mock"
    26  )
    27  
    28  func testTCPEnv(port string) *vmimpl.Env {
    29  	return &vmimpl.Env{
    30  		Config: []byte(`
    31  {
    32  		"rpc_server_uri": "localhost:` + port + `",
    33  		"security": "none",
    34  		"config": {
    35  			"internal_values": 123
    36  		}
    37  	}
    38  `)}
    39  }
    40  
    41  func testTCPEnvTLS(port, certPath string) *vmimpl.Env {
    42  	return &vmimpl.Env{
    43  		Config: []byte(`
    44  {
    45  		"rpc_server_uri": "localhost:` + port + `",
    46  		"security": "tls",
    47  		"server_tls_cert": "` + certPath + `",
    48  		"config": {
    49  			"internal_values": 123
    50  		}
    51  	}
    52  `)}
    53  }
    54  
    55  func proxyAppServerTCPFixture(t *testing.T) (*mockProxyAppInterface, string, *proxyAppParams) {
    56  	mProxyAppServer, port, _ := makeMockProxyAppServer(t)
    57  	return initProxyAppServerFixture(mProxyAppServer), port, makeTestParams()
    58  }
    59  
    60  func proxyAppServerTCPFixtureTLS(t *testing.T, cert tls.Certificate) (*mockProxyAppInterface, string, *proxyAppParams) {
    61  	mProxyAppServer, port, _ := makeMockProxyAppServerTLS(t, cert)
    62  	return initProxyAppServerFixture(mProxyAppServer), port, makeTestParams()
    63  }
    64  
    65  func TestCtor_TCP_Ok(t *testing.T) {
    66  	_, port, params := proxyAppServerTCPFixture(t)
    67  	p, err := ctor(params, testTCPEnv(port))
    68  
    69  	assert.Nil(t, err)
    70  	assert.Equal(t, 2, p.Count())
    71  }
    72  
    73  func TestCtor_TCP_Ok_TLS(t *testing.T) {
    74  	key, err := rsa.GenerateKey(rand.Reader, 2048)
    75  	if err != nil {
    76  		t.Fatalf("generate private key: %v", err)
    77  	}
    78  	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
    79  
    80  	template := &x509.Certificate{
    81  		SerialNumber:   big.NewInt(1),
    82  		NotBefore:      time.Now().Add(-10 * time.Second),
    83  		NotAfter:       time.Now().AddDate(10, 0, 0),
    84  		KeyUsage:       x509.KeyUsageCRLSign,
    85  		ExtKeyUsage:    []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    86  		IsCA:           false,
    87  		MaxPathLenZero: true,
    88  		DNSNames:       []string{"localhost"},
    89  	}
    90  	certBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
    91  	if err != nil {
    92  		t.Fatalf("generate certificate: %v", err)
    93  	}
    94  	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
    95  
    96  	cert, err := tls.X509KeyPair(certPEM, keyPEM)
    97  	if err != nil {
    98  		t.Fatalf("load keypair to cert: %v", err)
    99  	}
   100  
   101  	_, port, params := proxyAppServerTCPFixtureTLS(t, cert)
   102  
   103  	// Write the certificate to a temp file where the client can use it.
   104  	certFile, err := os.CreateTemp("", "test-cert")
   105  	if err != nil {
   106  		t.Fatalf("temp file for certificate: %v", err)
   107  	}
   108  	defer certFile.Close()
   109  	defer os.Remove(certFile.Name())
   110  
   111  	if _, err := certFile.Write(certPEM); err != nil {
   112  		t.Fatalf("write cert: %v", err)
   113  	}
   114  	if err := certFile.Close(); err != nil {
   115  		t.Fatalf("close cert: %v", err)
   116  	}
   117  
   118  	p, err := ctor(params, testTCPEnvTLS(port, certFile.Name()))
   119  
   120  	assert.Nil(t, err)
   121  	assert.Equal(t, 2, p.Count())
   122  }
   123  
   124  func TestCtor_TCP_WrongPort(t *testing.T) {
   125  	p, err := ctor(makeTestParams(), testTCPEnv("5"))
   126  
   127  	assert.NotNil(t, err)
   128  	assert.Nil(t, p)
   129  }
   130  
   131  func TestCtor_TCP_Reconnect_On_LostConnection(t *testing.T) {
   132  	mProxyAppServer, port, closeServerConnections := makeMockProxyAppServer(t)
   133  	onConnect := make(chan bool, 1)
   134  	mProxyAppServer.
   135  		On("CreatePool", mock.Anything, mock.Anything).
   136  		Run(func(args mock.Arguments) {
   137  			out := args.Get(1).(*proxyrpc.CreatePoolResult)
   138  			out.Count = 2
   139  			onConnect <- true
   140  		}).
   141  		Return(nil).
   142  		Times(2).
   143  		On("PoolLogs", mock.Anything, mock.Anything).
   144  		Run(func(args mock.Arguments) {
   145  			select {
   146  			case mProxyAppServer.OnLogsReceived <- true:
   147  			default:
   148  			}
   149  		}).
   150  		Return(nil)
   151  
   152  	ctor(makeTestParams(), testTCPEnv(port))
   153  	<-onConnect
   154  	<-mProxyAppServer.OnLogsReceived
   155  
   156  	closeServerConnections()
   157  
   158  	<-onConnect
   159  	<-mProxyAppServer.OnLogsReceived
   160  }
   161  
   162  func TestCtor_TCP_Reconnect_PoolChanged(t *testing.T) {
   163  	mProxyAppServer, port, closeServerConnections := makeMockProxyAppServer(t)
   164  	onConnect := make(chan bool, 1)
   165  	mProxyAppServer.
   166  		On("CreatePool", mock.Anything, mock.Anything).
   167  		Run(func(args mock.Arguments) {
   168  			out := args.Get(1).(*proxyrpc.CreatePoolResult)
   169  			out.Count = 2
   170  			onConnect <- true
   171  		}).
   172  		Return(nil).
   173  		Once().
   174  		On("CreatePool", mock.Anything, mock.Anything).
   175  		Run(func(args mock.Arguments) {
   176  			out := args.Get(1).(*proxyrpc.CreatePoolResult)
   177  			out.Count = 1
   178  			onConnect <- true
   179  		}).
   180  		Return(nil).
   181  		On("PoolLogs", mock.Anything, mock.Anything).
   182  		Return(nil)
   183  
   184  	p, _ := ctor(makeTestParams(), testTCPEnv(port))
   185  	<-onConnect
   186  	closeServerConnections()
   187  	for i := 0; i < 10; i++ {
   188  		<-onConnect
   189  		p.(*pool).mu.Lock()
   190  		assert.Nil(t, p.(*pool).proxy) // still can't initialize
   191  		p.(*pool).mu.Unlock()
   192  	}
   193  }
   194  
   195  func makeMockProxyAppServerWithListener(t *testing.T, l net.Listener) (*mockProxyAppInterface, string, func()) {
   196  	handler := makeMockProxyAppInterface(t)
   197  	server := rpc.NewServer()
   198  	server.RegisterName("ProxyVM", struct{ proxyrpc.ProxyAppInterface }{handler})
   199  
   200  	dest, err := url.Parse("http://" + l.Addr().String())
   201  	if err != nil {
   202  		t.Fatalf("failed to get server endpoint addr: %v", err)
   203  	}
   204  
   205  	connsMu := sync.Mutex{}
   206  	var conns []net.Conn
   207  
   208  	go func() {
   209  		for {
   210  			conn, err := l.Accept()
   211  			if err != nil {
   212  				panic("failed to accept connection")
   213  			}
   214  			go server.ServeCodec(jsonrpc.NewServerCodec(conn))
   215  
   216  			connsMu.Lock()
   217  			conns = append(conns, conn)
   218  			connsMu.Unlock()
   219  		}
   220  	}()
   221  
   222  	return handler, dest.Port(), func() {
   223  		connsMu.Lock()
   224  		defer connsMu.Unlock()
   225  		for _, conn := range conns {
   226  			conn.Close()
   227  		}
   228  	}
   229  }
   230  
   231  func makeMockProxyAppServer(t *testing.T) (*mockProxyAppInterface, string, func()) {
   232  	l, e := net.Listen("tcp", ":0")
   233  	if e != nil {
   234  		t.Fatalf("listen error: %v", e)
   235  	}
   236  
   237  	return makeMockProxyAppServerWithListener(t, l)
   238  }
   239  
   240  func makeMockProxyAppServerTLS(t *testing.T, cert tls.Certificate) (*mockProxyAppInterface, string, func()) {
   241  	l, e := tls.Listen("tcp", ":0", &tls.Config{Certificates: []tls.Certificate{cert}})
   242  	if e != nil {
   243  		t.Fatalf("listen error: %v", e)
   244  	}
   245  
   246  	return makeMockProxyAppServerWithListener(t, l)
   247  }