github.com/pion/dtls/v2@v2.2.12/e2e/e2e_lossy_test.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package e2e
     5  
     6  import (
     7  	"crypto/tls"
     8  	"fmt"
     9  	"math/rand"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/pion/dtls/v2"
    14  	"github.com/pion/dtls/v2/pkg/crypto/selfsign"
    15  	transportTest "github.com/pion/transport/v2/test"
    16  )
    17  
    18  const (
    19  	flightInterval   = time.Millisecond * 100
    20  	lossyTestTimeout = 30 * time.Second
    21  )
    22  
    23  /*
    24  DTLS Client/Server over a lossy transport, just asserts it can handle at increasing increments
    25  */
    26  func TestPionE2ELossy(t *testing.T) {
    27  	// Check for leaking routines
    28  	report := transportTest.CheckRoutines(t)
    29  	defer report()
    30  
    31  	type runResult struct {
    32  		dtlsConn *dtls.Conn
    33  		err      error
    34  	}
    35  
    36  	serverCert, err := selfsign.GenerateSelfSigned()
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  
    41  	clientCert, err := selfsign.GenerateSelfSigned()
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	for _, test := range []struct {
    47  		LossChanceRange int
    48  		DoClientAuth    bool
    49  		CipherSuites    []dtls.CipherSuiteID
    50  		MTU             int
    51  	}{
    52  		{
    53  			LossChanceRange: 0,
    54  		},
    55  		{
    56  			LossChanceRange: 10,
    57  		},
    58  		{
    59  			LossChanceRange: 20,
    60  		},
    61  		{
    62  			LossChanceRange: 50,
    63  		},
    64  		{
    65  			LossChanceRange: 0,
    66  			DoClientAuth:    true,
    67  		},
    68  		{
    69  			LossChanceRange: 10,
    70  			DoClientAuth:    true,
    71  		},
    72  		{
    73  			LossChanceRange: 20,
    74  			DoClientAuth:    true,
    75  		},
    76  		{
    77  			LossChanceRange: 50,
    78  			DoClientAuth:    true,
    79  		},
    80  		{
    81  			LossChanceRange: 0,
    82  			CipherSuites:    []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
    83  		},
    84  		{
    85  			LossChanceRange: 10,
    86  			CipherSuites:    []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
    87  		},
    88  		{
    89  			LossChanceRange: 20,
    90  			CipherSuites:    []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
    91  		},
    92  		{
    93  			LossChanceRange: 50,
    94  			CipherSuites:    []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
    95  		},
    96  		{
    97  			LossChanceRange: 10,
    98  			MTU:             100,
    99  			DoClientAuth:    true,
   100  		},
   101  		{
   102  			LossChanceRange: 20,
   103  			MTU:             100,
   104  			DoClientAuth:    true,
   105  		},
   106  		{
   107  			LossChanceRange: 50,
   108  			MTU:             100,
   109  			DoClientAuth:    true,
   110  		},
   111  	} {
   112  		name := fmt.Sprintf("Loss%d_MTU%d", test.LossChanceRange, test.MTU)
   113  		if test.DoClientAuth {
   114  			name += "_WithCliAuth"
   115  		}
   116  		for _, ciph := range test.CipherSuites {
   117  			name += "_With" + ciph.String()
   118  		}
   119  		test := test
   120  		t.Run(name, func(t *testing.T) {
   121  			// Limit runtime in case of deadlocks
   122  			lim := transportTest.TimeOut(lossyTestTimeout + time.Second)
   123  			defer lim.Stop()
   124  
   125  			rand.Seed(time.Now().UTC().UnixNano())
   126  			chosenLoss := rand.Intn(9) + test.LossChanceRange //nolint:gosec
   127  			serverDone := make(chan runResult)
   128  			clientDone := make(chan runResult)
   129  			br := transportTest.NewBridge()
   130  
   131  			if err = br.SetLossChance(chosenLoss); err != nil {
   132  				t.Fatal(err)
   133  			}
   134  
   135  			go func() {
   136  				cfg := &dtls.Config{
   137  					FlightInterval:     flightInterval,
   138  					CipherSuites:       test.CipherSuites,
   139  					InsecureSkipVerify: true,
   140  					MTU:                test.MTU,
   141  				}
   142  
   143  				if test.DoClientAuth {
   144  					cfg.Certificates = []tls.Certificate{clientCert}
   145  				}
   146  
   147  				client, startupErr := dtls.Client(br.GetConn0(), cfg)
   148  				clientDone <- runResult{client, startupErr}
   149  			}()
   150  
   151  			go func() {
   152  				cfg := &dtls.Config{
   153  					Certificates:   []tls.Certificate{serverCert},
   154  					FlightInterval: flightInterval,
   155  					MTU:            test.MTU,
   156  				}
   157  
   158  				if test.DoClientAuth {
   159  					cfg.ClientAuth = dtls.RequireAnyClientCert
   160  				}
   161  
   162  				server, startupErr := dtls.Server(br.GetConn1(), cfg)
   163  				serverDone <- runResult{server, startupErr}
   164  			}()
   165  
   166  			testTimer := time.NewTimer(lossyTestTimeout)
   167  			var serverConn, clientConn *dtls.Conn
   168  			defer func() {
   169  				if serverConn != nil {
   170  					if err = serverConn.Close(); err != nil {
   171  						t.Error(err)
   172  					}
   173  				}
   174  				if clientConn != nil {
   175  					if err = clientConn.Close(); err != nil {
   176  						t.Error(err)
   177  					}
   178  				}
   179  			}()
   180  
   181  			for {
   182  				if serverConn != nil && clientConn != nil {
   183  					break
   184  				}
   185  
   186  				br.Tick()
   187  				select {
   188  				case serverResult := <-serverDone:
   189  					if serverResult.err != nil {
   190  						t.Errorf("Fail, serverError: clientComplete(%t) serverComplete(%t) LossChance(%d) error(%v)", clientConn != nil, serverConn != nil, chosenLoss, serverResult.err)
   191  						return
   192  					}
   193  
   194  					serverConn = serverResult.dtlsConn
   195  				case clientResult := <-clientDone:
   196  					if clientResult.err != nil {
   197  						t.Errorf("Fail, clientError: clientComplete(%t) serverComplete(%t) LossChance(%d) error(%v)", clientConn != nil, serverConn != nil, chosenLoss, clientResult.err)
   198  						return
   199  					}
   200  
   201  					clientConn = clientResult.dtlsConn
   202  				case <-testTimer.C:
   203  					t.Errorf("Test expired: clientComplete(%t) serverComplete(%t) LossChance(%d)", clientConn != nil, serverConn != nil, chosenLoss)
   204  					return
   205  				case <-time.After(10 * time.Millisecond):
   206  				}
   207  			}
   208  		})
   209  	}
   210  }