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 }