162 lines
3.8 KiB
Go
162 lines
3.8 KiB
Go
package connection
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"net"
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/lucas-clemente/quic-go"
|
|
"github.com/rs/zerolog"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
quicpogs "github.com/cloudflare/cloudflared/quic"
|
|
)
|
|
|
|
// TestQUICServer tests if a quic server accepts and responds to a quic client with the acceptance protocol.
|
|
// It also serves as a demonstration for communication with the QUIC connection started by a cloudflared.
|
|
func TestQUICServer(t *testing.T) {
|
|
quicConfig := &quic.Config{
|
|
KeepAlive: true,
|
|
}
|
|
|
|
log := zerolog.New(os.Stdout)
|
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
|
|
|
require.NoError(t, err)
|
|
udpListener, err := net.ListenUDP(udpAddr.Network(), udpAddr)
|
|
require.NoError(t, err)
|
|
defer udpListener.Close()
|
|
tlsConfig := generateTLSConfig()
|
|
tlsConfClient := &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
NextProtos: []string{"argotunnel"},
|
|
}
|
|
var tests = []struct {
|
|
desc string
|
|
dest string
|
|
connectionType quicpogs.ConnectionType
|
|
metadata []quicpogs.Metadata
|
|
message []byte
|
|
expectedMessage []byte
|
|
}{
|
|
{
|
|
desc: "",
|
|
dest: "somehost.com",
|
|
connectionType: quicpogs.ConnectionTypeWebsocket,
|
|
metadata: []quicpogs.Metadata{
|
|
quicpogs.Metadata{
|
|
Key: "key",
|
|
Val: "value",
|
|
},
|
|
},
|
|
expectedMessage: []byte("OK"),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.desc, func(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
var wg sync.WaitGroup
|
|
go func() {
|
|
wg.Add(1)
|
|
quicServer(
|
|
t, udpListener, tlsConfig, quicConfig,
|
|
test.dest, test.connectionType, test.metadata, test.message, test.expectedMessage,
|
|
)
|
|
wg.Done()
|
|
}()
|
|
|
|
qC, err := NewQUICConnection(context.Background(), quicConfig, udpListener.LocalAddr(), tlsConfClient, log)
|
|
require.NoError(t, err)
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
cancel()
|
|
}()
|
|
|
|
qC.Serve(ctx)
|
|
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func quicServer(
|
|
t *testing.T,
|
|
conn net.PacketConn,
|
|
tlsConf *tls.Config,
|
|
config *quic.Config,
|
|
dest string,
|
|
connectionType quicpogs.ConnectionType,
|
|
metadata []quicpogs.Metadata,
|
|
message []byte,
|
|
expectedResponse []byte,
|
|
) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
earlyListener, err := quic.ListenEarly(conn, tlsConf, config)
|
|
require.NoError(t, err)
|
|
|
|
session, err := earlyListener.Accept(ctx)
|
|
require.NoError(t, err)
|
|
|
|
stream, err := session.OpenStreamSync(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
// Start off ALPN
|
|
err = quicpogs.WriteConnectRequestData(stream, dest, connectionType, metadata...)
|
|
require.NoError(t, err)
|
|
|
|
_, err = quicpogs.ReadConnectResponseData(stream)
|
|
require.NoError(t, err)
|
|
|
|
if message != nil {
|
|
// ALPN successful. Write data.
|
|
_, err = stream.Write([]byte(message))
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
response, err := ioutil.ReadAll(stream)
|
|
require.NoError(t, err)
|
|
|
|
// For now it is an echo server. Verify if the same data is returned.
|
|
assert.Equal(t, expectedResponse, response)
|
|
}
|
|
|
|
// Setup a bare-bones TLS config for the server
|
|
func generateTLSConfig() *tls.Config {
|
|
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
|
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
|
|
|
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
NextProtos: []string{"argotunnel"},
|
|
}
|
|
}
|